summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuben Undheim <ruben.undheim@gmail.com>2016-11-06 11:28:06 +0100
committerRuben Undheim <ruben.undheim@gmail.com>2016-11-06 11:28:06 +0100
commit11904476fc43de21892c0aaef94480d2a27d05af (patch)
treeadb13b830212c269d58031f900d652f29013d2d7
Import yosys_0.7.orig.tar.gz
[dgit import orig yosys_0.7.orig.tar.gz]
-rw-r--r--.gitignore29
-rw-r--r--.travis.yml34
-rw-r--r--CHANGELOG489
-rw-r--r--CodeOfConduct73
-rw-r--r--CodingReadme413
-rw-r--r--Makefile564
-rw-r--r--README451
-rw-r--r--backends/blif/Makefile.inc3
-rw-r--r--backends/blif/blif.cc632
-rw-r--r--backends/btor/Makefile.inc3
-rw-r--r--backends/btor/README23
-rw-r--r--backends/btor/btor.cc1111
-rwxr-xr-xbackends/btor/verilog2btor.sh37
-rw-r--r--backends/edif/Makefile.inc3
-rw-r--r--backends/edif/edif.cc367
-rw-r--r--backends/ilang/Makefile.inc3
-rw-r--r--backends/ilang/ilang_backend.cc504
-rw-r--r--backends/ilang/ilang_backend.h51
-rw-r--r--backends/intersynth/Makefile.inc3
-rw-r--r--backends/intersynth/intersynth.cc220
-rw-r--r--backends/json/Makefile.inc3
-rw-r--r--backends/json/json.cc542
-rw-r--r--backends/smt2/.gitignore1
-rw-r--r--backends/smt2/Makefile.inc16
-rw-r--r--backends/smt2/example.v11
-rw-r--r--backends/smt2/example.ys3
-rw-r--r--backends/smt2/smt2.cc1096
-rw-r--r--backends/smt2/smtbmc.py734
-rw-r--r--backends/smt2/smtio.py731
-rw-r--r--backends/smt2/test_cells.sh55
-rw-r--r--backends/smv/Makefile.inc3
-rw-r--r--backends/smv/smv.cc784
-rw-r--r--backends/smv/test_cells.sh33
-rw-r--r--backends/spice/Makefile.inc3
-rw-r--r--backends/spice/spice.cc266
-rw-r--r--backends/verilog/Makefile.inc3
-rw-r--r--backends/verilog/verilog_backend.cc1507
-rw-r--r--examples/basys3/README19
-rw-r--r--examples/basys3/example.v21
-rw-r--r--examples/basys3/example.xdc21
-rw-r--r--examples/basys3/run.sh4
-rw-r--r--examples/basys3/run_prog.tcl4
-rw-r--r--examples/basys3/run_vivado.tcl9
-rw-r--r--examples/basys3/run_yosys.ys2
-rw-r--r--examples/cmos/.gitignore4
-rw-r--r--examples/cmos/README13
-rw-r--r--examples/cmos/cmos_cells.lib55
-rw-r--r--examples/cmos/cmos_cells.sp39
-rw-r--r--examples/cmos/cmos_cells.v44
-rw-r--r--examples/cmos/cmos_cells_digital.sp31
-rw-r--r--examples/cmos/counter.v12
-rw-r--r--examples/cmos/counter.ys16
-rw-r--r--examples/cmos/counter_digital.ys16
-rw-r--r--examples/cmos/counter_tb.gtkw5
-rw-r--r--examples/cmos/counter_tb.v33
-rw-r--r--examples/cmos/testbench.sh7
-rw-r--r--examples/cmos/testbench.sp29
-rw-r--r--examples/cmos/testbench_digital.sh15
-rw-r--r--examples/cmos/testbench_digital.sp26
-rw-r--r--examples/cxx-api/demomain.cc22
-rw-r--r--examples/cxx-api/evaldemo.cc55
-rw-r--r--examples/smtbmc/.gitignore22
-rw-r--r--examples/smtbmc/Makefile59
-rw-r--r--examples/smtbmc/demo1.v19
-rw-r--r--examples/smtbmc/demo2.v29
-rw-r--r--examples/smtbmc/demo3.smtc5
-rw-r--r--examples/smtbmc/demo3.v18
-rw-r--r--examples/smtbmc/demo4.smtc11
-rw-r--r--examples/smtbmc/demo4.v13
-rw-r--r--examples/smtbmc/demo5.v18
-rw-r--r--examples/smtbmc/demo6.v14
-rw-r--r--examples/smtbmc/demo7.v19
-rw-r--r--frontends/ast/Makefile.inc6
-rw-r--r--frontends/ast/ast.cc1180
-rw-r--r--frontends/ast/ast.h319
-rw-r--r--frontends/ast/dpicall.cc148
-rw-r--r--frontends/ast/genrtlil.cc1538
-rw-r--r--frontends/ast/simplify.cc3341
-rw-r--r--frontends/blif/Makefile.inc3
-rw-r--r--frontends/blif/blifparse.cc495
-rw-r--r--frontends/blif/blifparse.h31
-rw-r--r--frontends/ilang/.gitignore4
-rw-r--r--frontends/ilang/Makefile.inc20
-rw-r--r--frontends/ilang/ilang_frontend.cc64
-rw-r--r--frontends/ilang/ilang_frontend.h48
-rw-r--r--frontends/ilang/ilang_lexer.l138
-rw-r--r--frontends/ilang/ilang_parser.y430
-rw-r--r--frontends/liberty/Makefile.inc3
-rw-r--r--frontends/liberty/liberty.cc660
-rw-r--r--frontends/verific/Makefile.inc17
-rw-r--r--frontends/verific/build_amd64.txt30
-rw-r--r--frontends/verific/test_navre.ys18
-rw-r--r--frontends/verific/verific.cc1005
-rw-r--r--frontends/verilog/.gitignore4
-rw-r--r--frontends/verilog/Makefile.inc23
-rw-r--r--frontends/verilog/const2ast.cc241
-rw-r--r--frontends/verilog/preproc.cc452
-rw-r--r--frontends/verilog/verilog_frontend.cc456
-rw-r--r--frontends/verilog/verilog_frontend.h85
-rw-r--r--frontends/verilog/verilog_lexer.l387
-rw-r--r--frontends/verilog/verilog_parser.y1583
-rw-r--r--frontends/vhdl2verilog/Makefile.inc1
-rw-r--r--frontends/vhdl2verilog/vhdl2verilog.cc183
-rw-r--r--kernel/bitpattern.h161
-rw-r--r--kernel/calc.cc586
-rw-r--r--kernel/cellaigs.cc481
-rw-r--r--kernel/cellaigs.h52
-rw-r--r--kernel/celledges.cc209
-rw-r--r--kernel/celledges.h63
-rw-r--r--kernel/celltypes.h449
-rw-r--r--kernel/consteval.h383
-rw-r--r--kernel/cost.h84
-rw-r--r--kernel/driver.cc525
-rw-r--r--kernel/hashlib.h1052
-rw-r--r--kernel/log.cc516
-rw-r--r--kernel/log.h308
-rw-r--r--kernel/macc.h242
-rw-r--r--kernel/modtools.h582
-rw-r--r--kernel/register.cc813
-rw-r--r--kernel/register.h129
-rw-r--r--kernel/rtlil.cc3679
-rw-r--r--kernel/rtlil.h1348
-rw-r--r--kernel/satgen.h1411
-rw-r--r--kernel/sigtools.h332
-rw-r--r--kernel/utils.h214
-rw-r--r--kernel/yosys.cc1157
-rw-r--r--kernel/yosys.h311
-rw-r--r--libs/bigint/.gitignore6
-rw-r--r--libs/bigint/BigInteger.cc405
-rw-r--r--libs/bigint/BigInteger.hh215
-rw-r--r--libs/bigint/BigIntegerAlgorithms.cc70
-rw-r--r--libs/bigint/BigIntegerAlgorithms.hh25
-rw-r--r--libs/bigint/BigIntegerLibrary.hh8
-rw-r--r--libs/bigint/BigIntegerUtils.cc50
-rw-r--r--libs/bigint/BigIntegerUtils.hh72
-rw-r--r--libs/bigint/BigUnsigned.cc697
-rw-r--r--libs/bigint/BigUnsigned.hh418
-rw-r--r--libs/bigint/BigUnsignedInABase.cc125
-rw-r--r--libs/bigint/BigUnsignedInABase.hh122
-rw-r--r--libs/bigint/ChangeLog146
-rw-r--r--libs/bigint/Makefile73
-rw-r--r--libs/bigint/NumberlikeArray.hh177
-rw-r--r--libs/bigint/README81
-rwxr-xr-xlibs/bigint/run-testsuite37
-rw-r--r--libs/bigint/sample.cc125
-rw-r--r--libs/bigint/testsuite.cc326
-rw-r--r--libs/ezsat/.gitignore5
-rw-r--r--libs/ezsat/Makefile30
-rw-r--r--libs/ezsat/README29
-rw-r--r--libs/ezsat/demo_bit.cc71
-rw-r--r--libs/ezsat/demo_cmp.cc146
-rw-r--r--libs/ezsat/demo_vec.cc112
-rw-r--r--libs/ezsat/ezminisat.cc243
-rw-r--r--libs/ezsat/ezminisat.h71
-rw-r--r--libs/ezsat/ezsat.cc1449
-rw-r--r--libs/ezsat/ezsat.h359
-rw-r--r--libs/ezsat/puzzle3d.cc295
-rw-r--r--libs/ezsat/puzzle3d.scad82
-rw-r--r--libs/ezsat/testbench.cc441
-rw-r--r--libs/minisat/00_PATCH_mkLit_default_arg.patch20
-rw-r--r--libs/minisat/00_PATCH_remove_zlib.patch38
-rw-r--r--libs/minisat/00_UPDATE.sh17
-rw-r--r--libs/minisat/Alg.h84
-rw-r--r--libs/minisat/Alloc.h131
-rw-r--r--libs/minisat/Dimacs.h87
-rw-r--r--libs/minisat/Heap.h168
-rw-r--r--libs/minisat/IntMap.h106
-rw-r--r--libs/minisat/IntTypes.h42
-rw-r--r--libs/minisat/LICENSE21
-rw-r--r--libs/minisat/Map.h193
-rw-r--r--libs/minisat/Options.cc94
-rw-r--r--libs/minisat/Options.h386
-rw-r--r--libs/minisat/ParseUtils.h119
-rw-r--r--libs/minisat/Queue.h69
-rw-r--r--libs/minisat/Rnd.h67
-rw-r--r--libs/minisat/SimpSolver.cc727
-rw-r--r--libs/minisat/SimpSolver.h222
-rw-r--r--libs/minisat/Solver.cc1068
-rw-r--r--libs/minisat/Solver.h409
-rw-r--r--libs/minisat/SolverTypes.h478
-rw-r--r--libs/minisat/Sort.h98
-rw-r--r--libs/minisat/System.cc171
-rw-r--r--libs/minisat/System.h72
-rw-r--r--libs/minisat/Vec.h134
-rw-r--r--libs/minisat/XAlloc.h45
-rw-r--r--libs/sha1/sha1.cpp275
-rw-r--r--libs/sha1/sha1.h57
-rw-r--r--libs/subcircuit/.gitignore2
-rw-r--r--libs/subcircuit/Makefile53
-rw-r--r--libs/subcircuit/README466
-rw-r--r--libs/subcircuit/demo.cc134
-rw-r--r--libs/subcircuit/scshell.cc261
-rw-r--r--libs/subcircuit/subcircuit.cc1694
-rw-r--r--libs/subcircuit/subcircuit.h155
-rw-r--r--libs/subcircuit/test_large.spl221
-rw-r--r--libs/subcircuit/test_macc22.txt48
-rw-r--r--libs/subcircuit/test_mine.txt41
-rw-r--r--libs/subcircuit/test_perm.pl108
-rw-r--r--libs/subcircuit/test_shorts.spl121
-rw-r--r--manual/.gitignore12
-rw-r--r--manual/APPNOTE_010_Verilog_to_BLIF.tex466
-rw-r--r--manual/APPNOTE_011_Design_Investigation.tex1070
-rw-r--r--manual/APPNOTE_011_Design_Investigation/cmos.v3
-rw-r--r--manual/APPNOTE_011_Design_Investigation/cmos_00.dot34
-rw-r--r--manual/APPNOTE_011_Design_Investigation/cmos_01.dot23
-rw-r--r--manual/APPNOTE_011_Design_Investigation/example.v6
-rw-r--r--manual/APPNOTE_011_Design_Investigation/example.ys11
-rw-r--r--manual/APPNOTE_011_Design_Investigation/example_00.dot23
-rw-r--r--manual/APPNOTE_011_Design_Investigation/example_01.dot33
-rw-r--r--manual/APPNOTE_011_Design_Investigation/example_02.dot20
-rw-r--r--manual/APPNOTE_011_Design_Investigation/example_03.dot11
-rw-r--r--manual/APPNOTE_011_Design_Investigation/foobaraddsub.v8
-rw-r--r--manual/APPNOTE_011_Design_Investigation/make.sh23
-rw-r--r--manual/APPNOTE_011_Design_Investigation/memdemo.v19
-rw-r--r--manual/APPNOTE_011_Design_Investigation/memdemo_00.dot138
-rw-r--r--manual/APPNOTE_011_Design_Investigation/memdemo_01.dot29
-rw-r--r--manual/APPNOTE_011_Design_Investigation/primetest.v4
-rw-r--r--manual/APPNOTE_011_Design_Investigation/splice.dot39
-rw-r--r--manual/APPNOTE_011_Design_Investigation/splice.v10
-rw-r--r--manual/APPNOTE_011_Design_Investigation/submod.ys16
-rw-r--r--manual/APPNOTE_011_Design_Investigation/submod_00.dot45
-rw-r--r--manual/APPNOTE_011_Design_Investigation/submod_01.dot87
-rw-r--r--manual/APPNOTE_011_Design_Investigation/submod_02.dot33
-rw-r--r--manual/APPNOTE_011_Design_Investigation/submod_03.dot26
-rw-r--r--manual/APPNOTE_011_Design_Investigation/sumprod.v12
-rw-r--r--manual/APPNOTE_011_Design_Investigation/sumprod_00.dot18
-rw-r--r--manual/APPNOTE_011_Design_Investigation/sumprod_01.dot15
-rw-r--r--manual/APPNOTE_011_Design_Investigation/sumprod_02.dot5
-rw-r--r--manual/APPNOTE_011_Design_Investigation/sumprod_03.dot11
-rw-r--r--manual/APPNOTE_011_Design_Investigation/sumprod_04.dot11
-rw-r--r--manual/APPNOTE_011_Design_Investigation/sumprod_05.dot15
-rw-r--r--manual/APPNOTE_012_Verilog_to_BTOR.tex435
-rw-r--r--manual/CHAPTER_Appnotes.tex29
-rw-r--r--manual/CHAPTER_Approach.tex145
-rw-r--r--manual/CHAPTER_Auxlibs.tex35
-rw-r--r--manual/CHAPTER_Auxprogs.tex25
-rw-r--r--manual/CHAPTER_Basics.tex839
-rw-r--r--manual/CHAPTER_CellLib.tex454
-rw-r--r--manual/CHAPTER_Eval.tex209
-rw-r--r--manual/CHAPTER_Eval/grep-it.sh84
-rw-r--r--manual/CHAPTER_Eval/openmsp430.prj14
-rw-r--r--manual/CHAPTER_Eval/openmsp430_ys.prj1
-rw-r--r--manual/CHAPTER_Eval/or1200.prj37
-rw-r--r--manual/CHAPTER_Eval/or1200_ys.prj1
-rw-r--r--manual/CHAPTER_Eval/run-it.sh74
-rw-r--r--manual/CHAPTER_Eval/settings.xst2
-rw-r--r--manual/CHAPTER_Intro.tex98
-rw-r--r--manual/CHAPTER_Optimize.tex324
-rw-r--r--manual/CHAPTER_Overview.tex526
-rw-r--r--manual/CHAPTER_Prog.tex26
-rw-r--r--manual/CHAPTER_Prog/.gitignore3
-rw-r--r--manual/CHAPTER_Prog/Makefile12
-rw-r--r--manual/CHAPTER_Prog/stubnets.cc130
-rw-r--r--manual/CHAPTER_Prog/test.v8
-rw-r--r--manual/CHAPTER_StateOfTheArt.tex289
-rw-r--r--manual/CHAPTER_StateOfTheArt/always01.v12
-rw-r--r--manual/CHAPTER_StateOfTheArt/always01_pub.v14
-rw-r--r--manual/CHAPTER_StateOfTheArt/always02.v15
-rw-r--r--manual/CHAPTER_StateOfTheArt/always02_pub.v14
-rw-r--r--manual/CHAPTER_StateOfTheArt/always03.v23
-rw-r--r--manual/CHAPTER_StateOfTheArt/arrays01.v16
-rw-r--r--manual/CHAPTER_StateOfTheArt/cmp_tbdata.c67
-rw-r--r--manual/CHAPTER_StateOfTheArt/forgen01.v20
-rw-r--r--manual/CHAPTER_StateOfTheArt/forgen02.v30
-rw-r--r--manual/CHAPTER_StateOfTheArt/iverilog-0.8.7-buildfixes.patch20
-rw-r--r--manual/CHAPTER_StateOfTheArt/mvsis-1.3.6-buildfixes.patch36
-rw-r--r--manual/CHAPTER_StateOfTheArt/simlib_hana.v1139
-rw-r--r--manual/CHAPTER_StateOfTheArt/simlib_icarus.v224
-rw-r--r--manual/CHAPTER_StateOfTheArt/simlib_yosys.v166
-rw-r--r--manual/CHAPTER_StateOfTheArt/sis-1.3.6-buildfixes.patch113
-rwxr-xr-xmanual/CHAPTER_StateOfTheArt/synth.sh64
-rwxr-xr-xmanual/CHAPTER_StateOfTheArt/validate_tb.sh55
-rw-r--r--manual/CHAPTER_Techmap.tex102
-rw-r--r--manual/CHAPTER_Verilog.tex849
-rw-r--r--manual/PRESENTATION_ExAdv.tex896
-rw-r--r--manual/PRESENTATION_ExAdv/.gitignore1
-rw-r--r--manual/PRESENTATION_ExAdv/Makefile28
-rw-r--r--manual/PRESENTATION_ExAdv/addshift_map.v20
-rw-r--r--manual/PRESENTATION_ExAdv/addshift_test.v5
-rw-r--r--manual/PRESENTATION_ExAdv/addshift_test.ys6
-rw-r--r--manual/PRESENTATION_ExAdv/macc_simple_test.v6
-rw-r--r--manual/PRESENTATION_ExAdv/macc_simple_test.ys37
-rw-r--r--manual/PRESENTATION_ExAdv/macc_simple_test_01.v6
-rw-r--r--manual/PRESENTATION_ExAdv/macc_simple_test_02.v6
-rw-r--r--manual/PRESENTATION_ExAdv/macc_simple_xmap.v6
-rw-r--r--manual/PRESENTATION_ExAdv/macc_xilinx_swap_map.v28
-rw-r--r--manual/PRESENTATION_ExAdv/macc_xilinx_test.v13
-rw-r--r--manual/PRESENTATION_ExAdv/macc_xilinx_test.ys43
-rw-r--r--manual/PRESENTATION_ExAdv/macc_xilinx_unwrap_map.v61
-rw-r--r--manual/PRESENTATION_ExAdv/macc_xilinx_wrap_map.v89
-rw-r--r--manual/PRESENTATION_ExAdv/macc_xilinx_xmap.v10
-rw-r--r--manual/PRESENTATION_ExAdv/mulshift_map.v26
-rw-r--r--manual/PRESENTATION_ExAdv/mulshift_test.v5
-rw-r--r--manual/PRESENTATION_ExAdv/mulshift_test.ys7
-rw-r--r--manual/PRESENTATION_ExAdv/mymul_map.v15
-rw-r--r--manual/PRESENTATION_ExAdv/mymul_test.v4
-rw-r--r--manual/PRESENTATION_ExAdv/mymul_test.ys15
-rw-r--r--manual/PRESENTATION_ExAdv/red_or3x1_cells.v5
-rw-r--r--manual/PRESENTATION_ExAdv/red_or3x1_map.v48
-rw-r--r--manual/PRESENTATION_ExAdv/red_or3x1_test.v5
-rw-r--r--manual/PRESENTATION_ExAdv/red_or3x1_test.ys7
-rw-r--r--manual/PRESENTATION_ExAdv/select.v15
-rw-r--r--manual/PRESENTATION_ExAdv/select.ys10
-rw-r--r--manual/PRESENTATION_ExAdv/sym_mul_cells.v6
-rw-r--r--manual/PRESENTATION_ExAdv/sym_mul_map.v15
-rw-r--r--manual/PRESENTATION_ExAdv/sym_mul_test.v5
-rw-r--r--manual/PRESENTATION_ExAdv/sym_mul_test.ys6
-rw-r--r--manual/PRESENTATION_ExOth.tex227
-rw-r--r--manual/PRESENTATION_ExOth/.gitignore1
-rw-r--r--manual/PRESENTATION_ExOth/Makefile16
-rw-r--r--manual/PRESENTATION_ExOth/axis_master.v27
-rw-r--r--manual/PRESENTATION_ExOth/axis_test.v27
-rw-r--r--manual/PRESENTATION_ExOth/axis_test.ys5
-rw-r--r--manual/PRESENTATION_ExOth/equiv.ys17
-rw-r--r--manual/PRESENTATION_ExOth/scrambler.v14
-rw-r--r--manual/PRESENTATION_ExOth/scrambler.ys23
-rw-r--r--manual/PRESENTATION_ExSyn.tex515
-rw-r--r--manual/PRESENTATION_ExSyn/.gitignore1
-rw-r--r--manual/PRESENTATION_ExSyn/Makefile20
-rw-r--r--manual/PRESENTATION_ExSyn/abc_01.v10
-rw-r--r--manual/PRESENTATION_ExSyn/abc_01.ys5
-rw-r--r--manual/PRESENTATION_ExSyn/abc_01_cells.lib54
-rw-r--r--manual/PRESENTATION_ExSyn/abc_01_cells.v40
-rw-r--r--manual/PRESENTATION_ExSyn/memory_01.v9
-rw-r--r--manual/PRESENTATION_ExSyn/memory_01.ys3
-rw-r--r--manual/PRESENTATION_ExSyn/memory_02.v27
-rw-r--r--manual/PRESENTATION_ExSyn/memory_02.ys4
-rw-r--r--manual/PRESENTATION_ExSyn/opt_01.v3
-rw-r--r--manual/PRESENTATION_ExSyn/opt_01.ys3
-rw-r--r--manual/PRESENTATION_ExSyn/opt_02.v3
-rw-r--r--manual/PRESENTATION_ExSyn/opt_02.ys3
-rw-r--r--manual/PRESENTATION_ExSyn/opt_03.v4
-rw-r--r--manual/PRESENTATION_ExSyn/opt_03.ys3
-rw-r--r--manual/PRESENTATION_ExSyn/opt_04.v19
-rw-r--r--manual/PRESENTATION_ExSyn/opt_04.ys3
-rw-r--r--manual/PRESENTATION_ExSyn/proc_01.v7
-rw-r--r--manual/PRESENTATION_ExSyn/proc_01.ys3
-rw-r--r--manual/PRESENTATION_ExSyn/proc_02.v8
-rw-r--r--manual/PRESENTATION_ExSyn/proc_02.ys3
-rw-r--r--manual/PRESENTATION_ExSyn/proc_03.v10
-rw-r--r--manual/PRESENTATION_ExSyn/proc_03.ys3
-rw-r--r--manual/PRESENTATION_ExSyn/techmap_01.v4
-rw-r--r--manual/PRESENTATION_ExSyn/techmap_01.ys3
-rw-r--r--manual/PRESENTATION_ExSyn/techmap_01_map.v24
-rw-r--r--manual/PRESENTATION_Intro.tex956
-rw-r--r--manual/PRESENTATION_Intro/.gitignore4
-rw-r--r--manual/PRESENTATION_Intro/Makefile10
-rw-r--r--manual/PRESENTATION_Intro/counter.v12
-rw-r--r--manual/PRESENTATION_Intro/counter.ys27
-rw-r--r--manual/PRESENTATION_Intro/mycells.lib38
-rw-r--r--manual/PRESENTATION_Intro/mycells.v23
-rw-r--r--manual/PRESENTATION_Prog.tex597
-rw-r--r--manual/PRESENTATION_Prog/.gitignore2
-rw-r--r--manual/PRESENTATION_Prog/Makefile18
-rw-r--r--manual/PRESENTATION_Prog/absval_ref.v3
-rw-r--r--manual/PRESENTATION_Prog/my_cmd.cc76
-rw-r--r--manual/PRESENTATION_Prog/sigmap_test.v3
-rwxr-xr-xmanual/appnotes.sh22
-rwxr-xr-xmanual/clean.sh2
-rw-r--r--manual/command-reference-manual.tex4466
-rw-r--r--manual/literature.bib163
-rwxr-xr-xmanual/manual.sh59
-rw-r--r--manual/manual.tex226
-rwxr-xr-xmanual/presentation.sh54
-rw-r--r--manual/presentation.tex162
-rw-r--r--manual/weblinks.bib134
-rw-r--r--misc/create_vcxsrc.sh54
-rw-r--r--misc/yosys-config.in107
-rw-r--r--misc/yosysjs/demo01.html197
-rw-r--r--misc/yosysjs/demo02.html103
-rw-r--r--misc/yosysjs/demo03.html103
-rw-r--r--misc/yosysjs/yosysjs.js312
-rw-r--r--misc/yosysjs/yosyswrk.js63
-rw-r--r--passes/cmds/Makefile.inc28
-rw-r--r--passes/cmds/add.cc154
-rw-r--r--passes/cmds/check.cc162
-rw-r--r--passes/cmds/connect.cc189
-rw-r--r--passes/cmds/connwrappers.cc209
-rw-r--r--passes/cmds/copy.cc59
-rw-r--r--passes/cmds/cover.cc157
-rw-r--r--passes/cmds/delete.cc144
-rw-r--r--passes/cmds/design.cc256
-rw-r--r--passes/cmds/edgetypes.cc106
-rw-r--r--passes/cmds/logcmd.cc82
-rw-r--r--passes/cmds/plugin.cc131
-rw-r--r--passes/cmds/qwp.cc870
-rw-r--r--passes/cmds/rename.cc232
-rw-r--r--passes/cmds/scatter.cc73
-rw-r--r--passes/cmds/scc.cc344
-rw-r--r--passes/cmds/select.cc1582
-rw-r--r--passes/cmds/setattr.cc271
-rw-r--r--passes/cmds/setundef.cc243
-rw-r--r--passes/cmds/show.cc851
-rw-r--r--passes/cmds/splice.cc369
-rw-r--r--passes/cmds/splitnets.cc270
-rw-r--r--passes/cmds/stat.cc297
-rw-r--r--passes/cmds/tee.cc102
-rw-r--r--passes/cmds/torder.cc123
-rw-r--r--passes/cmds/trace.cc98
-rw-r--r--passes/cmds/write_file.cc80
-rw-r--r--passes/equiv/Makefile.inc12
-rw-r--r--passes/equiv/equiv_add.cc177
-rw-r--r--passes/equiv/equiv_induct.cc241
-rw-r--r--passes/equiv/equiv_make.cc474
-rw-r--r--passes/equiv/equiv_mark.cc239
-rw-r--r--passes/equiv/equiv_miter.cc343
-rw-r--r--passes/equiv/equiv_purge.cc210
-rw-r--r--passes/equiv/equiv_remove.cc83
-rw-r--r--passes/equiv/equiv_simple.cc358
-rw-r--r--passes/equiv/equiv_status.cc94
-rw-r--r--passes/equiv/equiv_struct.cc367
-rw-r--r--passes/fsm/Makefile.inc11
-rw-r--r--passes/fsm/fsm.cc157
-rw-r--r--passes/fsm/fsm_detect.cc316
-rw-r--r--passes/fsm/fsm_expand.cc289
-rw-r--r--passes/fsm/fsm_export.cc189
-rw-r--r--passes/fsm/fsm_extract.cc480
-rw-r--r--passes/fsm/fsm_info.cc62
-rw-r--r--passes/fsm/fsm_map.cc355
-rw-r--r--passes/fsm/fsm_opt.cc351
-rw-r--r--passes/fsm/fsm_recode.cc197
-rw-r--r--passes/fsm/fsmdata.h178
-rw-r--r--passes/hierarchy/Makefile.inc5
-rw-r--r--passes/hierarchy/hierarchy.cc621
-rw-r--r--passes/hierarchy/singleton.cc101
-rw-r--r--passes/hierarchy/submod.cc368
-rw-r--r--passes/memory/Makefile.inc10
-rw-r--r--passes/memory/memory.cc102
-rw-r--r--passes/memory/memory_bram.cc1236
-rw-r--r--passes/memory/memory_collect.cc268
-rw-r--r--passes/memory/memory_dff.cc305
-rw-r--r--passes/memory/memory_map.cc373
-rw-r--r--passes/memory/memory_memx.cc92
-rw-r--r--passes/memory/memory_share.cc763
-rw-r--r--passes/memory/memory_unpack.cc149
-rw-r--r--passes/opt/Makefile.inc14
-rw-r--r--passes/opt/opt.cc168
-rw-r--r--passes/opt/opt_clean.cc484
-rw-r--r--passes/opt/opt_expr.cc1283
-rw-r--r--passes/opt/opt_merge.cc383
-rw-r--r--passes/opt/opt_muxtree.cc483
-rw-r--r--passes/opt/opt_reduce.cc388
-rw-r--r--passes/opt/opt_rmdff.cc325
-rw-r--r--passes/opt/share.cc1533
-rw-r--r--passes/opt/wreduce.cc436
-rw-r--r--passes/proc/Makefile.inc10
-rw-r--r--passes/proc/proc.cc100
-rw-r--r--passes/proc/proc_arst.cc292
-rw-r--r--passes/proc/proc_clean.cc187
-rw-r--r--passes/proc/proc_dff.cc398
-rw-r--r--passes/proc/proc_dlatch.cc449
-rw-r--r--passes/proc/proc_init.cc130
-rw-r--r--passes/proc/proc_mux.cc423
-rw-r--r--passes/proc/proc_rmdead.cc104
-rw-r--r--passes/sat/Makefile.inc9
-rw-r--r--passes/sat/assertpmux.cc240
-rw-r--r--passes/sat/clk2fflogic.cc226
-rw-r--r--passes/sat/eval.cc603
-rw-r--r--passes/sat/example.v85
-rw-r--r--passes/sat/example.ys14
-rw-r--r--passes/sat/expose.cc650
-rw-r--r--passes/sat/freduce.cc840
-rw-r--r--passes/sat/miter.cc421
-rw-r--r--passes/sat/sat.cc1713
-rw-r--r--passes/techmap/.gitignore1
-rw-r--r--passes/techmap/Makefile.inc57
-rw-r--r--passes/techmap/abc.cc1666
-rw-r--r--passes/techmap/aigmap.cc149
-rw-r--r--passes/techmap/alumacc.cc567
-rw-r--r--passes/techmap/attrmap.cc269
-rw-r--r--passes/techmap/attrmvcp.cc141
-rw-r--r--passes/techmap/deminout.cc116
-rw-r--r--passes/techmap/dff2dffe.cc369
-rw-r--r--passes/techmap/dffinit.cc135
-rw-r--r--passes/techmap/dfflibmap.cc654
-rw-r--r--passes/techmap/dffsr2dff.cc213
-rw-r--r--passes/techmap/extract.cc763
-rw-r--r--passes/techmap/filterlib.cc4
-rw-r--r--passes/techmap/hilomap.cc123
-rw-r--r--passes/techmap/insbuf.cc94
-rw-r--r--passes/techmap/iopadmap.cc364
-rw-r--r--passes/techmap/libparse.cc634
-rw-r--r--passes/techmap/libparse.h56
-rw-r--r--passes/techmap/lut2mux.cc93
-rw-r--r--passes/techmap/maccmap.cc404
-rw-r--r--passes/techmap/muxcover.cc632
-rw-r--r--passes/techmap/nlutmap.cc187
-rw-r--r--passes/techmap/pmuxtree.cc112
-rw-r--r--passes/techmap/shregmap.cc584
-rw-r--r--passes/techmap/simplemap.cc618
-rw-r--r--passes/techmap/simplemap.h49
-rw-r--r--passes/techmap/techmap.cc1210
-rw-r--r--passes/techmap/tribuf.cc186
-rw-r--r--passes/techmap/zinit.cc151
-rw-r--r--passes/tests/Makefile.inc5
-rw-r--r--passes/tests/test_abcloop.cc289
-rw-r--r--passes/tests/test_autotb.cc379
-rw-r--r--passes/tests/test_cell.cc965
-rw-r--r--techlibs/.gitignore1
-rw-r--r--techlibs/common/.gitignore2
-rw-r--r--techlibs/common/Makefile.inc28
-rw-r--r--techlibs/common/adff2dff.v27
-rw-r--r--techlibs/common/cellhelp.py34
-rw-r--r--techlibs/common/cells.lib108
-rw-r--r--techlibs/common/pmux2mux.v21
-rw-r--r--techlibs/common/prep.cc216
-rw-r--r--techlibs/common/simcells.v1276
-rw-r--r--techlibs/common/simlib.v1818
-rw-r--r--techlibs/common/synth.cc223
-rw-r--r--techlibs/common/techmap.v459
-rw-r--r--techlibs/gowin/Makefile.inc6
-rw-r--r--techlibs/gowin/cells_map.v31
-rw-r--r--techlibs/gowin/cells_sim.v51
-rw-r--r--techlibs/gowin/synth_gowin.cc178
-rw-r--r--techlibs/greenpak4/Makefile.inc8
-rw-r--r--techlibs/greenpak4/cells_map.v94
-rw-r--r--techlibs/greenpak4/cells_sim.v461
-rw-r--r--techlibs/greenpak4/gp_dff.lib36
-rw-r--r--techlibs/greenpak4/greenpak4_counters.cc442
-rw-r--r--techlibs/greenpak4/greenpak4_dffinv.cc197
-rw-r--r--techlibs/greenpak4/synth_greenpak4.cc209
-rw-r--r--techlibs/ice40/.gitignore4
-rw-r--r--techlibs/ice40/Makefile.inc33
-rw-r--r--techlibs/ice40/arith_map.v70
-rw-r--r--techlibs/ice40/brams.txt40
-rw-r--r--techlibs/ice40/brams_init.py14
-rw-r--r--techlibs/ice40/brams_map.v311
-rw-r--r--techlibs/ice40/cells_map.v57
-rw-r--r--techlibs/ice40/cells_sim.v883
-rw-r--r--techlibs/ice40/ice40_ffinit.cc173
-rw-r--r--techlibs/ice40/ice40_ffssr.cc128
-rw-r--r--techlibs/ice40/ice40_opt.cc193
-rw-r--r--techlibs/ice40/latches_map.v11
-rw-r--r--techlibs/ice40/synth_ice40.cc250
-rw-r--r--techlibs/ice40/tests/.gitignore2
-rw-r--r--techlibs/ice40/tests/test_arith.v3
-rw-r--r--techlibs/ice40/tests/test_arith.ys10
-rw-r--r--techlibs/ice40/tests/test_bram.sh19
-rw-r--r--techlibs/ice40/tests/test_bram.v24
-rw-r--r--techlibs/ice40/tests/test_bram_tb.v110
-rw-r--r--techlibs/ice40/tests/test_ffs.sh20
-rw-r--r--techlibs/ice40/tests/test_ffs.v42
-rw-r--r--techlibs/xilinx/.gitignore2
-rw-r--r--techlibs/xilinx/Makefile.inc37
-rw-r--r--techlibs/xilinx/arith_map.v91
-rw-r--r--techlibs/xilinx/brams.txt105
-rw-r--r--techlibs/xilinx/brams_bb.v319
-rw-r--r--techlibs/xilinx/brams_init.py34
-rw-r--r--techlibs/xilinx/brams_map.v359
-rw-r--r--techlibs/xilinx/cells_map.v84
-rw-r--r--techlibs/xilinx/cells_sim.v158
-rw-r--r--techlibs/xilinx/cells_xtra.sh145
-rw-r--r--techlibs/xilinx/cells_xtra.v3293
-rw-r--r--techlibs/xilinx/drams.txt36
-rw-r--r--techlibs/xilinx/drams_bb.v20
-rw-r--r--techlibs/xilinx/drams_map.v63
-rw-r--r--techlibs/xilinx/synth_xilinx.cc239
-rw-r--r--techlibs/xilinx/tests/.gitignore6
-rw-r--r--techlibs/xilinx/tests/bram1.sh64
-rw-r--r--techlibs/xilinx/tests/bram1.v41
-rw-r--r--techlibs/xilinx/tests/bram1_tb.v148
-rw-r--r--techlibs/xilinx/tests/bram2.sh8
-rw-r--r--techlibs/xilinx/tests/bram2.v35
-rw-r--r--techlibs/xilinx/tests/bram2_tb.v56
-rw-r--r--tests/asicworld/.gitignore2
-rw-r--r--tests/asicworld/README1
-rw-r--r--tests/asicworld/code_hdl_models_GrayCounter.v33
-rw-r--r--tests/asicworld/code_hdl_models_arbiter.v123
-rw-r--r--tests/asicworld/code_hdl_models_arbiter_tb.v60
-rw-r--r--tests/asicworld/code_hdl_models_cam.v60
-rw-r--r--tests/asicworld/code_hdl_models_clk_div.v27
-rw-r--r--tests/asicworld/code_hdl_models_clk_div_45.v54
-rw-r--r--tests/asicworld/code_hdl_models_d_ff_gates.v29
-rw-r--r--tests/asicworld/code_hdl_models_d_latch_gates.v15
-rw-r--r--tests/asicworld/code_hdl_models_decoder_2to4_gates.v14
-rw-r--r--tests/asicworld/code_hdl_models_decoder_using_assign.v20
-rw-r--r--tests/asicworld/code_hdl_models_decoder_using_case.v43
-rw-r--r--tests/asicworld/code_hdl_models_dff_async_reset.v30
-rw-r--r--tests/asicworld/code_hdl_models_dff_sync_reset.v30
-rw-r--r--tests/asicworld/code_hdl_models_encoder_4to2_gates.v8
-rw-r--r--tests/asicworld/code_hdl_models_encoder_using_case.v42
-rw-r--r--tests/asicworld/code_hdl_models_encoder_using_if.v58
-rw-r--r--tests/asicworld/code_hdl_models_full_adder_gates.v18
-rw-r--r--tests/asicworld/code_hdl_models_full_subtracter_gates.v20
-rw-r--r--tests/asicworld/code_hdl_models_gray_counter.v33
-rw-r--r--tests/asicworld/code_hdl_models_half_adder_gates.v14
-rw-r--r--tests/asicworld/code_hdl_models_lfsr.v35
-rw-r--r--tests/asicworld/code_hdl_models_lfsr_updown.v35
-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_mux_2to1_gates.v18
-rw-r--r--tests/asicworld/code_hdl_models_mux_using_assign.v22
-rw-r--r--tests/asicworld/code_hdl_models_mux_using_case.v28
-rw-r--r--tests/asicworld/code_hdl_models_mux_using_if.v29
-rw-r--r--tests/asicworld/code_hdl_models_nand_switch.v14
-rw-r--r--tests/asicworld/code_hdl_models_one_hot_cnt.v31
-rw-r--r--tests/asicworld/code_hdl_models_parallel_crc.v53
-rw-r--r--tests/asicworld/code_hdl_models_parity_using_assign.v21
-rw-r--r--tests/asicworld/code_hdl_models_parity_using_bitwise.v16
-rw-r--r--tests/asicworld/code_hdl_models_parity_using_function.v29
-rw-r--r--tests/asicworld/code_hdl_models_pri_encoder_using_assign.v36
-rw-r--r--tests/asicworld/code_hdl_models_rom_using_case.v42
-rw-r--r--tests/asicworld/code_hdl_models_serial_crc.v54
-rw-r--r--tests/asicworld/code_hdl_models_t_gate_switch.v11
-rw-r--r--tests/asicworld/code_hdl_models_tff_async_reset.v27
-rw-r--r--tests/asicworld/code_hdl_models_tff_sync_reset.v27
-rw-r--r--tests/asicworld/code_hdl_models_uart.v154
-rw-r--r--tests/asicworld/code_hdl_models_up_counter.v29
-rw-r--r--tests/asicworld/code_hdl_models_up_counter_load.v32
-rw-r--r--tests/asicworld/code_hdl_models_up_down_counter.v29
-rw-r--r--tests/asicworld/code_specman_switch_fabric.v82
-rw-r--r--tests/asicworld/code_tidbits_asyn_reset.v18
-rw-r--r--tests/asicworld/code_tidbits_blocking.v17
-rw-r--r--tests/asicworld/code_tidbits_fsm_using_always.v91
-rw-r--r--tests/asicworld/code_tidbits_fsm_using_function.v94
-rw-r--r--tests/asicworld/code_tidbits_fsm_using_single_always.v63
-rw-r--r--tests/asicworld/code_tidbits_nonblocking.v17
-rw-r--r--tests/asicworld/code_tidbits_reg_combo_example.v13
-rw-r--r--tests/asicworld/code_tidbits_reg_seq_example.v15
-rw-r--r--tests/asicworld/code_tidbits_syn_reset.v19
-rw-r--r--tests/asicworld/code_tidbits_wire_example.v9
-rw-r--r--tests/asicworld/code_verilog_tutorial_addbit.v24
-rw-r--r--tests/asicworld/code_verilog_tutorial_always_example.v11
-rw-r--r--tests/asicworld/code_verilog_tutorial_bus_con.v8
-rw-r--r--tests/asicworld/code_verilog_tutorial_comment.v25
-rw-r--r--tests/asicworld/code_verilog_tutorial_counter.v19
-rw-r--r--tests/asicworld/code_verilog_tutorial_counter_tb.v101
-rw-r--r--tests/asicworld/code_verilog_tutorial_d_ff.v14
-rw-r--r--tests/asicworld/code_verilog_tutorial_decoder.v14
-rw-r--r--tests/asicworld/code_verilog_tutorial_decoder_always.v20
-rw-r--r--tests/asicworld/code_verilog_tutorial_escape_id.v14
-rw-r--r--tests/asicworld/code_verilog_tutorial_explicit.v35
-rw-r--r--tests/asicworld/code_verilog_tutorial_first_counter.v47
-rw-r--r--tests/asicworld/code_verilog_tutorial_first_counter_tb.v37
-rw-r--r--tests/asicworld/code_verilog_tutorial_flip_flop.v15
-rw-r--r--tests/asicworld/code_verilog_tutorial_fsm_full.v114
-rw-r--r--tests/asicworld/code_verilog_tutorial_fsm_full_tb.v54
-rw-r--r--tests/asicworld/code_verilog_tutorial_good_code.v18
-rw-r--r--tests/asicworld/code_verilog_tutorial_if_else.v13
-rw-r--r--tests/asicworld/code_verilog_tutorial_multiply.v8
-rw-r--r--tests/asicworld/code_verilog_tutorial_mux_21.v9
-rw-r--r--tests/asicworld/code_verilog_tutorial_n_out_primitive.v13
-rw-r--r--tests/asicworld/code_verilog_tutorial_parallel_if.v21
-rw-r--r--tests/asicworld/code_verilog_tutorial_parity.v41
-rw-r--r--tests/asicworld/code_verilog_tutorial_simple_function.v10
-rw-r--r--tests/asicworld/code_verilog_tutorial_simple_if.v11
-rw-r--r--tests/asicworld/code_verilog_tutorial_task_global.v12
-rw-r--r--tests/asicworld/code_verilog_tutorial_tri_buf.v9
-rw-r--r--tests/asicworld/code_verilog_tutorial_v2k_reg.v24
-rw-r--r--tests/asicworld/code_verilog_tutorial_which_clock.v12
-rwxr-xr-xtests/asicworld/run-test.sh14
-rw-r--r--tests/bram/.gitignore1
-rw-r--r--tests/bram/generate.py287
-rw-r--r--tests/bram/run-single.sh12
-rwxr-xr-xtests/bram/run-test.sh47
-rw-r--r--tests/fsm/.gitignore1
-rw-r--r--tests/fsm/generate.py118
-rwxr-xr-xtests/fsm/run-test.sh44
-rw-r--r--tests/hana/.gitignore2
-rw-r--r--tests/hana/README4
-rw-r--r--tests/hana/hana_vlib.v1139
-rwxr-xr-xtests/hana/run-test.sh14
-rw-r--r--tests/hana/test_intermout.v418
-rw-r--r--tests/hana/test_parse2synthtrans.v117
-rw-r--r--tests/hana/test_parser.v87
-rw-r--r--tests/hana/test_simulation_always.v135
-rw-r--r--tests/hana/test_simulation_and.v35
-rw-r--r--tests/hana/test_simulation_buffer.v17
-rw-r--r--tests/hana/test_simulation_decoder.v219
-rw-r--r--tests/hana/test_simulation_inc.v42
-rw-r--r--tests/hana/test_simulation_mux.v176
-rw-r--r--tests/hana/test_simulation_nand.v25
-rw-r--r--tests/hana/test_simulation_nor.v20
-rw-r--r--tests/hana/test_simulation_or.v30
-rw-r--r--tests/hana/test_simulation_seq.v12
-rw-r--r--tests/hana/test_simulation_shifter.v60
-rw-r--r--tests/hana/test_simulation_sop.v65
-rw-r--r--tests/hana/test_simulation_techmap.v172
-rw-r--r--tests/hana/test_simulation_techmap_tech.v143
-rw-r--r--tests/hana/test_simulation_vlib.v65
-rw-r--r--tests/hana/test_simulation_xnor.v20
-rw-r--r--tests/hana/test_simulation_xor.v20
-rw-r--r--tests/memories/.gitignore3
-rw-r--r--tests/memories/amber23_sram_byte_en.v84
-rw-r--r--tests/memories/implicit_en.v24
-rw-r--r--tests/memories/no_implicit_en.v24
-rwxr-xr-xtests/memories/run-test.sh30
-rw-r--r--tests/memories/shared_ports.v25
-rw-r--r--tests/memories/simple_sram_byte_en.v26
-rw-r--r--tests/realmath/.gitignore1
-rw-r--r--tests/realmath/generate.py101
-rwxr-xr-xtests/realmath/run-test.sh36
-rw-r--r--tests/sat/.gitignore1
-rw-r--r--tests/sat/asserts.v14
-rw-r--r--tests/sat/asserts.ys3
-rw-r--r--tests/sat/asserts_seq.v87
-rw-r--r--tests/sat/asserts_seq.ys15
-rw-r--r--tests/sat/counters.v35
-rw-r--r--tests/sat/counters.ys10
-rw-r--r--tests/sat/expose_dff.v33
-rw-r--r--tests/sat/expose_dff.ys15
-rw-r--r--tests/sat/initval.v15
-rw-r--r--tests/sat/initval.ys4
-rwxr-xr-xtests/sat/run-test.sh6
-rw-r--r--tests/sat/share.v32
-rw-r--r--tests/sat/share.ys17
-rw-r--r--tests/sat/splice.v14
-rw-r--r--tests/sat/splice.ys14
-rw-r--r--tests/share/.gitignore1
-rw-r--r--tests/share/generate.py82
-rwxr-xr-xtests/share/run-test.sh39
-rw-r--r--tests/simple/.gitignore2
-rw-r--r--tests/simple/aes_kexp128.v24
-rw-r--r--tests/simple/always01.v10
-rw-r--r--tests/simple/always02.v13
-rw-r--r--tests/simple/always03.v22
-rw-r--r--tests/simple/arraycells.v15
-rw-r--r--tests/simple/arrays01.v16
-rw-r--r--tests/simple/carryadd.v24
-rw-r--r--tests/simple/constmuldivmod.v27
-rw-r--r--tests/simple/constpower.v15
-rw-r--r--tests/simple/dff_different_styles.v105
-rw-r--r--tests/simple/fiedler-cooley.v33
-rw-r--r--tests/simple/forgen01.v23
-rw-r--r--tests/simple/forgen02.v30
-rw-r--r--tests/simple/fsm.v69
-rw-r--r--tests/simple/generate.v94
-rw-r--r--tests/simple/graphtest.v34
-rw-r--r--tests/simple/hierarchy.v27
-rw-r--r--tests/simple/i2c_master_tests.v62
-rw-r--r--tests/simple/loops.v79
-rw-r--r--tests/simple/macros.v244
-rw-r--r--tests/simple/mem2reg.v94
-rw-r--r--tests/simple/mem_arst.v43
-rw-r--r--tests/simple/memory.v309
-rw-r--r--tests/simple/multiplier.v132
-rw-r--r--tests/simple/muxtree.v83
-rw-r--r--tests/simple/omsp_dbg_uart.v34
-rw-r--r--tests/simple/operators.v121
-rw-r--r--tests/simple/paramods.v53
-rw-r--r--tests/simple/partsel.v62
-rw-r--r--tests/simple/process.v84
-rw-r--r--tests/simple/realexpr.v24
-rw-r--r--tests/simple/repwhile.v36
-rw-r--r--tests/simple/rotate.v48
-rwxr-xr-xtests/simple/run-test.sh20
-rw-r--r--tests/simple/scopes.v63
-rw-r--r--tests/simple/signedexpr.v18
-rw-r--r--tests/simple/sincos.v124
-rw-r--r--tests/simple/subbytes.v82
-rw-r--r--tests/simple/task_func.v122
-rw-r--r--tests/simple/undef_eqx_nex.v11
-rw-r--r--tests/simple/usb_phy_tests.v36
-rw-r--r--tests/simple/values.v44
-rw-r--r--tests/simple/vloghammer.v82
-rw-r--r--tests/simple/wreduce.v9
-rw-r--r--tests/smv/.gitignore1
-rw-r--r--tests/smv/run-single.sh33
-rwxr-xr-xtests/smv/run-test.sh19
-rw-r--r--tests/techmap/.gitignore1
-rw-r--r--tests/techmap/mem_simple_4x1_cells.v13
-rw-r--r--tests/techmap/mem_simple_4x1_map.v152
-rw-r--r--tests/techmap/mem_simple_4x1_runtest.sh17
-rw-r--r--tests/techmap/mem_simple_4x1_tb.v29
-rw-r--r--tests/techmap/mem_simple_4x1_uut.v15
-rwxr-xr-xtests/techmap/run-test.sh10
-rw-r--r--tests/tools/.gitignore1
-rw-r--r--tests/tools/autotest.mk13
-rwxr-xr-xtests/tools/autotest.sh178
-rw-r--r--tests/tools/cmp_tbdata.c69
-rwxr-xr-xtests/tools/profiler.pl55
-rwxr-xr-xtests/tools/txt2tikztiming.py106
-rwxr-xr-xtests/tools/vcd2txt.pl61
-rwxr-xr-xtests/tools/vcdcd.pl287
-rw-r--r--tests/various/.gitignore1
-rw-r--r--tests/various/constmsk_test.v4
-rw-r--r--tests/various/constmsk_test.ys15
-rw-r--r--tests/various/constmsk_testmap.v49
-rw-r--r--tests/various/muxcover.ys51
-rwxr-xr-xtests/various/run-test.sh6
-rw-r--r--tests/various/submod_extract.ys21
-rw-r--r--tests/vloghtb/.gitignore9
-rw-r--r--tests/vloghtb/common.sh106
-rwxr-xr-xtests/vloghtb/run-test.sh15
-rw-r--r--tests/vloghtb/test_febe.sh13
-rw-r--r--tests/vloghtb/test_makefile9
-rw-r--r--tests/vloghtb/test_mapopt.sh17
-rw-r--r--tests/vloghtb/test_share.sh11
788 files changed, 140939 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..93e28cd6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,29 @@
+*.o
+*.d
+.*.swp
+/.cproject
+/.project
+/.settings
+/qtcreator.files
+/qtcreator.includes
+/qtcreator.config
+/qtcreator.creator
+/qtcreator.creator.user
+/Makefile.conf
+/abc
+/viz.js
+/yosys
+/yosys.exe
+/yosys.js
+/yosys-abc
+/yosys-abc.exe
+/yosys-config
+/yosys-smtbmc
+/yosys-filterlib
+/yosys-filterlib.exe
+/kernel/version_*.cc
+/share
+/yosys-win32-mxebin-*
+/yosys-win32-vcxsrc-*
+/yosysjs-*
+/libyosys.so
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..9f0cc06e
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,34 @@
+sudo: false
+script: make && make test
+language: cpp
+addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - gperf
+ - build-essential
+ - clang
+ - bison
+ - flex
+ - libreadline-dev
+ - gawk
+ - tcl-dev
+ - libffi-dev
+ - git
+ - mercurial
+ - graphviz
+ - xdot
+ - pkg-config
+ - python
+ - g++-4.8
+before_install:
+ - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi
+ - git clone git://github.com/steveicarus/iverilog.git
+ - (cd iverilog && autoconf && ./configure --prefix=$HOME/iverilog && make && make install)
+ - export PATH=$PATH:$HOME/iverilog/bin
+compiler:
+# - clang
+ - gcc
+os:
+ - linux
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 00000000..bfea999a
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,489 @@
+
+List of major changes and improvements between releases
+=======================================================
+
+
+Yosys 0.6 .. Yosys 0.7
+----------------------
+
+ * Various
+ - Added "yosys -D" feature
+ - Added support for installed plugins in $(DATDIR)/plugins/
+ - Renamed opt_const to opt_expr
+ - Renamed opt_share to opt_merge
+ - Added "prep -flatten" and "synth -flatten"
+ - Added "prep -auto-top" and "synth -auto-top"
+ - Using "mfs" and "lutpack" in ABC lut mapping
+ - Support for abstract modules in chparam
+ - Cleanup abstract modules at end of "hierarchy -top"
+ - Added tristate buffer support to iopadmap
+ - Added opt_expr support for div/mod by power-of-two
+ - Added "select -assert-min <N> -assert-max <N>"
+ - Added "attrmvcp" pass
+ - Added "attrmap" command
+ - Added "tee +INT -INT"
+ - Added "zinit" pass
+ - Added "setparam -type"
+ - Added "shregmap" pass
+ - Added "setundef -init"
+ - Added "nlutmap -assert"
+ - Added $sop cell type and "abc -sop -I <num> -P <num>"
+ - Added "dc2" to default ABC scripts
+ - Added "deminout"
+ - Added "insbuf" command
+ - Added "prep -nomem"
+ - Added "opt_rmdff -keepdc"
+ - Added "prep -nokeepdc"
+ - Added initial version of "synth_gowin"
+ - Added "fsm_expand -full"
+ - Added support for fsm_encoding="user"
+ - Many improvements in GreenPAK4 support
+ - Added black box modules for all Xilinx 7-series lib cells
+ - Added synth_ice40 support for latches via logic loops
+ - Fixed ice40_opt lut unmapping, added "ice40_opt -unlut"
+
+ * Build System
+ - Added ABCEXTERNAL and ABCURL make variables
+ - Added BINDIR, LIBDIR, and DATDIR make variables
+ - Added PKG_CONFIG make variable
+ - Added SEED make variable (for "make test")
+ - Added YOSYS_VER_STR make variable
+ - Updated min GCC requirement to GCC 4.8
+ - Updated required Bison version to Bison 3.x
+
+ * Internal APIs
+ - Added ast.h to exported headers
+ - Added ScriptPass helper class for script-like passes
+ - Added CellEdgesDatabase API
+
+ * Front-ends and Back-ends
+ - Added filename glob support to all front-ends
+ - Added avail (black-box) module params to ilang format
+ - Added $display %m support
+ - Added support for $stop Verilog system task
+ - Added support for SystemVerilog packages
+ - Fixed procedural assignments to non-unique lvalues, e.g. {y,y} = {a,b}
+ - Added support for "active high" and "active low" latches in read_blif and write_blif
+ - Use init value "2" for all uninitialized FFs in BLIF back-end
+ - Added "read_blif -sop"
+ - Added "write_blif -noalias"
+ - Added various write_blif options for VTR support
+ - write_json: also write module attributes.
+ - Added "write_verilog -nodec -nostr -defparam"
+ - Added "read_verilog -norestrict -assume-asserts"
+ - Added support for bus interfaces to "read_liberty -lib"
+ - Added liberty parser support for types within cell decls
+ - Added "write_verilog -renameprefix -v"
+ - Added "write_edif -nogndvcc"
+
+ * Formal Verification
+ - Support for hierarchical designs in smt2 back-end
+ - Yosys-smtbmc: Support for hierarchical VCD dumping
+ - Added $initstate cell type and vlog function
+ - Added $anyconst and $anyseq cell types and vlog functions
+ - Added printing of code loc of failed asserts to yosys-smtbmc
+ - Added memory_memx pass, "memory -memx", and "prep -memx"
+ - Added "proc_mux -ifx"
+ - Added "yosys-smtbmc -g"
+ - Deprecated "write_smt2 -regs" (by default on now)
+ - Made "write_smt2 -bv -mem" default, added "write_smt2 -nobv -nomem"
+ - Added support for memories to smtio.py
+ - Added "yosys-smtbmc --dump-vlogtb"
+ - Added "yosys-smtbmc --smtc --dump-smtc"
+ - Added "yosys-smtbmc --dump-all"
+ - Added assertpmux command
+ - Added "yosys-smtbmc --unroll"
+ - Added $past, $stable, $rose, $fell SVA functions
+ - Added "yosys-smtbmc --noinfo and --dummy"
+ - Added "yosys-smtbmc --noincr"
+ - Added "yosys-smtbmc --cex <filename>"
+ - Added $ff and $_FF_ cell types
+ - Added $global_clock verilog syntax support for creating $ff cells
+ - Added clk2fflogic
+
+
+Yosys 0.5 .. Yosys 0.6
+----------------------
+
+ * Various
+ - Added Contributor Covenant Code of Conduct
+ - Various improvements in dict<> and pool<>
+ - Added hashlib::mfp and refactored SigMap
+ - Improved support for reals as module parameters
+ - Various improvements in SMT2 back-end
+ - Added "keep_hierarchy" attribute
+ - Verilog front-end: define `BLACKBOX in -lib mode
+ - Added API for converting internal cells to AIGs
+ - Added ENABLE_LIBYOSYS Makefile option
+ - Removed "techmap -share_map" (use "-map +/filename" instead)
+ - Switched all Python scripts to Python 3
+ - Added support for $display()/$write() and $finish() to Verilog front-end
+ - Added "yosys-smtbmc" formal verification flow
+ - Added options for clang sanitizers to Makefile
+
+ * New commands and options
+ - Added "scc -expect <N> -nofeedback"
+ - Added "proc_dlatch"
+ - Added "check"
+ - Added "select %xe %cie %coe %M %C %R"
+ - Added "sat -dump_json" (WaveJSON format)
+ - Added "sat -tempinduct-baseonly -tempinduct-inductonly"
+ - Added "sat -stepsize" and "sat -tempinduct-step"
+ - Added "sat -show-regs -show-public -show-all"
+ - Added "write_json" (Native Yosys JSON format)
+ - Added "write_blif -attr"
+ - Added "dffinit"
+ - Added "chparam"
+ - Added "muxcover"
+ - Added "pmuxtree"
+ - Added memory_bram "make_outreg" feature
+ - Added "splice -wires"
+ - Added "dff2dffe -direct-match"
+ - Added simplemap $lut support
+ - Added "read_blif"
+ - Added "opt_share -share_all"
+ - Added "aigmap"
+ - Added "write_smt2 -mem -regs -wires"
+ - Added "memory -nordff"
+ - Added "write_smv"
+ - Added "synth -nordff -noalumacc"
+ - Added "rename -top new_name"
+ - Added "opt_const -clkinv"
+ - Added "synth -nofsm"
+ - Added "miter -assert"
+ - Added "read_verilog -noautowire"
+ - Added "read_verilog -nodpi"
+ - Added "tribuf"
+ - Added "lut2mux"
+ - Added "nlutmap"
+ - Added "qwp"
+ - Added "test_cell -noeval"
+ - Added "edgetypes"
+ - Added "equiv_struct"
+ - Added "equiv_purge"
+ - Added "equiv_mark"
+ - Added "equiv_add -try -cell"
+ - Added "singleton"
+ - Added "abc -g -luts"
+ - Added "torder"
+ - Added "write_blif -cname"
+ - Added "submod -copy"
+ - Added "dffsr2dff"
+ - Added "stat -liberty"
+
+ * Synthesis metacommands
+ - Various improvements in synth_xilinx
+ - Added synth_ice40 and synth_greenpak4
+ - Added "prep" metacommand for "synthesis lite"
+
+ * Cell library changes
+ - Added cell types to "help" system
+ - Added $meminit cell type
+ - Added $assume cell type
+ - Added $_MUX4_, $_MUX8_, and $_MUX16_ cells
+ - Added $tribuf and $_TBUF_ cell types
+ - Added read-enable to memory model
+
+ * YosysJS
+ - Various improvements in emscripten build
+ - Added alternative webworker-based JS API
+ - Added a few example applications
+
+
+Yosys 0.4 .. Yosys 0.5
+----------------------
+
+ * API changes
+ - Added log_warning()
+ - Added eval_select_args() and eval_select_op()
+ - Added cell->known(), cell->input(portname), cell->output(portname)
+ - Skip blackbox modules in design->selected_modules()
+ - Replaced std::map<> and std::set<> with dict<> and pool<>
+ - New SigSpec::extend() is what used to be SigSpec::extend_u0()
+ - Added YS_OVERRIDE, YS_FINAL, YS_ATTRIBUTE, YS_NORETURN
+
+ * Cell library changes
+ - Added flip-flops with enable ($dffe etc.)
+ - Added $equiv cells for equivalence checking framework
+
+ * Various
+ - Updated ABC to hg rev 61ad5f908c03
+ - Added clock domain partitioning to ABC pass
+ - Improved plugin building (see "yosys-config --build")
+ - Added ENABLE_NDEBUG Makefile flag for high-performance builds
+ - Added "yosys -d", "yosys -L" and other driver improvements
+ - Added support for multi-bit (array) cell ports to "write_edif"
+ - Now printing most output to stdout, not stderr
+ - Added "onehot" attribute (set by "fsm_map")
+ - Various performance improvements
+ - Vastly improved Xilinx flow
+ - Added "make unsintall"
+
+ * Equivalence checking
+ - Added equivalence checking commands:
+ equiv_make equiv_simple equiv_status
+ equiv_induct equiv_miter
+ equiv_add equiv_remove
+
+ * Block RAM support:
+ - Added "memory_bram" command
+ - Added BRAM support to Xilinx flow
+
+ * Other New Commands and Options
+ - Added "dff2dffe"
+ - Added "fsm -encfile"
+ - Added "dfflibmap -prepare"
+ - Added "write_blid -unbuf -undef -blackbox"
+ - Added "write_smt2" for writing SMT-LIBv2 files
+ - Added "test_cell -w -muxdiv"
+ - Added "select -read"
+
+
+Yosys 0.3.0 .. Yosys 0.4
+------------------------
+
+ * Platform Support
+ - Added support for mxe-based cross-builds for win32
+ - Added sourcecode-export as VisualStudio project
+ - Added experimental EMCC (JavaScript) support
+
+ * Verilog Frontend
+ - Added -sv option for SystemVerilog (and automatic *.sv file support)
+ - Added support for real-valued constants and constant expressions
+ - Added support for non-standard "via_celltype" attribute on task/func
+ - Added support for non-standard "module mod_name(...);" syntax
+ - Added support for non-standard """ macro bodies
+ - Added support for array with more than one dimension
+ - Added support for $readmemh and $readmemb
+ - Added support for DPI functions
+
+ * Changes in internal cell library
+ - Added $shift and $shiftx cell types
+ - Added $alu, $lcu, $fa and $macc cell types
+ - Removed $bu0 and $safe_pmux cell types
+ - $mem/$memwr WR_EN input is now a per-data-bit enable signal
+ - Added $_NAND_ $_NOR_ $_XNOR_ $_AOI3_ $_OAI3_ $_AOI4_ $_OAI4_
+ - Renamed ports of $lut cells (from I->O to A->Y)
+ - Renamed $_INV_ to $_NOT_
+
+ * Changes for simple synthesis flows
+ - There is now a "synth" command with a recommended default script
+ - Many improvements in synthesis of arithmetic functions to gates
+ - Multipliers and adders with many operands are using carry-save adder trees
+ - Remaining adders are now implemented using Brent-Kung carry look-ahead adders
+ - Various new high-level optimizations on RTL netlist
+ - Various improvements in FSM optimization
+ - Updated ABC to hg 5b5af75f1dda (from 2014-11-07)
+
+ * Changes in internal APIs and RTLIL
+ - Added log_id() and log_cell() helper functions
+ - Added function-like cell creation helpers
+ - Added GetSize() function (like .size() but with int)
+ - Major refactoring of RTLIL::Module and related classes
+ - Major refactoring of RTLIL::SigSpec and related classes
+ - Now RTLIL::IdString is essentially an int
+ - Added macros for code coverage counters
+ - Added some Makefile magic for pretty make logs
+ - Added "kernel/yosys.h" with all the core definitions
+ - Changed a lot of code from FILE* to c++ streams
+ - Added RTLIL::Monitor API and "trace" command
+ - Added "Yosys" C++ namespace
+
+ * Changes relevant to SAT solving
+ - Added ezSAT::keep_cnf() and ezSAT::non_incremental()
+ - Added native ezSAT support for vector shift ops
+ - Updated MiniSAT to git 37dc6c67e2 (from 2013-09-25)
+
+ * New commands (or large improvements to commands)
+ - Added "synth" command with default script
+ - Added "share" (finally some real resource sharing)
+ - Added "memory_share" (reduce number of ports on memories)
+ - Added "wreduce" and "alumacc" commands
+ - Added "opt -keepdc -fine -full -fast"
+ - Added some "test_*" commands
+
+ * Various other changes
+ - Added %D and %c select operators
+ - Added support for labels in yosys scripts
+ - Added support for here-documents in yosys scripts
+ - Support "+/" prefix for files from proc_share_dir
+ - Added "autoidx" statement to ilang language
+ - Switched from "yosys-svgviewer" to "xdot"
+ - Renamed "stdcells.v" to "techmap.v"
+ - Various bug fixes and small improvements
+ - Improved welcome and bye messages
+
+
+Yosys 0.2.0 .. Yosys 0.3.0
+--------------------------
+
+ * Driver program and overall behavior:
+ - Added "design -push" and "design -pop"
+ - Added "tee" command for redirecting log output
+
+ * Changes in the internal cell library:
+ - Added $dlatchsr and $_DLATCHSR_???_ cell types
+
+ * Improvements in Verilog frontend:
+ - Improved support for const functions (case, always, repeat)
+ - The generate..endgenerate keywords are now optional
+ - Added support for arrays of module instances
+ - Added support for "`default_nettype" directive
+ - Added support for "`line" directive
+
+ * Other front- and back-ends:
+ - Various changes to "write_blif" options
+ - Various improvements in EDIF backend
+ - Added "vhdl2verilog" pseudo-front-end
+ - Added "verific" pseudo-front-end
+
+ * Improvements in technology mapping:
+ - Added support for recursive techmap
+ - Added CONSTMSK and CONSTVAL features to techmap
+ - Added _TECHMAP_CONNMAP_*_ feature to techmap
+ - Added _TECHMAP_REPLACE_ feature to techmap
+ - Added "connwrappers" command for wrap-extract-unwrap method
+ - Added "extract -map %<design_name>" feature
+ - Added "extract -ignore_param ..." and "extract -ignore_parameters"
+ - Added "techmap -max_iter" option
+
+ * Improvements to "eval" and "sat" framework:
+ - Now include a copy of Minisat (with build fixes applied)
+ - Switched to Minisat::SimpSolver as SAT back-end
+ - Added "sat -dump_vcd" feature
+ - Added "sat -dump_cnf" feature
+ - Added "sat -initsteps <N>" feature
+ - Added "freduce -stop <N>" feature
+ - Added "freduce -dump <prefix>" feature
+
+ * Integration with ABC:
+ - Updated ABC rev to 7600ffb9340c
+
+ * Improvements in the internal APIs:
+ - Added RTLIL::Module::add... helper methods
+ - Various build fixes for OSX (Darwin) and OpenBSD
+
+
+Yosys 0.1.0 .. Yosys 0.2.0
+--------------------------
+
+ * Changes to the driver program:
+ - Added "yosys -h" and "yosys -H"
+ - Added support for backslash line continuation in scripts
+ - Added support for #-comments in same line as command
+ - Added "echo" and "log" commands
+
+ * Improvements in Verilog frontend:
+ - Added support for local registers in named blocks
+ - Added support for "case" in "generate" blocks
+ - Added support for $clog2 system function
+ - Added support for basic SystemVerilog assert statements
+ - Added preprocessor support for macro arguments
+ - Added preprocessor support for `elsif statement
+ - Added "verilog_defaults" command
+ - Added read_verilog -icells option
+ - Added support for constant sizes from parameters
+ - Added "read_verilog -setattr"
+ - Added support for function returning 'integer'
+ - Added limited support for function calls in parameter values
+ - Added "read_verilog -defer" to suppress evaluation of modules with default parameters
+
+ * Other front- and back-ends:
+ - Added BTOR backend
+ - Added Liberty frontend
+
+ * Improvements in technology mapping:
+ - The "dfflibmap" command now strongly prefers solutions with
+ no inverters in clock paths
+ - The "dfflibmap" command now prefers cells with smaller area
+ - Added support for multiple -map options to techmap
+ - Added "dfflibmap" support for //-comments in liberty files
+ - Added "memory_unpack" command to revert "memory_collect"
+ - Added standard techmap rule "techmap -share_map pmux2mux.v"
+ - Added "iopadmap -bits"
+ - Added "setundef" command
+ - Added "hilomap" command
+
+ * Changes in the internal cell library:
+ - Major rewrite of simlib.v for better compatibility with other tools
+ - Added PRIORITY parameter to $memwr cells
+ - Added TRANSPARENT parameter to $memrd cells
+ - Added RD_TRANSPARENT parameter to $mem cells
+ - Added $bu0 cell (always 0-extend, even undef MSB)
+ - Added $assert cell type
+ - Added $slice and $concat cell types
+
+ * Integration with ABC:
+ - Updated ABC to hg rev 2058c8ccea68
+ - Tighter integration of ABC build with Yosys build. The make
+ targets 'make abc' and 'make install-abc' are now obsolete.
+ - Added support for passing FFs from one clock domain through ABC
+ - Now always use BLIF as exchange format with ABC
+ - Added support for "abc -script +<command_sequence>"
+ - Improved standard ABC recipe
+ - Added support for "keep" attribute to abc command
+ - Added "abc -dff / -clk / -keepff" options
+
+ * Improvements to "eval" and "sat" framework:
+ - Added support for "0" and "~0" in right-hand side -set expressions
+ - Added "eval -set-undef" and "eval -table"
+ - Added "sat -set-init" and "sat -set-init-*" for sequential problems
+ - Added undef support to SAT solver, incl. various new "sat" options
+ - Added correct support for === and !== for "eval" and "sat"
+ - Added "sat -tempinduct" (default -seq is now non-induction sequential)
+ - Added "sat -prove-asserts"
+ - Complete rewrite of the 'freduce' command
+ - Added "miter" command
+ - Added "sat -show-inputs" and "sat -show-outputs"
+ - Added "sat -ignore_unknown_cells" (now produce an error by default)
+ - Added "sat -falsify"
+ - Now "sat -verify" and "sat -falsify" can also be used without "-prove"
+ - Added "expose" command
+ - Added support for @<sel_name> to sat and eval signal expressions
+
+ * Changes in the 'make test' framework and auxiliary test tools:
+ - Added autotest.sh -p and -f options
+ - Replaced autotest.sh ISIM support with XSIM support
+ - Added test cases for SAT framework
+
+ * Added "abbreviated IDs":
+ - Now $<something>$foo can be abbreviated as $foo.
+ - Usually this last part is a unique id (from RTLIL::autoidx)
+ - This abbreviated IDs are now also used in "show" output
+
+ * Other changes to selection framework:
+ - Now */ is optional in */<mode>:<arg> expressions
+ - Added "select -assert-none" and "select -assert-any"
+ - Added support for matching modules by attribute (A:<expr>)
+ - Added "select -none"
+ - Added support for r:<expr> pattern for matching cell parameters
+ - Added support for !=, <, <=, >=, > for attribute and parameter matching
+ - Added support for %s for selecting sub-modules
+ - Added support for %m for expanding selections to whole modules
+ - Added support for i:*, o:* and x:* pattern for selecting module ports
+ - Added support for s:<expr> pattern for matching wire width
+ - Added support for %a operation to select wire aliases
+
+ * Various other changes to commands and options:
+ - The "ls" command now supports wildcards
+ - Added "show -pause" and "show -format dot"
+ - Added "show -color" support for cells
+ - Added "show -label" and "show -notitle"
+ - Added "dump -m" and "dump -n"
+ - Added "history" command
+ - Added "rename -hide"
+ - Added "connect" command
+ - Added "splitnets -driver"
+ - Added "opt_const -mux_undef"
+ - Added "opt_const -mux_bool"
+ - Added "opt_const -undriven"
+ - Added "opt -mux_undef -mux_bool -undriven -purge"
+ - Added "hierarchy -libdir"
+ - Added "hierarchy -purge_lib" (by default now do not remove lib cells)
+ - Added "delete" command
+ - Added "dump -append"
+ - Added "setattr" and "setparam" commands
+ - Added "design -stash/-copy-from/-copy-to"
+ - Added "copy" command
+ - Added "splice" command
+
diff --git a/CodeOfConduct b/CodeOfConduct
new file mode 100644
index 00000000..4f779977
--- /dev/null
+++ b/CodeOfConduct
@@ -0,0 +1,73 @@
+Contributor Covenant Code of Conduct
+
+Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, gender identity and expression, level of experience,
+nationality, personal appearance, race, religion, or sexual identity and
+orientation.
+
+Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at clifford@clifford.at (and/or
+cliffordvienna@gmail.com if you think your mail to the other address got
+stuck in the spam filter). All complaints will be reviewed and investigated and
+will result in a response that is deemed necessary and appropriate to the
+circumstances. The project team is obligated to maintain confidentiality with
+regard to the reporter of an incident. Further details of specific enforcement
+policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+Attribution
+
+This Code of Conduct is adapted from the Contributor Covenant, version 1.4,
+available at http://contributor-covenant.org/version/1/4/
diff --git a/CodingReadme b/CodingReadme
new file mode 100644
index 00000000..cbe1fb8b
--- /dev/null
+++ b/CodingReadme
@@ -0,0 +1,413 @@
+
+This file contains some very brief documentation on things like programming APIs.
+Also consult the Yosys manual and the section about programming in the presentation.
+(Both can be downloaded as PDF from the yosys webpage.)
+
+
+--snip-- only the lines below this mark are included in the yosys manual --snip--
+Getting Started
+===============
+
+
+Outline of a Yosys command
+--------------------------
+
+Here is a the C++ code for a "hello_world" Yosys command (hello.cc):
+
+ #include "kernel/yosys.h"
+
+ USING_YOSYS_NAMESPACE
+ PRIVATE_NAMESPACE_BEGIN
+
+ struct HelloWorldPass : public Pass {
+ HelloWorldPass() : Pass("hello_world") { }
+ virtual void execute(vector<string>, Design*) {
+ log("Hello World!\n");
+ }
+ } HelloWorldPass;
+
+ PRIVATE_NAMESPACE_END
+
+This can be built into a Yosys module using the following command:
+
+ yosys-config --exec --cxx --cxxflags --ldflags -o hello.so -shared hello.cc --ldlibs
+
+Or short:
+
+ yosys-config --build hello.so hello.cc
+
+And then executed using the following command:
+
+ yosys -m hello.so -p hello_world
+
+
+Yosys Data Structures
+---------------------
+
+Here is a short list of data structures that you should make yourself familiar
+with before you write C++ code for Yosys. The following data structures are all
+defined when "kernel/yosys.h" is included and USING_YOSYS_NAMESPACE is used.
+
+ 1. Yosys Container Classes
+
+Yosys uses dict<K, T> and pool<T> as main container classes. dict<K, T> is
+essentially a replacement for std::unordered_map<K, T> and pool<T> is a
+replacement for std::unordered_set<T>. The main characteristics are:
+
+ - dict<K, T> and pool<T> are about 2x faster than the std containers
+
+ - references to elements in a dict<K, T> or pool<T> are invalidated by
+ insert and remove operations (similar to std::vector<T> on push_back()).
+
+ - some iterators are invalidated by erase(). specifically, iterators
+ that have not passed the erased element yet are invalidated. (erase()
+ itself returns valid iterator to the next element.)
+
+ - no iterators are invalidated by insert(). elements are inserted at
+ begin(). i.e. only a new iterator that starts at begin() will see the
+ inserted elements.
+
+ - the method .count(key, iterator) is like .count(key) but only
+ considers elements that can be reached via the iterator.
+
+ - iterators can be compared. it1 < it2 means that the position of t2
+ can be reached via t1 but not vice versa.
+
+ - the method .sort() can be used to sort the elements in the container
+ the container stays sorted until elements are added or removed.
+
+ - dict<K, T> and pool<T> will have the same order of iteration across
+ all compilers, standard libraries and architectures.
+
+In addition to dict<K, T> and pool<T> there is also an idict<K> that
+creates a bijective map from K to the integers. For example:
+
+ idict<string, 42> si;
+ log("%d\n", si("hello")); // will print 42
+ log("%d\n", si("world")); // will print 43
+ log("%d\n", si.at("world")); // will print 43
+ log("%d\n", si.at("dummy")); // will throw exception
+ log("%s\n", si[42].c_str())); // will print hello
+ log("%s\n", si[43].c_str())); // will print world
+ log("%s\n", si[44].c_str())); // will throw exception
+
+It is not possible to remove elements from an idict.
+
+Finally mfp<K> implements a merge-find set data structure (aka. disjoint-set or
+union-find) over the type K ("mfp" = merge-find-promote).
+
+ 2. Standard STL data types
+
+In Yosys we use std::vector<T> and std::string whenever applicable. When
+dict<K, T> and pool<T> are not suitable then std::map<K, T> and std::set<T>
+are used instead.
+
+The types std::vector<T> and std::string are also available as vector<T>
+and string in the Yosys namespace.
+
+ 3. RTLIL objects
+
+The current design (essentially a collection of modules, each defined by a
+netlist) is stored in memory using RTLIL object (declared in kernel/rtlil.h,
+automatically included by kernel/yosys.h). You should glance over at least
+the declarations for the following types in kernel/rtlil.h:
+
+ RTLIL::IdString
+ This is a handle for an identifier (e.g. cell or wire name).
+ It feels a lot like a std::string, but is only a single int
+ in size. (The actual string is stored in a global lookup
+ table.)
+
+ RTLIL::SigBit
+ A single signal bit. I.e. either a constant state (0, 1,
+ x, z) or a single bit from a wire.
+
+ RTLIL::SigSpec
+ Essentially a vector of SigBits.
+
+ RTLIL::Wire
+ RTLIL::Cell
+ The building blocks of the netlist in a module.
+
+ RTLIL::Module
+ RTLIL::Design
+ The module is a container with connected cells and wires
+ in it. The design is a container with modules in it.
+
+All this types are also available without the RTLIL:: prefix in the Yosys
+namespace.
+
+ 4. SigMap and other Helper Classes
+
+There are a couple of additional helper classes that are in wide use
+in Yosys. Most importantly there is SigMap (declared in kernel/sigtools.h).
+
+When a design has many wires in it that are connected to each other, then a
+single signal bit can have multiple valid names. The SigMap object can be used
+to map SigSpecs or SigBits to unique SigSpecs and SigBits that consistently
+only use one wire from such a group of connected wires. For example:
+
+ SigBit a = module->addWire(NEW_ID);
+ SigBit b = module->addWire(NEW_ID);
+ module->connect(a, b);
+
+ log("%d\n", a == b); // will print 0
+
+ SigMap sigmap(module);
+ log("%d\n", sigmap(a) == sigmap(b)); // will print 1
+
+
+Using the RTLIL Netlist Format
+------------------------------
+
+In the RTLIL netlist format the cell ports contain SigSpecs that point to the
+Wires. There are no references in the other direction. This has two direct
+consequences:
+
+(1) It is very easy to go from cells to wires but hard to go in the other way.
+
+(2) There is no danger in removing cells from the netlists, but removing wires
+can break the netlist format when there are still references to the wire
+somewhere in the netlist.
+
+The solution to (1) is easy: Create custom indexes that allow you to make fast
+lookups for the wire-to-cell direction. You can either use existing generic
+index structures to do that (such as the ModIndex class) or write your own
+index. For many application it is simplest to construct a custom index. For
+example:
+
+ SigMap sigmap(module);
+ dict<SigBit, Cell*> sigbit_to_driver_index;
+
+ for (auto cell : module->cells())
+ for (auto &conn : cell->connections())
+ if (cell->output(conn.first))
+ for (auto bit : sigmap(conn.second))
+ sigbit_to_driver_index[bit] = cell;
+
+Regarding (2): There is a general theme in Yosys that you don't remove wires
+from the design. You can rename them, unconnect them, but you do not actually remove
+the Wire object from the module. Instead you let the "clean" command take care
+of the dangling wires. On the other hand it is safe to remove cells (as long as
+you make sure this does not invalidate a custom index you are using in your code).
+
+
+Example Code
+------------
+
+The following yosys commands are a good starting point if you are looking for examples
+of how to use the Yosys API:
+
+ manual/CHAPTER_Prog/stubnets.cc
+ manual/PRESENTATION_Prog/my_cmd.cc
+
+
+Notes on the existing codebase
+------------------------------
+
+For historical reasons not all parts of Yosys adhere to the current coding
+style. When adding code to existing parts of the system, adhere to this guide
+for the new code instead of trying to mimic the style of the surrounding code.
+
+
+
+Coding Style
+============
+
+
+Formatting of code
+------------------
+
+- Yosys code is using tabs for indentation. Tabs are 8 characters.
+
+- A continuation of a statement in the following line is indented by
+ two additional tabs.
+
+- Lines are as long as you want them to be. A good rule of thumb is
+ to break lines at about column 150.
+
+- Opening braces can be put on the same or next line as the statement
+ opening the block (if, switch, for, while, do). Put the opening brace
+ on its own line for larger blocks, especially blocks that contains
+ blank lines.
+
+- Otherwise stick to the Linux Kernel Coding Style:
+ https://www.kernel.org/doc/Documentation/CodingStyle
+
+
+C++ Language
+-------------
+
+Yosys is written in C++11. At the moment only constructs supported by
+gcc 4.8 are allowed in Yosys code. This will change in future releases.
+
+In general Yosys uses "int" instead of "size_t". To avoid compiler
+warnings for implicit type casts, always use "GetSize(foobar)" instead
+of "foobar.size()". (GetSize() is defined in kernel/yosys.h)
+
+Use range-based for loops whenever applicable.
+
+
+--snap-- only the lines above this mark are included in the yosys manual --snap--
+
+
+Creating the Visual Studio Template Project
+===========================================
+
+1. Create an empty Visual C++ Win32 Console App project
+
+ Microsoft Visual Studio Express 2013 for Windows Desktop
+ Open New Project Wizard (File -> New Project..)
+
+ Project Name: YosysVS
+ Solution Name: YosysVS
+ [X] Create directory for solution
+ [ ] Add to source control
+
+ [X] Console applications
+ [X] Empty Project
+ [ ] SDL checks
+
+2. Open YosysVS Project Properties
+
+ Select Configuration: All Configurations
+
+ C/C++ -> General -> Additional Include Directories
+ Add: ..\yosys
+
+ C/C++ -> Preprocessor -> Preprocessor Definitions
+ Add: _YOSYS_;_CRT_SECURE_NO_WARNINGS
+
+3. Resulting file system tree:
+
+ YosysVS/
+ YosysVS/YosysVS
+ YosysVS/YosysVS/YosysVS.vcxproj
+ YosysVS/YosysVS/YosysVS.vcxproj.filters
+ YosysVS/YosysVS.sdf
+ YosysVS/YosysVS.sln
+ YosysVS/YosysVS.v12.suo
+
+4. Zip YosysVS as YosysVS-Tpl-v1.zip
+
+
+
+Checklist for adding internal cell types
+========================================
+
+Things to do right away:
+
+ - Add to kernel/celltypes.h (incl. eval() handling for non-mem cells)
+ - Add to InternalCellChecker::check() in kernel/rtlil.cc
+ - Add to techlibs/common/simlib.v
+ - Add to techlibs/common/techmap.v
+
+Things to do after finalizing the cell interface:
+
+ - Add support to kernel/satgen.h for the new cell type
+ - Add to manual/CHAPTER_CellLib.tex (or just add a fixme to the bottom)
+ - Maybe add support to the Verilog backend for dumping such cells as expression
+
+
+
+Checklist for creating Yosys releases
+=====================================
+
+Update the CHANGELOG file:
+
+ cd ~yosys
+ gitk &
+ vi CHANGELOG
+
+
+Update and check documentation:
+
+ cd ~yosys
+ make update-manual
+ make manual
+ - sanity check the figures in the appnotes and presentation
+ - if there are any odd things -> investigate
+ - make cosmetic changes to the .tex files if necessary
+
+ cd ~yosys
+ vi README CodingReadme
+ - is the information provided in those file still up to date
+
+
+Then with default config setting:
+
+ cd ~yosys
+ make vgtest
+
+ cd ~yosys
+ ./yosys -p 'proc; show' tests/simple/fiedler-cooley.v
+ ./yosys -p 'proc; opt; show' tests/simple/fiedler-cooley.v
+ ./yosys -p 'synth; show' tests/simple/fiedler-cooley.v
+ ./yosys -p 'synth_xilinx -top up3down5; show' tests/simple/fiedler-cooley.v
+
+ cd ~yosys/examples/cmos
+ bash testbench.sh
+
+ cd ~yosys/examples/basys3
+ bash run.sh
+
+
+Test building plugins with various of the standard passes:
+
+ yosys-config --build test.so equiv_simple.cc
+ - also check the code examples in CodingReadme
+
+
+And if a version of the verific library is currently available:
+
+ cd ~yosys
+ cat frontends/verific/build_amd64.txt
+ - follow instructions
+
+ cd frontends/verific
+ ../../yosys test_navre.ys
+
+
+Finally run all tests with "make config-{clang,gcc,gcc-4.8}":
+
+ cd ~yosys
+ make clean
+ make test
+ make vloghtb
+ make install
+
+ cd ~yosys-bigsim
+ make clean
+ make full
+
+ cd ~vloghammer
+ make purge gen_issues gen_samples
+ make SYN_LIST="yosys" SIM_LIST="icarus yosim verilator" REPORT_FULL=1 world
+ chromium-browser report.html
+
+
+Release:
+
+ - set YOSYS_VER to x.y.z in Makefile
+ - update version string in CHANGELOG
+ git commit -am "Yosys x.y.z"
+
+ - push tag to github
+ - post changelog on github
+ - post short release note on reddit
+
+
+Updating the website:
+
+ cd ~yosys
+ make manual
+ make install
+
+ - update pdf files on the website
+
+ cd ~yosys-web
+ make update_cmd
+ make update_show
+ git commit -am update
+ make push
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..0a61fe65
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,564 @@
+
+CONFIG := clang
+# CONFIG := gcc
+# CONFIG := gcc-4.8
+# CONFIG := emcc
+# CONFIG := mxe
+# CONFIG := msys2
+
+# features (the more the better)
+ENABLE_TCL := 1
+ENABLE_ABC := 1
+ENABLE_PLUGINS := 1
+ENABLE_READLINE := 1
+ENABLE_VERIFIC := 0
+ENABLE_COVER := 1
+ENABLE_LIBYOSYS := 0
+
+# other configuration flags
+ENABLE_GPROF := 0
+ENABLE_NDEBUG := 0
+
+# clang sanitizers
+SANITIZER =
+# SANITIZER = address
+# SANITIZER = memory
+# SANITIZER = undefined
+# SANITIZER = cfi
+
+
+PREFIX ?= /usr/local
+INSTALL_SUDO :=
+
+BINDIR := $(PREFIX)/bin
+LIBDIR := $(PREFIX)/lib
+DATDIR := $(PREFIX)/share/yosys
+
+EXE =
+OBJS =
+GENFILES =
+EXTRA_OBJS =
+EXTRA_TARGETS =
+TARGETS = yosys$(EXE) yosys-config
+
+PRETTY = 1
+SMALL = 0
+
+all: top-all
+
+YOSYS_SRC := $(dir $(firstword $(MAKEFILE_LIST)))
+VPATH := $(YOSYS_SRC)
+
+CXXFLAGS += -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -D_YOSYS_ -fPIC -I$(PREFIX)/include
+LDFLAGS += -L$(LIBDIR)
+LDLIBS = -lstdc++ -lm
+
+PKG_CONFIG = pkg-config
+SED = sed
+BISON = bison
+
+ifeq (Darwin,$(findstring Darwin,$(shell uname)))
+ # add macports/homebrew include and library path to search directories, don't use '-rdynamic' and '-lrt':
+ CXXFLAGS += -I/opt/local/include -I/usr/local/opt/readline/include
+ LDFLAGS += -L/opt/local/lib -L/usr/local/opt/readline/lib
+ # add homebrew's libffi include and library path
+ CXXFLAGS += $(shell PKG_CONFIG_PATH=$$(brew list libffi | grep pkgconfig | xargs dirname) pkg-config --silence-errors --cflags libffi)
+ LDFLAGS += $(shell PKG_CONFIG_PATH=$$(brew list libffi | grep pkgconfig | xargs dirname) pkg-config --silence-errors --libs libffi)
+ # use bison installed by homebrew if available
+ BISON = $(shell (brew list bison | grep -m1 "bin/bison") || echo bison)
+ SED = sed
+else
+ LDFLAGS += -rdynamic
+ LDLIBS += -lrt
+endif
+
+YOSYS_VER := 0.7
+GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
+OBJS = kernel/version_$(GIT_REV).o
+
+# set 'ABCREV = default' to use abc/ as it is
+#
+# Note: If you do ABC development, make sure that 'abc' in this directory
+# 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 = eb6eca6807cc
+ABCPULL = 1
+ABCURL ?= https://bitbucket.org/alanmi/abc
+ABCMKARGS = CC="$(CXX)" CXX="$(CXX)"
+
+# set ABCEXTERNAL = <abc-command> to use an external ABC instance
+# Note: The in-tree ABC (yosys-abc) will not be installed when ABCEXTERNAL is set.
+ABCEXTERNAL ?=
+
+define newline
+
+
+endef
+
+ifneq ($(wildcard Makefile.conf),)
+$(info $(subst $$--$$,$(newline),$(shell sed 's,^,[Makefile.conf] ,; s,$$,$$--$$,;' < Makefile.conf | tr -d '\n' | sed 's,\$$--\$$$$,,')))
+include Makefile.conf
+endif
+
+ifeq ($(CONFIG),clang)
+CXX = clang
+LD = clang++
+CXXFLAGS += -std=c++11 -Os
+
+ifneq ($(SANITIZER),)
+$(info [Clang Sanitizer] $(SANITIZER))
+CXXFLAGS += -g -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=$(SANITIZER)
+LDFLAGS += -g -fsanitize=$(SANITIZER)
+ifeq ($(SANITIZER),address)
+ENABLE_COVER := 0
+endif
+ifeq ($(SANITIZER),memory)
+CXXFLAGS += -fPIE -fsanitize-memory-track-origins
+LDFLAGS += -fPIE -fsanitize-memory-track-origins
+endif
+ifeq ($(SANITIZER),cfi)
+CXXFLAGS += -flto
+LDFLAGS += -flto
+endif
+endif
+
+else ifeq ($(CONFIG),gcc)
+CXX = gcc
+LD = gcc
+CXXFLAGS += -std=c++11 -Os
+
+else ifeq ($(CONFIG),gcc-4.8)
+CXX = gcc-4.8
+LD = gcc-4.8
+CXXFLAGS += -std=c++11 -Os
+
+else ifeq ($(CONFIG),emcc)
+CXX = emcc
+LD = emcc
+CXXFLAGS := -std=c++11 $(filter-out -fPIC -ggdb,$(CXXFLAGS))
+EMCCFLAGS := -Os -Wno-warn-absolute-paths
+EMCCFLAGS += --memory-init-file 0 --embed-file share -s NO_EXIT_RUNTIME=1
+EMCCFLAGS += -s EXPORTED_FUNCTIONS="['_main','_run','_prompt','_errmsg']"
+EMCCFLAGS += -s TOTAL_MEMORY=128*1024*1024
+# https://github.com/kripken/emscripten/blob/master/src/settings.js
+CXXFLAGS += $(EMCCFLAGS)
+LDFLAGS += $(EMCCFLAGS)
+LDLIBS =
+EXE = .js
+
+TARGETS := $(filter-out yosys-config,$(TARGETS))
+EXTRA_TARGETS += yosysjs-$(YOSYS_VER).zip
+
+viz.js:
+ wget -O viz.js.part https://github.com/mdaines/viz.js/releases/download/0.0.3/viz.js
+ mv viz.js.part viz.js
+
+yosysjs-$(YOSYS_VER).zip: yosys.js viz.js misc/yosysjs/*
+ rm -rf yosysjs-$(YOSYS_VER) yosysjs-$(YOSYS_VER).zip
+ mkdir -p yosysjs-$(YOSYS_VER)
+ cp viz.js misc/yosysjs/* yosys.js yosysjs-$(YOSYS_VER)/
+ zip -r yosysjs-$(YOSYS_VER).zip yosysjs-$(YOSYS_VER)
+
+yosys.html: misc/yosys.html
+ $(P) cp misc/yosys.html yosys.html
+
+else ifeq ($(CONFIG),mxe)
+CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc
+LD = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc
+CXXFLAGS += -std=c++11 -Os -D_POSIX_SOURCE
+CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
+LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s
+LDLIBS := $(filter-out -lrt,$(LDLIBS))
+ABCMKARGS += ARCHFLAGS="-DSIZEOF_VOID_P=4 -DSIZEOF_LONG=4 -DSIZEOF_INT=4 -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -x c++ -fpermissive -w"
+ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" ABC_USE_NO_READLINE=1 CC="$(CXX)" CXX="$(CXX)"
+EXE = .exe
+
+else ifeq ($(CONFIG),msys2)
+CXX = i686-w64-mingw32-gcc
+LD = i686-w64-mingw32-gcc
+CXXFLAGS += -std=c++11 -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR
+CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
+LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s
+LDLIBS := $(filter-out -lrt,$(LDLIBS))
+ABCMKARGS += ARCHFLAGS="-DSIZEOF_VOID_P=4 -DSIZEOF_LONG=4 -DSIZEOF_INT=4 -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -x c++ -fpermissive -w"
+ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" ABC_USE_NO_READLINE=0 CC="$(CXX)" CXX="$(CXX)"
+EXE = .exe
+
+else ifneq ($(CONFIG),none)
+$(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, gcc-4.8, emcc, mxe, msys2)
+endif
+
+ifeq ($(ENABLE_LIBYOSYS),1)
+TARGETS += libyosys.so
+endif
+
+ifeq ($(ENABLE_READLINE),1)
+CXXFLAGS += -DYOSYS_ENABLE_READLINE
+LDLIBS += -lreadline
+ifeq ($(CONFIG),mxe)
+LDLIBS += -lpdcurses
+endif
+endif
+
+ifeq ($(ENABLE_PLUGINS),1)
+CXXFLAGS += -DYOSYS_ENABLE_PLUGINS $(shell $(PKG_CONFIG) --silence-errors --cflags libffi)
+LDLIBS += $(shell $(PKG_CONFIG) --silence-errors --libs libffi || echo -lffi) -ldl
+endif
+
+ifeq ($(ENABLE_TCL),1)
+TCL_VERSION ?= tcl$(shell bash -c "tclsh <(echo 'puts [info tclversion]')")
+TCL_INCLUDE ?= /usr/include/$(TCL_VERSION)
+CXXFLAGS += -I$(TCL_INCLUDE) -DYOSYS_ENABLE_TCL
+LDLIBS += -l$(TCL_VERSION)
+endif
+
+ifeq ($(ENABLE_GPROF),1)
+CXXFLAGS += -pg
+LDFLAGS += -pg
+endif
+
+ifeq ($(ENABLE_NDEBUG),1)
+CXXFLAGS := -O3 -DNDEBUG $(filter-out -Os,$(CXXFLAGS))
+endif
+
+ifeq ($(ENABLE_ABC),1)
+CXXFLAGS += -DYOSYS_ENABLE_ABC
+ifeq ($(ABCEXTERNAL),)
+TARGETS += yosys-abc$(EXE)
+endif
+endif
+
+ifeq ($(ENABLE_VERIFIC),1)
+VERIFIC_DIR ?= /usr/local/src/verific_lib_eval
+VERIFIC_COMPONENTS ?= verilog vhdl database util containers sdf
+CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC
+LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS))
+endif
+
+ifeq ($(ENABLE_COVER),1)
+CXXFLAGS += -DYOSYS_ENABLE_COVER
+endif
+
+define add_share_file
+EXTRA_TARGETS += $(subst //,/,$(1)/$(notdir $(2)))
+$(subst //,/,$(1)/$(notdir $(2))): $(2)
+ $$(P) mkdir -p $(1)
+ $$(Q) cp "$(YOSYS_SRC)"/$(2) $(subst //,/,$(1)/$(notdir $(2)))
+endef
+
+define add_gen_share_file
+EXTRA_TARGETS += $(subst //,/,$(1)/$(notdir $(2)))
+$(subst //,/,$(1)/$(notdir $(2))): $(2)
+ $$(P) mkdir -p $(1)
+ $$(Q) cp $(2) $(subst //,/,$(1)/$(notdir $(2)))
+endef
+
+define add_include_file
+$(eval $(call add_share_file,$(dir share/include/$(1)),$(1)))
+endef
+
+ifeq ($(PRETTY), 1)
+P_STATUS = 0
+P_OFFSET = 0
+P_UPDATE = $(eval P_STATUS=$(shell echo $(OBJS) yosys$(EXE) | gawk 'BEGIN { RS = " "; I = $(P_STATUS)+0; } $$1 == "$@" && NR > I { I = NR; } END { print I; }'))
+P_SHOW = [$(shell gawk "BEGIN { N=$(words $(OBJS) yosys$(EXE)); printf \"%3d\", $(P_OFFSET)+90*$(P_STATUS)/N; exit; }")%]
+P = @echo "$(if $(findstring $@,$(TARGETS) $(EXTRA_TARGETS)),$(eval P_OFFSET = 10))$(call P_UPDATE)$(call P_SHOW) Building $@";
+Q = @
+S = -s
+else
+P_SHOW = ->
+P =
+Q =
+S =
+endif
+
+$(eval $(call add_include_file,kernel/yosys.h))
+$(eval $(call add_include_file,kernel/hashlib.h))
+$(eval $(call add_include_file,kernel/log.h))
+$(eval $(call add_include_file,kernel/rtlil.h))
+$(eval $(call add_include_file,kernel/register.h))
+$(eval $(call add_include_file,kernel/celltypes.h))
+$(eval $(call add_include_file,kernel/celledges.h))
+$(eval $(call add_include_file,kernel/consteval.h))
+$(eval $(call add_include_file,kernel/sigtools.h))
+$(eval $(call add_include_file,kernel/modtools.h))
+$(eval $(call add_include_file,kernel/macc.h))
+$(eval $(call add_include_file,kernel/utils.h))
+$(eval $(call add_include_file,kernel/satgen.h))
+$(eval $(call add_include_file,libs/ezsat/ezsat.h))
+$(eval $(call add_include_file,libs/ezsat/ezminisat.h))
+$(eval $(call add_include_file,libs/sha1/sha1.h))
+$(eval $(call add_include_file,passes/fsm/fsmdata.h))
+$(eval $(call add_include_file,frontends/ast/ast.h))
+$(eval $(call add_include_file,backends/ilang/ilang_backend.h))
+
+OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o
+OBJS += kernel/cellaigs.o kernel/celledges.o
+
+kernel/log.o: CXXFLAGS += -DYOSYS_SRC='"$(YOSYS_SRC)"'
+kernel/yosys.o: CXXFLAGS += -DYOSYS_DATDIR='"$(DATDIR)"'
+
+OBJS += libs/bigint/BigIntegerAlgorithms.o libs/bigint/BigInteger.o libs/bigint/BigIntegerUtils.o
+OBJS += libs/bigint/BigUnsigned.o libs/bigint/BigUnsignedInABase.o
+
+OBJS += libs/sha1/sha1.o
+
+ifneq ($(SMALL),1)
+
+OBJS += libs/subcircuit/subcircuit.o
+
+OBJS += libs/ezsat/ezsat.o
+OBJS += libs/ezsat/ezminisat.o
+
+OBJS += libs/minisat/Options.o
+OBJS += libs/minisat/SimpSolver.o
+OBJS += libs/minisat/Solver.o
+OBJS += libs/minisat/System.o
+
+include $(YOSYS_SRC)/frontends/*/Makefile.inc
+include $(YOSYS_SRC)/passes/*/Makefile.inc
+include $(YOSYS_SRC)/backends/*/Makefile.inc
+include $(YOSYS_SRC)/techlibs/*/Makefile.inc
+
+else
+
+include frontends/verilog/Makefile.inc
+include frontends/ilang/Makefile.inc
+include frontends/ast/Makefile.inc
+include frontends/blif/Makefile.inc
+
+OBJS += passes/hierarchy/hierarchy.o
+OBJS += passes/cmds/select.o
+OBJS += passes/cmds/show.o
+OBJS += passes/cmds/stat.o
+OBJS += passes/cmds/cover.o
+OBJS += passes/cmds/design.o
+OBJS += passes/cmds/plugin.o
+
+include passes/proc/Makefile.inc
+include passes/opt/Makefile.inc
+include passes/techmap/Makefile.inc
+
+include backends/verilog/Makefile.inc
+include backends/ilang/Makefile.inc
+
+include techlibs/common/Makefile.inc
+
+endif
+
+top-all: $(TARGETS) $(EXTRA_TARGETS)
+ @echo ""
+ @echo " Build successful."
+ @echo ""
+
+ifeq ($(CONFIG),emcc)
+yosys.js: $(filter-out yosysjs-$(YOSYS_VER).zip,$(EXTRA_TARGETS))
+endif
+
+yosys$(EXE): $(OBJS)
+ $(P) $(LD) -o yosys$(EXE) $(LDFLAGS) $(OBJS) $(LDLIBS)
+
+libyosys.so: $(filter-out kernel/driver.o,$(OBJS))
+ $(P) $(LD) -o libyosys.so -shared -Wl,-soname,libyosys.so $(LDFLAGS) $^ $(LDLIBS)
+
+%.o: %.cc
+ $(Q) mkdir -p $(dir $@)
+ $(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $<
+
+%.o: %.cpp
+ $(Q) mkdir -p $(dir $@)
+ $(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $<
+
+YOSYS_VER_STR := Yosys $(YOSYS_VER) (git sha1 $(GIT_REV), $(notdir $(CXX)) $(shell \
+ $(CXX) --version | tr ' ()' '\n' | grep '^[0-9]' | head -n1) $(filter -f% -m% -O% -DNDEBUG,$(CXXFLAGS)))
+
+kernel/version_$(GIT_REV).cc: $(YOSYS_SRC)/Makefile
+ $(P) rm -f kernel/version_*.o kernel/version_*.d kernel/version_*.cc
+ $(Q) mkdir -p kernel && echo "namespace Yosys { extern const char *yosys_version_str; const char *yosys_version_str=\"$(YOSYS_VER_STR)\"; }" > kernel/version_$(GIT_REV).cc
+
+yosys-config: misc/yosys-config.in
+ $(P) $(SED) -e 's#@CXXFLAGS@#$(subst -I. -I"$(YOSYS_SRC)",-I"$(DATDIR)/include",$(CXXFLAGS))#;' \
+ -e 's#@CXX@#$(CXX)#;' -e 's#@LDFLAGS@#$(LDFLAGS)#;' -e 's#@LDLIBS@#$(LDLIBS)#;' \
+ -e 's#@BINDIR@#$(BINDIR)#;' -e 's#@DATDIR@#$(DATDIR)#;' < $< > yosys-config
+ $(Q) chmod +x yosys-config
+
+abc/abc-$(ABCREV)$(EXE):
+ $(P)
+ifneq ($(ABCREV),default)
+ $(Q) if ( cd abc 2> /dev/null && hg identify; ) | grep -q +; then \
+ echo 'REEBE: NOP pbagnvaf ybpny zbqvsvpngvbaf! Frg NOPERI=qrsnhyg va Lbflf Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; false; \
+ fi
+ $(Q) if test "`cd abc 2> /dev/null && hg identify | cut -f1 -d' '`" != "$(ABCREV)"; then \
+ test $(ABCPULL) -ne 0 || { echo 'REEBE: NOP abg hc gb qngr naq NOPCHYY frg gb 0 va Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; exit 1; }; \
+ echo "Pulling ABC from $(ABCURL):"; set -x; \
+ test -d abc || hg clone $(ABCURL) abc; \
+ cd abc && $(MAKE) DEP= clean && hg pull && hg update -r $(ABCREV); \
+ fi
+endif
+ $(Q) rm -f abc/abc-[0-9a-f]*
+ $(Q) cd abc && $(MAKE) $(S) $(ABCMKARGS) PROG="abc-$(ABCREV)$(EXE)" MSG_PREFIX="$(eval P_OFFSET = 5)$(call P_SHOW)$(eval P_OFFSET = 10) ABC: "
+
+ifeq ($(ABCREV),default)
+.PHONY: abc/abc-$(ABCREV)$(EXE)
+endif
+
+yosys-abc$(EXE): abc/abc-$(ABCREV)$(EXE)
+ $(P) cp abc/abc-$(ABCREV)$(EXE) yosys-abc$(EXE)
+
+ifneq ($(SEED),)
+SEEDOPT="-S $(SEED)"
+else
+SEEDOPT=""
+endif
+
+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/share && bash run-test.sh $(SEEDOPT)
+ +cd tests/fsm && bash run-test.sh $(SEEDOPT)
+ +cd tests/techmap && bash run-test.sh
+ +cd tests/memories && bash run-test.sh $(SEEDOPT)
+ +cd tests/bram && bash run-test.sh $(SEEDOPT)
+ +cd tests/various && bash run-test.sh
+ +cd tests/sat && bash run-test.sh
+ @echo ""
+ @echo " Passed \"make test\"."
+ @echo ""
+
+VALGRIND ?= valgrind --error-exitcode=1 --leak-check=full --show-reachable=yes --errors-for-leak-kinds=all
+
+vgtest: $(TARGETS) $(EXTRA_TARGETS)
+ $(VALGRIND) ./yosys -p 'setattr -mod -unset top; synth' $$( ls tests/simple/*.v | grep -v repwhile.v )
+ @echo ""
+ @echo " Passed \"make vgtest\"."
+ @echo ""
+
+vloghtb: $(TARGETS) $(EXTRA_TARGETS)
+ +cd tests/vloghtb && bash run-test.sh
+ @echo ""
+ @echo " Passed \"make vloghtb\"."
+ @echo ""
+
+install: $(TARGETS) $(EXTRA_TARGETS)
+ $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR)
+ $(INSTALL_SUDO) install $(TARGETS) $(DESTDIR)$(BINDIR)
+ $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(DATDIR)
+ $(INSTALL_SUDO) cp -r share/. $(DESTDIR)$(DATDIR)/.
+ifeq ($(ENABLE_LIBYOSYS),1)
+ $(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR)
+ $(INSTALL_SUDO) ldconfig
+endif
+
+uninstall:
+ $(INSTALL_SUDO) rm -vf $(addprefix $(DESTDIR)$(BINDIR),$(notdir $(TARGETS)))
+ $(INSTALL_SUDO) rm -rvf $(DESTDIR)$(DATDIR)
+ifeq ($(ENABLE_LIBYOSYS),1)
+ $(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.so
+endif
+
+update-manual: $(TARGETS) $(EXTRA_TARGETS)
+ cd manual && ../yosys -p 'help -write-tex-command-reference-manual'
+
+manual: $(TARGETS) $(EXTRA_TARGETS)
+ cd manual && bash appnotes.sh
+ cd manual && bash presentation.sh
+ cd manual && bash manual.sh
+
+clean:
+ rm -rf share
+ if test -d manual; then cd manual && sh clean.sh; fi
+ rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS)
+ rm -f kernel/version_*.o kernel/version_*.cc abc/abc-[0-9a-f]*
+ rm -f libs/*/*.d frontends/*/*.d passes/*/*.d backends/*/*.d kernel/*.d techlibs/*/*.d
+
+clean-abc:
+ $(MAKE) -C abc DEP= clean
+ rm -f yosys-abc$(EXE) abc/abc-[0-9a-f]*
+
+mrproper: clean
+ git clean -xdf
+
+qtcreator:
+ { for file in $(basename $(OBJS)); do \
+ for prefix in cc y l; do if [ -f $${file}.$${prefix} ]; then echo $$file.$${prefix}; fi; done \
+ done; find backends frontends kernel libs passes -type f \( -name '*.h' -o -name '*.hh' \); } > qtcreator.files
+ { echo .; find backends frontends kernel libs passes -type f \( -name '*.h' -o -name '*.hh' \) -printf '%h\n' | sort -u; } > qtcreator.includes
+ touch qtcreator.config qtcreator.creator
+
+vcxsrc: $(GENFILES) $(EXTRA_TARGETS)
+ rm -rf yosys-win32-vcxsrc-$(YOSYS_VER){,.zip}
+ set -e; for f in `ls $(filter %.cc %.cpp,$(GENFILES)) $(addsuffix .cc,$(basename $(OBJS))) $(addsuffix .cpp,$(basename $(OBJS))) 2> /dev/null`; do \
+ echo "Analyse: $$f" >&2; cpp -std=c++11 -MM -I. -D_YOSYS_ $$f; done | sed 's,.*:,,; s,//*,/,g; s,/[^/]*/\.\./,/,g; y, \\,\n\n,;' | grep '^[^/]' | sort -u | grep -v kernel/version_ > srcfiles.txt
+ bash misc/create_vcxsrc.sh yosys-win32-vcxsrc $(YOSYS_VER) $(GIT_REV)
+ echo "namespace Yosys { extern const char *yosys_version_str; const char *yosys_version_str=\"Yosys (Version Information Unavailable)\"; }" > kernel/version.cc
+ zip yosys-win32-vcxsrc-$(YOSYS_VER)/genfiles.zip $(GENFILES) kernel/version.cc
+ zip -r yosys-win32-vcxsrc-$(YOSYS_VER).zip yosys-win32-vcxsrc-$(YOSYS_VER)/
+ rm -f srcfiles.txt kernel/version.cc
+
+ifeq ($(CONFIG),mxe)
+mxebin: $(TARGETS) $(EXTRA_TARGETS)
+ rm -rf yosys-win32-mxebin-$(YOSYS_VER){,.zip}
+ mkdir -p yosys-win32-mxebin-$(YOSYS_VER)
+ cp -r yosys.exe share/ yosys-win32-mxebin-$(YOSYS_VER)/
+ifeq ($(ENABLE_ABC),1)
+ cp -r yosys-abc.exe abc/lib/x86/pthreadVC2.dll yosys-win32-mxebin-$(YOSYS_VER)/
+endif
+ echo -en 'This is Yosys $(YOSYS_VER) for Win32.\r\n' > yosys-win32-mxebin-$(YOSYS_VER)/readme.txt
+ echo -en 'Documentation at http://www.clifford.at/yosys/.\r\n' >> yosys-win32-mxebin-$(YOSYS_VER)/readme.txt
+ zip -r yosys-win32-mxebin-$(YOSYS_VER).zip yosys-win32-mxebin-$(YOSYS_VER)/
+endif
+
+config-clean: clean
+ rm -f Makefile.conf
+
+config-clang: clean
+ echo 'CONFIG := clang' > Makefile.conf
+
+config-gcc: clean
+ echo 'CONFIG := gcc' > Makefile.conf
+
+config-gcc-4.8: clean
+ echo 'CONFIG := gcc-4.8' > Makefile.conf
+
+config-emcc: clean
+ echo 'CONFIG := emcc' > Makefile.conf
+ echo 'ENABLE_TCL := 0' >> Makefile.conf
+ echo 'ENABLE_ABC := 0' >> Makefile.conf
+ echo 'ENABLE_PLUGINS := 0' >> Makefile.conf
+ echo 'ENABLE_READLINE := 0' >> Makefile.conf
+
+config-mxe: clean
+ echo 'CONFIG := mxe' > Makefile.conf
+ echo 'ENABLE_TCL := 0' >> Makefile.conf
+ echo 'ENABLE_PLUGINS := 0' >> Makefile.conf
+ echo 'ENABLE_READLINE := 0' >> Makefile.conf
+
+config-msys2: clean
+ echo 'CONFIG := msys2' > Makefile.conf
+
+config-gprof: clean
+ echo 'CONFIG := gcc' > Makefile.conf
+ echo 'ENABLE_GPROF := 1' >> Makefile.conf
+
+config-sudo:
+ echo "INSTALL_SUDO := sudo" >> Makefile.conf
+
+echo-yosys-ver:
+ @echo "$(YOSYS_VER)"
+
+echo-git-rev:
+ @echo "$(GIT_REV)"
+
+-include libs/*/*.d
+-include frontends/*/*.d
+-include passes/*/*.d
+-include backends/*/*.d
+-include kernel/*.d
+-include techlibs/*/*.d
+
+.PHONY: all top-all abc test install install-abc manual clean mrproper qtcreator
+.PHONY: config-clean config-clang config-gcc config-gcc-4.8 config-gprof config-sudo
+
diff --git a/README b/README
new file mode 100644
index 00000000..8e43d444
--- /dev/null
+++ b/README
@@ -0,0 +1,451 @@
+
+ /-----------------------------------------------------------------------------\
+ | |
+ | yosys -- Yosys Open SYnthesis Suite |
+ | |
+ | Copyright (C) 2012 - 2016 Clifford Wolf <clifford@clifford.at> |
+ | |
+ | Permission to use, copy, modify, and/or distribute this software for any |
+ | purpose with or without fee is hereby granted, provided that the above |
+ | copyright notice and this permission notice appear in all copies. |
+ | |
+ | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
+ | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
+ | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
+ | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
+ | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
+ | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
+ | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
+ | |
+ \-----------------------------------------------------------------------------/
+
+
+yosys -- Yosys Open SYnthesis Suite
+===================================
+
+This is a framework for RTL synthesis tools. It currently has
+extensive Verilog-2005 support and provides a basic set of
+synthesis algorithms for various application domains.
+
+Yosys can be adapted to perform any synthesis job by combining
+the existing passes (algorithms) using synthesis scripts and
+adding additional passes as needed by extending the yosys C++
+code base.
+
+Yosys is free software licensed under the ISC license (a GPL
+compatible license that is similar in terms to the MIT license
+or the 2-clause BSD license).
+
+
+Web Site
+========
+
+More information and documentation can be found on the Yosys web site:
+
+ http://www.clifford.at/yosys/
+
+
+Getting Started
+===============
+
+You need a C++ compiler with C++11 support (up-to-date CLANG or GCC is
+recommended) and some standard tools such as GNU Flex, GNU Bison, and GNU Make.
+TCL, readline and libffi are optional (see ENABLE_* settings in Makefile).
+Xdot (graphviz) is used by the "show" command in yosys to display schematics.
+For example on Ubuntu Linux 16.04 LTS the following commands will install all
+prerequisites for building yosys:
+
+ $ sudo apt-get install build-essential clang bison flex \
+ libreadline-dev gawk tcl-dev libffi-dev git mercurial \
+ graphviz xdot pkg-config python3
+
+There are also pre-compiled Yosys binary packages for Ubuntu and Win32 as well
+as a source distribution for Visual Studio. Visit the Yosys download page for
+more information:
+
+ http://www.clifford.at/yosys/download.html
+
+To configure the build system to use a specific compiler, use one of
+
+ $ make config-clang
+ $ make config-gcc
+
+For other compilers and build configurations it might be
+necessary to make some changes to the config section of the
+Makefile.
+
+ $ vi Makefile ..or..
+ $ vi Makefile.conf
+
+To build Yosys simply type 'make' in this directory.
+
+ $ make
+ $ make test
+ $ sudo make install
+
+Note that this also downloads, builds and installs ABC (using yosys-abc
+as executable name).
+
+Yosys can be used with the interactive command shell, with
+synthesis scripts or with command line arguments. Let's perform
+a simple synthesis job using the interactive command shell:
+
+ $ ./yosys
+ yosys>
+
+the command "help" can be used to print a list of all available
+commands and "help <command>" to print details on the specified command:
+
+ yosys> help help
+
+reading the design using the Verilog frontend:
+
+ yosys> read_verilog tests/simple/fiedler-cooley.v
+
+writing the design to the console in yosys's internal format:
+
+ yosys> write_ilang
+
+elaborate design hierarchy:
+
+ yosys> hierarchy
+
+convert processes ("always" blocks) to netlist elements and perform
+some simple optimizations:
+
+ yosys> proc; opt
+
+display design netlist using xdot:
+
+ yosys> show
+
+the same thing using 'gv' as postscript viewer:
+
+ yosys> show -format ps -viewer gv
+
+translating netlist to gate logic and perform some simple optimizations:
+
+ yosys> techmap; opt
+
+write design netlist to a new Verilog file:
+
+ yosys> write_verilog synth.v
+
+a similar synthesis can be performed using yosys command line options only:
+
+ $ ./yosys -o synth.v -p hierarchy -p proc -p opt \
+ -p techmap -p opt tests/simple/fiedler-cooley.v
+
+or using a simple synthesis script:
+
+ $ cat synth.ys
+ read_verilog tests/simple/fiedler-cooley.v
+ hierarchy; proc; opt; techmap; opt
+ write_verilog synth.v
+
+ $ ./yosys synth.ys
+
+It is also possible to only have the synthesis commands but not the read/write
+commands in the synthesis script:
+
+ $ cat synth.ys
+ hierarchy; proc; opt; techmap; opt
+
+ $ ./yosys -o synth.v tests/simple/fiedler-cooley.v synth.ys
+
+The following very basic synthesis script should work well with all designs:
+
+ # check design hierarchy
+ hierarchy
+
+ # translate processes (always blocks)
+ proc; opt
+
+ # detect and optimize FSM encodings
+ fsm; opt
+
+ # implement memories (arrays)
+ memory; opt
+
+ # convert to gate logic
+ techmap; opt
+
+If ABC is enabled in the Yosys build configuration and a cell library is given
+in the liberty file mycells.lib, the following synthesis script will synthesize
+for the given cell library:
+
+ # the high-level stuff
+ hierarchy; proc; fsm; opt; memory; opt
+
+ # mapping to internal cell library
+ techmap; opt
+
+ # mapping flip-flops to mycells.lib
+ dfflibmap -liberty mycells.lib
+
+ # mapping logic to mycells.lib
+ abc -liberty mycells.lib
+
+ # cleanup
+ clean
+
+If you do not have a liberty file but want to test this synthesis script,
+you can use the file examples/cmos/cmos_cells.lib from the yosys sources.
+
+Liberty file downloads for and information about free and open ASIC standard
+cell libraries can be found here:
+
+ http://www.vlsitechnology.org/html/libraries.html
+ http://www.vlsitechnology.org/synopsys/vsclib013.lib
+
+The command "synth" provides a good default synthesis script (see "help synth").
+If possible a synthesis script should borrow from "synth". For example:
+
+ # the high-level stuff
+ hierarchy
+ synth -run coarse
+
+ # mapping to internal cells
+ techmap; opt -fast
+ dfflibmap -liberty mycells.lib
+ abc -liberty mycells.lib
+ clean
+
+Yosys is under construction. A more detailed documentation will follow.
+
+
+Unsupported Verilog-2005 Features
+=================================
+
+The following Verilog-2005 features are not supported by
+yosys and there are currently no plans to add support
+for them:
+
+- Non-synthesizable language features as defined in
+ IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002
+
+- The "tri", "triand", "trior", "wand" and "wor" net types
+
+- The "config" keyword and library map files
+
+- The "disable", "primitive" and "specify" statements
+
+- Latched logic (is synthesized as logic with feedback loops)
+
+
+Verilog Attributes and non-standard features
+============================================
+
+- The 'full_case' attribute on case statements is supported
+ (also the non-standard "// synopsys full_case" directive)
+
+- The 'parallel_case' attribute on case statements is supported
+ (also the non-standard "// synopsys parallel_case" directive)
+
+- The "// synopsys translate_off" and "// synopsys translate_on"
+ directives are also supported (but the use of `ifdef .. `endif
+ is strongly recommended instead).
+
+- The "nomem2reg" attribute on modules or arrays prohibits the
+ automatic early conversion of arrays to separate registers. This
+ is potentially dangerous. Usually the front-end has good reasons
+ for converting an array to a list of registers. Prohibiting this
+ step will likely result in incorrect synthesis results.
+
+- The "mem2reg" attribute on modules or arrays forces the early
+ conversion of arrays to separate registers.
+
+- The "nomeminit" attribute on modules or arrays prohibits the
+ creation of initialized memories. This effectively puts "mem2reg"
+ on all memories that are written to in an "initial" block and
+ are not ROMs.
+
+- The "nolatches" attribute on modules or always-blocks
+ prohibits the generation of logic-loops for latches. Instead
+ all not explicitly assigned values default to x-bits. This does
+ not affect clocked storage elements such as flip-flops.
+
+- The "nosync" attribute on registers prohibits the generation of a
+ storage element. The register itself will always have all bits set
+ to 'x' (undefined). The variable may only be used as blocking assigned
+ temporary variable within an always block. This is mostly used internally
+ by yosys to synthesize Verilog functions and access arrays.
+
+- The "onehot" attribute on wires mark them as onehot state register. This
+ is used for example for memory port sharing and set by the fsm_map pass.
+
+- The "blackbox" attribute on modules is used to mark empty stub modules
+ that have the same ports as the real thing but do not contain information
+ on the internal configuration. This modules are only used by the synthesis
+ passes to identify input and output ports of cells. The Verilog backend
+ also does not output blackbox modules on default.
+
+- The "keep" attribute on cells and wires is used to mark objects that should
+ never be removed by the optimizer. This is used for example for cells that
+ have hidden connections that are not part of the netlist, such as IO pads.
+ Setting the "keep" attribute on a module has the same effect as setting it
+ on all instances of the module.
+
+- The "keep_hierarchy" attribute on cells and modules keeps the "flatten"
+ command from flattening the indicated cells and modules.
+
+- The "init" attribute on wires is set by the frontend when a register is
+ initialized "FPGA-style" with 'reg foo = val'. It can be used during synthesis
+ to add the necessary reset logic.
+
+- The "top" attribute on a module marks this module as the top of the
+ design hierarchy. The "hierarchy" command sets this attribute when called
+ with "-top". Other commands, such as "flatten" and various backends
+ use this attribute to determine the top module.
+
+- The "src" attribute is set on cells and wires created by to the string
+ "<hdl-file-name>:<line-number>" by the HDL front-end and is then carried
+ through the synthesis. When entities are combined, a new |-separated
+ string is created that contains all the string from the original entities.
+
+- In addition to the (* ... *) attribute syntax, yosys supports
+ the non-standard {* ... *} attribute syntax to set default attributes
+ for everything that comes after the {* ... *} statement. (Reset
+ by adding an empty {* *} statement.)
+
+- In module parameter and port declarations, and cell port and parameter
+ lists, a trailing comma is ignored. This simplifies writing verilog code
+ generators a bit in some cases.
+
+- Modules can be declared with "module mod_name(...);" (with three dots
+ instead of a list of module ports). With this syntax it is sufficient
+ to simply declare a module port as 'input' or 'output' in the module
+ body.
+
+- When defining a macro with `define, all text between triple double quotes
+ is interpreted as macro body, even if it contains unescaped newlines. The
+ tipple double quotes are removed from the macro body. For example:
+
+ `define MY_MACRO(a, b) """
+ assign a = 23;
+ assign b = 42;
+ """
+
+- The attribute "via_celltype" can be used to implement a Verilog task or
+ function by instantiating the specified cell type. The value is the name
+ of the cell type to use. For functions the name of the output port can
+ be specified by appending it to the cell type separated by a whitespace.
+ The body of the task or function is unused in this case and can be used
+ to specify a behavioral model of the cell type for simulation. For example:
+
+ module my_add3(A, B, C, Y);
+ parameter WIDTH = 8;
+ input [WIDTH-1:0] A, B, C;
+ output [WIDTH-1:0] Y;
+ ...
+ endmodule
+
+ module top;
+ ...
+ (* via_celltype = "my_add3 Y" *)
+ (* via_celltype_defparam_WIDTH = 32 *)
+ function [31:0] add3;
+ input [31:0] A, B, C;
+ begin
+ add3 = A + B + C;
+ end
+ endfunction
+ ...
+ endmodule
+
+- A limited subset of DPI-C functions is supported. The plugin mechanism
+ (see "help plugin") can be used to load .so files with implementations
+ of DPI-C routines. As a non-standard extension it is possible to specify
+ a plugin alias using the "<alias>:" syntax. for example:
+
+ module dpitest;
+ import "DPI-C" function foo:round = real my_round (real);
+ parameter real r = my_round(12.345);
+ endmodule
+
+ $ yosys -p 'plugin -a foo -i /lib/libm.so; read_verilog dpitest.v'
+
+- Sized constants (the syntax <size>'s?[bodh]<value>) support constant
+ expressions as <size>. If the expression is not a simple identifier, it
+ must be put in parentheses. Examples: WIDTH'd42, (4+2)'b101010
+
+- The system tasks $finish and $display are supported in initial blocks
+ in an unconditional context (only if/case statements on parameters
+ and constant values). The intended use for this is synthesis-time DRC.
+
+
+Non-standard or SystemVerilog features for formal verification
+==============================================================
+
+- Support for "assert", "assume", and "restrict" is enabled when
+ read_verilog is called with -formal.
+
+- The system task $initstate evaluates to 1 in the initial state and
+ to 0 otherwise.
+
+- The system task $anyconst evaluates to any constant value.
+
+- The system task $anyseq evaluates to any value, possibly a different
+ value in each cycle.
+
+- The SystemVerilog tasks $past, $stable, $rose and $fell are supported
+ in any clocked block.
+
+- The syntax @($global_clock) can be used to create FFs that have no
+ explicit clock input ($ff cells).
+
+
+Supported features from SystemVerilog
+=====================================
+
+When read_verilog is called with -sv, it accepts some language features
+from SystemVerilog:
+
+- The "assert" statement from SystemVerilog is supported in its most basic
+ form. In module context: "assert property (<expression>);" and within an
+ always block: "assert(<expression>);". It is transformed to a $assert cell.
+
+- The "assume" and "restrict" statements from SystemVerilog are also
+ supported. The same limitations as with the "assert" statement apply.
+
+- The keywords "always_comb", "always_ff" and "always_latch", "logic" and
+ "bit" are supported.
+
+- SystemVerilog packages are supported. Once a SystemVerilog file is read
+ into a design with "read_verilog", all its packages are available to
+ SystemVerilog files being read into the same design afterwards.
+
+
+Building the documentation
+==========================
+
+Note that there is no need to build the manual if you just want to read it.
+Simply download the PDF from http://www.clifford.at/yosys/documentation.html
+instead.
+
+On Ubuntu, texlive needs these packages to be able to build the manual:
+
+ sudo apt-get install texlive-binaries
+ sudo apt-get install texlive-science # install algorithm2e.sty
+ sudo apt-get install texlive-bibtex-extra # gets multibib.sty
+ sudo apt-get install texlive-fonts-extra # gets skull.sty and dsfont.sty
+ sudo apt-get install texlive-publishers # IEEEtran.cls
+
+Also the non-free font luximono should be installed, there is unfortunately
+no Ubuntu package for this so it should be installed separately using
+`getnonfreefonts`:
+
+ wget https://tug.org/fonts/getnonfreefonts/install-getnonfreefonts
+ sudo texlua install-getnonfreefonts # will install to /usr/local by default, can be changed by editing BINDIR at MANDIR at the top of the script
+ getnonfreefonts luximono # installs to /home/user/texmf
+
+Then execute, from the root of the repository:
+
+ make manual
+
+Notes:
+
+- To run `make manual` you need to have installed yosys with `make install`,
+ otherwise it will fail on finding `kernel/yosys.h` while building
+ `PRESENTATION_Prog`.
+
diff --git a/backends/blif/Makefile.inc b/backends/blif/Makefile.inc
new file mode 100644
index 00000000..517dabaf
--- /dev/null
+++ b/backends/blif/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += backends/blif/blif.o
+
diff --git a/backends/blif/blif.cc b/backends/blif/blif.cc
new file mode 100644
index 00000000..d9d0cc17
--- /dev/null
+++ b/backends/blif/blif.cc
@@ -0,0 +1,632 @@
+/*
+ * 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.
+ *
+ */
+
+// [[CITE]] Berkeley Logic Interchange Format (BLIF)
+// University of California. Berkeley. July 28, 1992
+// http://www.ece.cmu.edu/~ee760/760docs/blif.pdf
+
+#include "kernel/rtlil.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
+#include "kernel/log.h"
+#include <string>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct BlifDumperConfig
+{
+ bool icells_mode;
+ bool conn_mode;
+ bool impltf_mode;
+ bool gates_mode;
+ bool cname_mode;
+ bool param_mode;
+ bool attr_mode;
+ bool blackbox_mode;
+ bool noalias_mode;
+
+ std::string buf_type, buf_in, buf_out;
+ std::map<RTLIL::IdString, std::pair<RTLIL::IdString, RTLIL::IdString>> unbuf_types;
+ std::string true_type, true_out, false_type, false_out, undef_type, undef_out;
+
+ BlifDumperConfig() : icells_mode(false), conn_mode(false), impltf_mode(false), gates_mode(false),
+ cname_mode(false), param_mode(false), attr_mode(false), blackbox_mode(false), noalias_mode(false) { }
+};
+
+struct BlifDumper
+{
+ std::ostream &f;
+ RTLIL::Module *module;
+ RTLIL::Design *design;
+ BlifDumperConfig *config;
+ CellTypes ct;
+
+ SigMap sigmap;
+ dict<SigBit, int> init_bits;
+
+ BlifDumper(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, BlifDumperConfig *config) :
+ f(f), module(module), design(design), config(config), ct(design), sigmap(module)
+ {
+ for (Wire *wire : module->wires())
+ if (wire->attributes.count("\\init")) {
+ SigSpec initsig = sigmap(wire);
+ Const initval = wire->attributes.at("\\init");
+ for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
+ switch (initval[i]) {
+ case State::S0:
+ init_bits[initsig[i]] = 0;
+ break;
+ case State::S1:
+ init_bits[initsig[i]] = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ vector<shared_str> cstr_buf;
+ pool<SigBit> cstr_bits_seen;
+
+ const char *cstr(RTLIL::IdString id)
+ {
+ std::string str = RTLIL::unescape_id(id);
+ for (size_t i = 0; i < str.size(); i++)
+ if (str[i] == '#' || str[i] == '=' || str[i] == '<' || str[i] == '>')
+ str[i] = '?';
+ cstr_buf.push_back(str);
+ return cstr_buf.back().c_str();
+ }
+
+ const char *cstr(RTLIL::SigBit sig)
+ {
+ cstr_bits_seen.insert(sig);
+
+ if (sig.wire == NULL) {
+ if (sig == RTLIL::State::S0) return config->false_type == "-" || config->false_type == "+" ? config->false_out.c_str() : "$false";
+ if (sig == RTLIL::State::S1) return config->true_type == "-" || config->true_type == "+" ? config->true_out.c_str() : "$true";
+ return config->undef_type == "-" || config->undef_type == "+" ? config->undef_out.c_str() : "$undef";
+ }
+
+ std::string str = RTLIL::unescape_id(sig.wire->name);
+ for (size_t i = 0; i < str.size(); i++)
+ if (str[i] == '#' || str[i] == '=' || str[i] == '<' || str[i] == '>')
+ str[i] = '?';
+
+ if (sig.wire->width != 1)
+ str += stringf("[%d]", sig.offset);
+
+ cstr_buf.push_back(str);
+ return cstr_buf.back().c_str();
+ }
+
+ const char *cstr_init(RTLIL::SigBit sig)
+ {
+ sigmap.apply(sig);
+
+ if (init_bits.count(sig) == 0)
+ return " 2";
+
+ string str = stringf(" %d", init_bits.at(sig));
+
+ cstr_buf.push_back(str);
+ return cstr_buf.back().c_str();
+ }
+
+ const char *subckt_or_gate(std::string cell_type)
+ {
+ if (!config->gates_mode)
+ return "subckt";
+ if (!design->modules_.count(RTLIL::escape_id(cell_type)))
+ return "gate";
+ if (design->modules_.at(RTLIL::escape_id(cell_type))->get_bool_attribute("\\blackbox"))
+ return "gate";
+ return "subckt";
+ }
+
+ void dump_params(const char *command, dict<IdString, Const> &params)
+ {
+ for (auto &param : params) {
+ f << stringf("%s %s ", command, RTLIL::id2cstr(param.first));
+ if (param.second.flags & RTLIL::CONST_FLAG_STRING) {
+ std::string str = param.second.decode_string();
+ f << stringf("\"");
+ for (char ch : str)
+ if (ch == '"' || ch == '\\')
+ f << stringf("\\%c", ch);
+ else if (ch < 32 || ch >= 127)
+ f << stringf("\\%03o", ch);
+ else
+ f << stringf("%c", ch);
+ f << stringf("\"\n");
+ } else
+ f << stringf("%s\n", param.second.as_string().c_str());
+ }
+ }
+
+ void dump()
+ {
+ f << stringf("\n");
+ f << stringf(".model %s\n", cstr(module->name));
+
+ std::map<int, RTLIL::Wire*> inputs, outputs;
+
+ for (auto &wire_it : module->wires_) {
+ RTLIL::Wire *wire = wire_it.second;
+ if (wire->port_input)
+ inputs[wire->port_id] = wire;
+ if (wire->port_output)
+ outputs[wire->port_id] = wire;
+ }
+
+ f << stringf(".inputs");
+ for (auto &it : inputs) {
+ RTLIL::Wire *wire = it.second;
+ for (int i = 0; i < wire->width; i++)
+ f << stringf(" %s", cstr(RTLIL::SigSpec(wire, i)));
+ }
+ f << stringf("\n");
+
+ f << stringf(".outputs");
+ for (auto &it : outputs) {
+ RTLIL::Wire *wire = it.second;
+ for (int i = 0; i < wire->width; i++)
+ f << stringf(" %s", cstr(RTLIL::SigSpec(wire, i)));
+ }
+ f << stringf("\n");
+
+ if (module->get_bool_attribute("\\blackbox")) {
+ f << stringf(".blackbox\n");
+ f << stringf(".end\n");
+ return;
+ }
+
+ if (!config->impltf_mode) {
+ if (!config->false_type.empty()) {
+ if (config->false_type == "+")
+ f << stringf(".names %s\n", config->false_out.c_str());
+ else if (config->false_type != "-")
+ f << stringf(".%s %s %s=$false\n", subckt_or_gate(config->false_type),
+ config->false_type.c_str(), config->false_out.c_str());
+ } else
+ f << stringf(".names $false\n");
+ if (!config->true_type.empty()) {
+ if (config->true_type == "+")
+ f << stringf(".names %s\n1\n", config->true_out.c_str());
+ else if (config->true_type != "-")
+ f << stringf(".%s %s %s=$true\n", subckt_or_gate(config->true_type),
+ config->true_type.c_str(), config->true_out.c_str());
+ } else
+ f << stringf(".names $true\n1\n");
+ if (!config->undef_type.empty()) {
+ if (config->undef_type == "+")
+ f << stringf(".names %s\n", config->undef_out.c_str());
+ else if (config->undef_type != "-")
+ f << stringf(".%s %s %s=$undef\n", subckt_or_gate(config->undef_type),
+ config->undef_type.c_str(), config->undef_out.c_str());
+ } else
+ f << stringf(".names $undef\n");
+ }
+
+ for (auto &cell_it : module->cells_)
+ {
+ RTLIL::Cell *cell = cell_it.second;
+
+ if (config->unbuf_types.count(cell->type)) {
+ auto portnames = config->unbuf_types.at(cell->type);
+ f << stringf(".names %s %s\n1 1\n",
+ cstr(cell->getPort(portnames.first)), cstr(cell->getPort(portnames.second)));
+ continue;
+ }
+
+ if (!config->icells_mode && cell->type == "$_NOT_") {
+ f << stringf(".names %s %s\n0 1\n",
+ cstr(cell->getPort("\\A")), cstr(cell->getPort("\\Y")));
+ continue;
+ }
+
+ if (!config->icells_mode && cell->type == "$_AND_") {
+ f << stringf(".names %s %s %s\n11 1\n",
+ cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
+ continue;
+ }
+
+ if (!config->icells_mode && cell->type == "$_OR_") {
+ f << stringf(".names %s %s %s\n1- 1\n-1 1\n",
+ cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
+ continue;
+ }
+
+ if (!config->icells_mode && cell->type == "$_XOR_") {
+ f << stringf(".names %s %s %s\n10 1\n01 1\n",
+ cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
+ continue;
+ }
+
+ if (!config->icells_mode && cell->type == "$_NAND_") {
+ f << stringf(".names %s %s %s\n0- 1\n-0 1\n",
+ cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
+ continue;
+ }
+
+ if (!config->icells_mode && cell->type == "$_NOR_") {
+ f << stringf(".names %s %s %s\n00 1\n",
+ cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
+ continue;
+ }
+
+ if (!config->icells_mode && cell->type == "$_XNOR_") {
+ f << stringf(".names %s %s %s\n11 1\n00 1\n",
+ cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
+ continue;
+ }
+
+ if (!config->icells_mode && cell->type == "$_AOI3_") {
+ f << stringf(".names %s %s %s %s\n-00 1\n0-0 1\n",
+ cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\Y")));
+ continue;
+ }
+
+ if (!config->icells_mode && cell->type == "$_OAI3_") {
+ f << stringf(".names %s %s %s %s\n00- 1\n--0 1\n",
+ cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\Y")));
+ continue;
+ }
+
+ if (!config->icells_mode && cell->type == "$_AOI4_") {
+ f << stringf(".names %s %s %s %s %s\n-0-0 1\n-00- 1\n0--0 1\n0-0- 1\n",
+ cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")),
+ cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y")));
+ continue;
+ }
+
+ if (!config->icells_mode && cell->type == "$_OAI4_") {
+ f << stringf(".names %s %s %s %s %s\n00-- 1\n--00 1\n",
+ cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")),
+ cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y")));
+ continue;
+ }
+
+ if (!config->icells_mode && cell->type == "$_MUX_") {
+ f << stringf(".names %s %s %s %s\n1-0 1\n-11 1\n",
+ cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")),
+ cstr(cell->getPort("\\S")), cstr(cell->getPort("\\Y")));
+ continue;
+ }
+
+ if (!config->icells_mode && cell->type == "$_FF_") {
+ f << stringf(".latch %s %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
+ cstr_init(cell->getPort("\\Q")));
+ continue;
+ }
+
+ if (!config->icells_mode && cell->type == "$_DFF_N_") {
+ f << stringf(".latch %s %s fe %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
+ cstr(cell->getPort("\\C")), cstr_init(cell->getPort("\\Q")));
+ continue;
+ }
+
+ if (!config->icells_mode && cell->type == "$_DFF_P_") {
+ f << stringf(".latch %s %s re %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
+ cstr(cell->getPort("\\C")), cstr_init(cell->getPort("\\Q")));
+ continue;
+ }
+
+ if (!config->icells_mode && cell->type == "$_DLATCH_N_") {
+ f << stringf(".latch %s %s al %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
+ cstr(cell->getPort("\\E")), cstr_init(cell->getPort("\\Q")));
+ continue;
+ }
+
+ if (!config->icells_mode && cell->type == "$_DLATCH_P_") {
+ f << stringf(".latch %s %s ah %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
+ cstr(cell->getPort("\\E")), cstr_init(cell->getPort("\\Q")));
+ continue;
+ }
+
+ if (!config->icells_mode && cell->type == "$lut") {
+ f << stringf(".names");
+ auto &inputs = cell->getPort("\\A");
+ auto width = cell->parameters.at("\\WIDTH").as_int();
+ log_assert(inputs.size() == width);
+ for (int i = width-1; i >= 0; i--)
+ f << stringf(" %s", cstr(inputs.extract(i, 1)));
+ auto &output = cell->getPort("\\Y");
+ log_assert(output.size() == 1);
+ f << stringf(" %s", cstr(output));
+ f << stringf("\n");
+ RTLIL::SigSpec mask = cell->parameters.at("\\LUT");
+ for (int i = 0; i < (1 << width); i++)
+ if (mask[i] == RTLIL::S1) {
+ for (int j = width-1; j >= 0; j--) {
+ f << ((i>>j)&1 ? '1' : '0');
+ }
+ f << " 1\n";
+ }
+ continue;
+ }
+
+ if (!config->icells_mode && cell->type == "$sop") {
+ f << stringf(".names");
+ auto &inputs = cell->getPort("\\A");
+ auto width = cell->parameters.at("\\WIDTH").as_int();
+ auto depth = cell->parameters.at("\\DEPTH").as_int();
+ vector<State> table = cell->parameters.at("\\TABLE").bits;
+ while (GetSize(table) < 2*width*depth)
+ table.push_back(State::S0);
+ log_assert(inputs.size() == width);
+ for (int i = 0; i < width; i++)
+ f << stringf(" %s", cstr(inputs.extract(i, 1)));
+ auto &output = cell->getPort("\\Y");
+ log_assert(output.size() == 1);
+ f << stringf(" %s", cstr(output));
+ f << stringf("\n");
+ for (int i = 0; i < depth; i++) {
+ for (int j = 0; j < width; j++) {
+ bool pat0 = table.at(2*width*i + 2*j + 0) == State::S1;
+ bool pat1 = table.at(2*width*i + 2*j + 1) == State::S1;
+ if (pat0 && !pat1) f << "0";
+ else if (!pat0 && pat1) f << "1";
+ else f << "-";
+ }
+ f << " 1\n";
+ }
+ continue;
+ }
+
+ f << stringf(".%s %s", subckt_or_gate(cell->type.str()), cstr(cell->type));
+ for (auto &conn : cell->connections())
+ for (int i = 0; i < conn.second.size(); i++) {
+ if (conn.second.size() == 1)
+ f << stringf(" %s", cstr(conn.first));
+ else
+ f << stringf(" %s[%d]", cstr(conn.first), i);
+ f << stringf("=%s", cstr(conn.second.extract(i, 1)));
+ }
+ f << stringf("\n");
+
+ if (config->cname_mode)
+ f << stringf(".cname %s\n", cstr(cell->name));
+ if (config->attr_mode)
+ dump_params(".attr", cell->attributes);
+ if (config->param_mode)
+ dump_params(".param", cell->parameters);
+ }
+
+ for (auto &conn : module->connections())
+ for (int i = 0; i < conn.first.size(); i++)
+ {
+ SigBit lhs_bit = conn.first[i];
+ SigBit rhs_bit = conn.second[i];
+
+ if (config->noalias_mode && cstr_bits_seen.count(lhs_bit) == 0)
+ continue;
+
+ if (config->conn_mode)
+ f << stringf(".conn %s %s\n", cstr(rhs_bit), cstr(lhs_bit));
+ else if (!config->buf_type.empty())
+ f << stringf(".%s %s %s=%s %s=%s\n", subckt_or_gate(config->buf_type), config->buf_type.c_str(),
+ config->buf_in.c_str(), cstr(rhs_bit), config->buf_out.c_str(), cstr(lhs_bit));
+ else
+ f << stringf(".names %s %s\n1 1\n", cstr(rhs_bit), cstr(lhs_bit));
+ }
+
+ f << stringf(".end\n");
+ }
+
+ static void dump(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, BlifDumperConfig &config)
+ {
+ BlifDumper dumper(f, module, design, &config);
+ dumper.dump();
+ }
+};
+
+struct BlifBackend : public Backend {
+ BlifBackend() : Backend("blif", "write design to BLIF file") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" write_blif [options] [filename]\n");
+ log("\n");
+ log("Write the current design to an BLIF file.\n");
+ log("\n");
+ log(" -top top_module\n");
+ log(" set the specified module as design top module\n");
+ log("\n");
+ log(" -buf <cell-type> <in-port> <out-port>\n");
+ log(" use cells of type <cell-type> with the specified port names for buffers\n");
+ log("\n");
+ log(" -unbuf <cell-type> <in-port> <out-port>\n");
+ log(" replace buffer cells with the specified name and port names with\n");
+ log(" a .names statement that models a buffer\n");
+ log("\n");
+ log(" -true <cell-type> <out-port>\n");
+ log(" -false <cell-type> <out-port>\n");
+ log(" -undef <cell-type> <out-port>\n");
+ log(" use the specified cell types to drive nets that are constant 1, 0, or\n");
+ log(" undefined. when '-' is used as <cell-type>, then <out-port> specifies\n");
+ log(" the wire name to be used for the constant signal and no cell driving\n");
+ log(" that wire is generated. when '+' is used as <cell-type>, then <out-port>\n");
+ log(" specifies the wire name to be used for the constant signal and a .names\n");
+ log(" statement is generated to drive the wire.\n");
+ log("\n");
+ log(" -noalias\n");
+ log(" if a net name is aliasing another net name, then by default a net\n");
+ log(" without fanout is created that is driven by the other net. This option\n");
+ log(" suppresses the generation of this nets without fanout.\n");
+ log("\n");
+ log("The following options can be useful when the generated file is not going to be\n");
+ log("read by a BLIF parser but a custom tool. It is recommended to not name the output\n");
+ log("file *.blif when any of this options is used.\n");
+ log("\n");
+ log(" -icells\n");
+ log(" do not translate Yosys's internal gates to generic BLIF logic\n");
+ log(" functions. Instead create .subckt or .gate lines for all cells.\n");
+ log("\n");
+ log(" -gates\n");
+ log(" print .gate instead of .subckt lines for all cells that are not\n");
+ log(" instantiations of other modules from this design.\n");
+ log("\n");
+ log(" -conn\n");
+ log(" do not generate buffers for connected wires. instead use the\n");
+ log(" non-standard .conn statement.\n");
+ log("\n");
+ log(" -attr\n");
+ log(" use the non-standard .attr statement to write cell attributes\n");
+ log("\n");
+ log(" -param\n");
+ log(" use the non-standard .param statement to write cell parameters\n");
+ log("\n");
+ log(" -cname\n");
+ log(" use the non-standard .cname statement to write cell names\n");
+ log("\n");
+ log(" -blackbox\n");
+ log(" write blackbox cells with .blackbox statement.\n");
+ log("\n");
+ log(" -impltf\n");
+ log(" do not write definitions for the $true, $false and $undef wires.\n");
+ log("\n");
+ }
+ virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::string top_module_name;
+ std::string buf_type, buf_in, buf_out;
+ std::string true_type, true_out;
+ std::string false_type, false_out;
+ BlifDumperConfig config;
+
+ log_header(design, "Executing BLIF backend.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-top" && argidx+1 < args.size()) {
+ top_module_name = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-buf" && argidx+3 < args.size()) {
+ config.buf_type = args[++argidx];
+ config.buf_in = args[++argidx];
+ config.buf_out = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-unbuf" && argidx+3 < args.size()) {
+ RTLIL::IdString unbuf_type = RTLIL::escape_id(args[++argidx]);
+ RTLIL::IdString unbuf_in = RTLIL::escape_id(args[++argidx]);
+ RTLIL::IdString unbuf_out = RTLIL::escape_id(args[++argidx]);
+ config.unbuf_types[unbuf_type] = std::pair<RTLIL::IdString, RTLIL::IdString>(unbuf_in, unbuf_out);
+ continue;
+ }
+ if (args[argidx] == "-true" && argidx+2 < args.size()) {
+ config.true_type = args[++argidx];
+ config.true_out = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-false" && argidx+2 < args.size()) {
+ config.false_type = args[++argidx];
+ config.false_out = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-undef" && argidx+2 < args.size()) {
+ config.undef_type = args[++argidx];
+ config.undef_out = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-icells") {
+ config.icells_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-gates") {
+ config.gates_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-conn") {
+ config.conn_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-cname") {
+ config.cname_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-param") {
+ config.param_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-attr") {
+ config.attr_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-blackbox") {
+ config.blackbox_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-impltf") {
+ config.impltf_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-noalias") {
+ config.noalias_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ if (top_module_name.empty())
+ for (auto & mod_it:design->modules_)
+ if (mod_it.second->get_bool_attribute("\\top"))
+ top_module_name = mod_it.first.str();
+
+ *f << stringf("# Generated by %s\n", yosys_version_str);
+
+ std::vector<RTLIL::Module*> mod_list;
+
+ design->sort();
+ for (auto module_it : design->modules_)
+ {
+ RTLIL::Module *module = module_it.second;
+ if (module->get_bool_attribute("\\blackbox") && !config.blackbox_mode)
+ continue;
+
+ if (module->processes.size() != 0)
+ log_error("Found unmapped processes in module %s: unmapped processes are not supported in BLIF backend!\n", RTLIL::id2cstr(module->name));
+ if (module->memories.size() != 0)
+ log_error("Found unmapped memories in module %s: unmapped memories are not supported in BLIF backend!\n", RTLIL::id2cstr(module->name));
+
+ if (module->name == RTLIL::escape_id(top_module_name)) {
+ BlifDumper::dump(*f, module, design, config);
+ top_module_name.clear();
+ continue;
+ }
+
+ mod_list.push_back(module);
+ }
+
+ if (!top_module_name.empty())
+ log_error("Can't find top module `%s'!\n", top_module_name.c_str());
+
+ for (auto module : mod_list)
+ BlifDumper::dump(*f, module, design, config);
+ }
+} BlifBackend;
+
+PRIVATE_NAMESPACE_END
diff --git a/backends/btor/Makefile.inc b/backends/btor/Makefile.inc
new file mode 100644
index 00000000..af7ab14d
--- /dev/null
+++ b/backends/btor/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += backends/btor/btor.o
+
diff --git a/backends/btor/README b/backends/btor/README
new file mode 100644
index 00000000..efcf0d8f
--- /dev/null
+++ b/backends/btor/README
@@ -0,0 +1,23 @@
+
+This is the Yosys BTOR backend.
+It is developed by Ahmed Irfan <irfan@fbk.eu> - Fondazione Bruno Kessler, Trento, Italy
+
+Master git repository for the BTOR backend:
+https://github.com/ahmedirfan1983/yosys
+
+
+[[CITE]] BTOR: Bit-Precise Modelling of Word-Level Problems for Model Checking
+Johannes Kepler University, Linz, Austria
+http://fmv.jku.at/papers/BrummayerBiereLonsing-BPR08.pdf
+
+
+Todos:
+------
+
+- Add checks for unsupported stuff
+ - unsupported cell types
+ - async resets
+ - etc..
+
+- Add support for $lut cells
+
diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc
new file mode 100644
index 00000000..bbe90e85
--- /dev/null
+++ b/backends/btor/btor.cc
@@ -0,0 +1,1111 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2014 Ahmed Irfan <irfan@fbk.eu>
+ *
+ * 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]] BTOR: Bit-Precise Modelling of Word-Level Problems for Model Checking
+// Johannes Kepler University, Linz, Austria
+// http://fmv.jku.at/papers/BrummayerBiereLonsing-BPR08.pdf
+
+#include "kernel/rtlil.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
+#include "kernel/log.h"
+#include <string>
+#include <math.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct BtorDumperConfig
+{
+ bool subckt_mode;
+ bool conn_mode;
+ bool impltf_mode;
+
+ std::string buf_type, buf_in, buf_out;
+ std::string true_type, true_out, false_type, false_out;
+
+ BtorDumperConfig() : subckt_mode(false), conn_mode(false), impltf_mode(false) { }
+};
+
+struct WireInfo
+{
+ RTLIL::IdString cell_name;
+ const RTLIL::SigChunk *chunk;
+
+ WireInfo(RTLIL::IdString c, const RTLIL::SigChunk* ch) : cell_name(c), chunk(ch) { }
+};
+
+struct WireInfoOrder
+{
+ bool operator() (const WireInfo& x, const WireInfo& y)
+ {
+ return x.chunk < y.chunk;
+ }
+};
+
+struct BtorDumper
+{
+ std::ostream &f;
+ RTLIL::Module *module;
+ RTLIL::Design *design;
+ BtorDumperConfig *config;
+ CellTypes ct;
+
+ SigMap sigmap;
+ std::map<RTLIL::IdString, std::set<WireInfo,WireInfoOrder>> inter_wire_map;//<wire, dependency list> for mapping the intermediate wires that are output of some cell
+ std::map<RTLIL::IdString, int> line_ref;//mapping of ids to line_num of the btor file
+ std::map<RTLIL::SigSpec, int> sig_ref;//mapping of sigspec to the line_num of the btor file
+ int line_num;//last line number of btor file
+ std::string str;//temp string for writing file
+ std::map<RTLIL::IdString, bool> basic_wires;//input wires and registers
+ RTLIL::IdString curr_cell; //current cell being dumped
+ std::map<std::string, std::string> cell_type_translation, s_cell_type_translation; //RTLIL to BTOR translation
+ std::map<int, std::set<std::pair<int,int>>> mem_next; // memory (line_number)'s set of condition and write
+ BtorDumper(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, BtorDumperConfig *config) :
+ f(f), module(module), design(design), config(config), ct(design), sigmap(module)
+ {
+ line_num=0;
+ str.clear();
+ for(auto it=module->wires_.begin(); it!=module->wires_.end(); ++it)
+ {
+ if(it->second->port_input)
+ {
+ basic_wires[it->first]=true;
+ }
+ else
+ {
+ basic_wires[it->first]=false;
+ }
+ inter_wire_map[it->first].clear();
+ }
+ curr_cell.clear();
+ //assert
+ cell_type_translation["$assert"] = "root";
+ //unary
+ cell_type_translation["$not"] = "not";
+ cell_type_translation["$neg"] = "neg";
+ cell_type_translation["$reduce_and"] = "redand";
+ cell_type_translation["$reduce_or"] = "redor";
+ cell_type_translation["$reduce_xor"] = "redxor";
+ cell_type_translation["$reduce_bool"] = "redor";
+ //binary
+ cell_type_translation["$and"] = "and";
+ cell_type_translation["$or"] = "or";
+ cell_type_translation["$xor"] = "xor";
+ cell_type_translation["$xnor"] = "xnor";
+ cell_type_translation["$shr"] = "srl";
+ cell_type_translation["$shl"] = "sll";
+ cell_type_translation["$sshr"] = "sra";
+ cell_type_translation["$sshl"] = "sll";
+ cell_type_translation["$shift"] = "srl";
+ cell_type_translation["$shiftx"] = "srl";
+ cell_type_translation["$lt"] = "ult";
+ cell_type_translation["$le"] = "ulte";
+ cell_type_translation["$gt"] = "ugt";
+ cell_type_translation["$ge"] = "ugte";
+ cell_type_translation["$eq"] = "eq";
+ cell_type_translation["$eqx"] = "eq";
+ cell_type_translation["$ne"] = "ne";
+ cell_type_translation["$nex"] = "ne";
+ cell_type_translation["$add"] = "add";
+ cell_type_translation["$sub"] = "sub";
+ cell_type_translation["$mul"] = "mul";
+ cell_type_translation["$mod"] = "urem";
+ cell_type_translation["$div"] = "udiv";
+ //mux
+ cell_type_translation["$mux"] = "cond";
+ //reg
+ cell_type_translation["$dff"] = "next";
+ cell_type_translation["$adff"] = "next";
+ cell_type_translation["$dffsr"] = "next";
+ //memories
+ //nothing here
+ //slice
+ cell_type_translation["$slice"] = "slice";
+ //concat
+ cell_type_translation["$concat"] = "concat";
+
+ //signed cell type translation
+ //binary
+ s_cell_type_translation["$modx"] = "srem";
+ s_cell_type_translation["$mody"] = "smod";
+ s_cell_type_translation["$div"] = "sdiv";
+ s_cell_type_translation["$lt"] = "slt";
+ s_cell_type_translation["$le"] = "slte";
+ s_cell_type_translation["$gt"] = "sgt";
+ s_cell_type_translation["$ge"] = "sgte";
+
+ }
+
+ vector<shared_str> cstr_buf;
+
+ const char *cstr(const RTLIL::IdString id)
+ {
+ str = RTLIL::unescape_id(id);
+ for (size_t i = 0; i < str.size(); ++i)
+ if (str[i] == '#' || str[i] == '=')
+ str[i] = '?';
+ cstr_buf.push_back(str);
+ return cstr_buf.back().c_str();
+ }
+
+ int dump_wire(RTLIL::Wire* wire)
+ {
+ if(basic_wires[wire->name])
+ {
+ log("writing wire %s\n", cstr(wire->name));
+ auto it = line_ref.find(wire->name);
+ if(it==std::end(line_ref))
+ {
+ ++line_num;
+ line_ref[wire->name]=line_num;
+ str = stringf("%d var %d %s", line_num, wire->width, cstr(wire->name));
+ f << stringf("%s\n", str.c_str());
+ return line_num;
+ }
+ else return it->second;
+ }
+ else // case when the wire is not basic wire
+ {
+ log("case of non-basic wire - %s\n", cstr(wire->name));
+ auto it = line_ref.find(wire->name);
+ if(it==std::end(line_ref))
+ {
+ std::set<WireInfo, WireInfoOrder>& dep_set = inter_wire_map.at(wire->name);
+ int wire_line = 0;
+ int wire_width = 0;
+ for(auto dep_set_it=dep_set.begin(); dep_set_it!=dep_set.end(); ++dep_set_it)
+ {
+ RTLIL::IdString cell_id = dep_set_it->cell_name;
+ if(cell_id == curr_cell)
+ break;
+ log(" -- found cell %s\n", cstr(cell_id));
+ RTLIL::Cell* cell = module->cells_.at(cell_id);
+ const RTLIL::SigSpec* cell_output = get_cell_output(cell);
+ int cell_line = dump_cell(cell);
+
+ if(dep_set.size()==1 && wire->width == cell_output->size())
+ {
+ wire_line = cell_line;
+ break;
+ }
+ else
+ {
+ int prev_wire_line=0; //previously dumped wire line
+ int start_bit=0;
+ for(unsigned j=0; j<cell_output->chunks().size(); ++j)
+ {
+ start_bit+=cell_output->chunks().at(j).width;
+ if(cell_output->chunks().at(j).wire->name == wire->name)
+ {
+ prev_wire_line = wire_line;
+ wire_line = ++line_num;
+ str = stringf("%d slice %d %d %d %d;1", line_num, cell_output->chunks().at(j).width,
+ cell_line, start_bit-1, start_bit-cell_output->chunks().at(j).width);
+ f << stringf("%s\n", str.c_str());
+ wire_width += cell_output->chunks().at(j).width;
+ if(prev_wire_line!=0)
+ {
+ ++line_num;
+ str = stringf("%d concat %d %d %d", line_num, wire_width, wire_line, prev_wire_line);
+ f << stringf("%s\n", str.c_str());
+ wire_line = line_num;
+ }
+ }
+ }
+ }
+ }
+ if(dep_set.size()==0)
+ {
+ log(" - checking sigmap\n");
+ RTLIL::SigSpec s = RTLIL::SigSpec(wire);
+ wire_line = dump_sigspec(&s, s.size());
+ line_ref[wire->name]=wire_line;
+ }
+ line_ref[wire->name]=wire_line;
+ return wire_line;
+ }
+ else
+ {
+ log(" -- already processed wire\n");
+ return it->second;
+ }
+ }
+ log_abort();
+ return -1;
+ }
+
+ int dump_memory(const RTLIL::Memory* memory)
+ {
+ log("writing memory %s\n", cstr(memory->name));
+ auto it = line_ref.find(memory->name);
+ if(it==std::end(line_ref))
+ {
+ ++line_num;
+ int address_bits = ceil_log2(memory->size);
+ str = stringf("%d array %d %d", line_num, memory->width, address_bits);
+ line_ref[memory->name]=line_num;
+ f << stringf("%s\n", str.c_str());
+ return line_num;
+ }
+ else return it->second;
+ }
+
+ int dump_memory_next(const RTLIL::Memory* memory)
+ {
+ auto mem_it = line_ref.find(memory->name);
+ int address_bits = ceil_log2(memory->size);
+ if(mem_it==std::end(line_ref))
+ {
+ log("can not write next of a memory that is not dumped yet\n");
+ log_abort();
+ }
+ else
+ {
+ auto acond_list_it = mem_next.find(mem_it->second);
+ if(acond_list_it!=std::end(mem_next))
+ {
+ std::set<std::pair<int,int>>& cond_list = acond_list_it->second;
+ if(cond_list.empty())
+ {
+ return 0;
+ }
+ auto it=cond_list.begin();
+ ++line_num;
+ str = stringf("%d acond %d %d %d %d %d", line_num, memory->width, address_bits, it->first, it->second, mem_it->second);
+ f << stringf("%s\n", str.c_str());
+ ++it;
+ for(; it!=cond_list.end(); ++it)
+ {
+ ++line_num;
+ str = stringf("%d acond %d %d %d %d %d", line_num, memory->width, address_bits, it->first, it->second, line_num-1);
+ f << stringf("%s\n", str.c_str());
+ }
+ ++line_num;
+ str = stringf("%d anext %d %d %d %d", line_num, memory->width, address_bits, mem_it->second, line_num-1);
+ f << stringf("%s\n", str.c_str());
+ return 1;
+ }
+ return 0;
+ }
+ }
+
+ int dump_const(const RTLIL::Const* data, int width, int offset)
+ {
+ log("writing const \n");
+ if((data->flags & RTLIL::CONST_FLAG_STRING) == 0)
+ {
+ if(width<0)
+ width = data->bits.size() - offset;
+
+ std::string data_str = data->as_string();
+ //if(offset > 0)
+ data_str = data_str.substr(offset, width);
+
+ ++line_num;
+ str = stringf("%d const %d %s", line_num, width, data_str.c_str());
+ f << stringf("%s\n", str.c_str());
+ return line_num;
+ }
+ else
+ log("writing const error\n");
+ log_abort();
+ return -1;
+ }
+
+ int dump_sigchunk(const RTLIL::SigChunk* chunk)
+ {
+ log("writing sigchunk\n");
+ int l=-1;
+ if(chunk->wire == NULL)
+ {
+ RTLIL::Const data_const(chunk->data);
+ l=dump_const(&data_const, chunk->width, chunk->offset);
+ }
+ else
+ {
+ if (chunk->width == chunk->wire->width && chunk->offset == 0)
+ l = dump_wire(chunk->wire);
+ else
+ {
+ int wire_line_num = dump_wire(chunk->wire);
+ log_assert(wire_line_num>0);
+ ++line_num;
+ str = stringf("%d slice %d %d %d %d;2", line_num, chunk->width, wire_line_num,
+ chunk->width + chunk->offset - 1, chunk->offset);
+ f << stringf("%s\n", str.c_str());
+ l = line_num;
+ }
+ }
+ return l;
+ }
+
+ int dump_sigspec(const RTLIL::SigSpec* sig, int expected_width)
+ {
+ log("writing sigspec\n");
+ RTLIL::SigSpec s = sigmap(*sig);
+ int l = -1;
+ auto it = sig_ref.find(s);
+ if(it == std::end(sig_ref))
+ {
+ if (s.is_chunk())
+ {
+ l = dump_sigchunk(&s.chunks().front());
+ }
+ else
+ {
+ int l1, l2, w1, w2;
+ l1 = dump_sigchunk(&s.chunks().front());
+ log_assert(l1>0);
+ w1 = s.chunks().front().width;
+ for (unsigned i=1; i < s.chunks().size(); ++i)
+ {
+ l2 = dump_sigchunk(&s.chunks().at(i));
+ log_assert(l2>0);
+ w2 = s.chunks().at(i).width;
+ ++line_num;
+ str = stringf("%d concat %d %d %d", line_num, w1+w2, l2, l1);
+ f << stringf("%s\n", str.c_str());
+ l1=line_num;
+ w1+=w2;
+ }
+ l = line_num;
+ }
+ sig_ref[s] = l;
+ }
+ else
+ {
+ l = it->second;
+ }
+
+ if (expected_width != s.size())
+ {
+ log(" - changing width of sigspec\n");
+ //TODO: this block may not be needed anymore, due to explicit type conversion by "splice" command
+ if(expected_width > s.size())
+ {
+ //TODO: case the signal is signed
+ ++line_num;
+ str = stringf ("%d zero %d", line_num, expected_width - s.size());
+ f << stringf("%s\n", str.c_str());
+ ++line_num;
+ str = stringf ("%d concat %d %d %d", line_num, expected_width, line_num-1, l);
+ f << stringf("%s\n", str.c_str());
+ l = line_num;
+ }
+ else if(expected_width < s.size())
+ {
+ ++line_num;
+ str = stringf ("%d slice %d %d %d %d;3", line_num, expected_width, l, expected_width-1, 0);
+ f << stringf("%s\n", str.c_str());
+ l = line_num;
+ }
+ }
+ log_assert(l>0);
+ return l;
+ }
+
+ int dump_cell(const RTLIL::Cell* cell)
+ {
+ auto it = line_ref.find(cell->name);
+ if(it==std::end(line_ref))
+ {
+ curr_cell = cell->name;
+ //assert cell
+ if(cell->type == "$assert")
+ {
+ log("writing assert cell - %s\n", cstr(cell->type));
+ const RTLIL::SigSpec* expr = &cell->getPort(RTLIL::IdString("\\A"));
+ const RTLIL::SigSpec* en = &cell->getPort(RTLIL::IdString("\\EN"));
+ log_assert(expr->size() == 1);
+ log_assert(en->size() == 1);
+ int expr_line = dump_sigspec(expr, 1);
+ int en_line = dump_sigspec(en, 1);
+ int one_line = ++line_num;
+ str = stringf("%d one 1", line_num);
+ f << stringf("%s\n", str.c_str());
+ ++line_num;
+ str = stringf("%d %s %d %d %d", line_num, cell_type_translation.at("$eq").c_str(), 1, en_line, one_line);
+ f << stringf("%s\n", str.c_str());
+ ++line_num;
+ str = stringf("%d %s %d %d %d %d", line_num, cell_type_translation.at("$mux").c_str(), 1, line_num-1,
+ expr_line, one_line);
+ f << stringf("%s\n", str.c_str());
+ int cell_line = ++line_num;
+ str = stringf("%d %s %d %d", line_num, cell_type_translation.at("$assert").c_str(), 1, -1*(line_num-1));
+ //multiplying the line number with -1, which means logical negation
+ //the reason for negative sign is that the properties in btor are given as "negation of the original property"
+ //bug identified by bobosoft
+ //http://www.reddit.com/r/yosys/comments/1w3xig/btor_backend_bug/
+ f << stringf("%s\n", str.c_str());
+ line_ref[cell->name]=cell_line;
+ }
+ //unary cells
+ else if(cell->type == "$not" || cell->type == "$neg" || cell->type == "$pos" || cell->type == "$reduce_and" ||
+ cell->type == "$reduce_or" || cell->type == "$reduce_xor" || cell->type == "$reduce_bool")
+ {
+ log("writing unary cell - %s\n", cstr(cell->type));
+ int w = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int();
+ int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int();
+ w = w>output_width ? w:output_width; //padding of w
+ int l = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), w);
+ int cell_line = l;
+ if(cell->type != "$pos")
+ {
+ cell_line = ++line_num;
+ bool reduced = (cell->type == "$not" || cell->type == "$neg") ? false : true;
+ str = stringf ("%d %s %d %d", cell_line, cell_type_translation.at(cell->type.str()).c_str(), reduced?output_width:w, l);
+ f << stringf("%s\n", str.c_str());
+ }
+ if(output_width < w && (cell->type == "$not" || cell->type == "$neg" || cell->type == "$pos"))
+ {
+ ++line_num;
+ str = stringf ("%d slice %d %d %d %d;4", line_num, output_width, cell_line, output_width-1, 0);
+ f << stringf("%s\n", str.c_str());
+ cell_line = line_num;
+ }
+ line_ref[cell->name]=cell_line;
+ }
+ else if(cell->type == "$reduce_xnor" || cell->type == "$logic_not")//no direct translation in btor
+ {
+ log("writing unary cell - %s\n", cstr(cell->type));
+ int w = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int();
+ int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int();
+ log_assert(output_width == 1);
+ int l = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), w);
+ if(cell->type == "$logic_not" && w > 1)
+ {
+ ++line_num;
+ str = stringf ("%d %s %d %d", line_num, cell_type_translation.at("$reduce_or").c_str(), output_width, l);
+ f << stringf("%s\n", str.c_str());
+ }
+ else if(cell->type == "$reduce_xnor")
+ {
+ ++line_num;
+ str = stringf ("%d %s %d %d", line_num, cell_type_translation.at("$reduce_xor").c_str(), output_width, l);
+ f << stringf("%s\n", str.c_str());
+ }
+ ++line_num;
+ str = stringf ("%d %s %d %d", line_num, cell_type_translation.at("$not").c_str(), output_width, l);
+ f << stringf("%s\n", str.c_str());
+ line_ref[cell->name]=line_num;
+ }
+ //binary cells
+ else if(cell->type == "$and" || cell->type == "$or" || cell->type == "$xor" || cell->type == "$xnor" ||
+ cell->type == "$lt" || cell->type == "$le" || cell->type == "$eq" || cell->type == "$ne" ||
+ cell->type == "$eqx" || cell->type == "$nex" || cell->type == "$ge" || cell->type == "$gt" )
+ {
+ log("writing binary cell - %s\n", cstr(cell->type));
+ int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int();
+ log_assert(!(cell->type == "$eq" || cell->type == "$ne" || cell->type == "$eqx" || cell->type == "$nex" ||
+ cell->type == "$ge" || cell->type == "$gt") || output_width == 1);
+ bool l1_signed = cell->parameters.at(RTLIL::IdString("\\A_SIGNED")).as_bool();
+ bool l2_signed YS_ATTRIBUTE(unused) = cell->parameters.at(RTLIL::IdString("\\B_SIGNED")).as_bool();
+ int l1_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int();
+ int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int();
+
+ log_assert(l1_signed == l2_signed);
+ l1_width = l1_width > output_width ? l1_width : output_width;
+ l1_width = l1_width > l2_width ? l1_width : l2_width;
+ l2_width = l2_width > l1_width ? l2_width : l1_width;
+
+ int l1 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), l1_width);
+ int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), l2_width);
+
+ ++line_num;
+ std::string op = cell_type_translation.at(cell->type.str());
+ if(cell->type == "$lt" || cell->type == "$le" ||
+ cell->type == "$eq" || cell->type == "$ne" || cell->type == "$eqx" || cell->type == "$nex" ||
+ cell->type == "$ge" || cell->type == "$gt")
+ {
+ if(l1_signed)
+ op = s_cell_type_translation.at(cell->type.str());
+ }
+
+ str = stringf ("%d %s %d %d %d", line_num, op.c_str(), output_width, l1, l2);
+ f << stringf("%s\n", str.c_str());
+
+ line_ref[cell->name]=line_num;
+ }
+ else if(cell->type == "$add" || cell->type == "$sub" || cell->type == "$mul" || cell->type == "$div" ||
+ cell->type == "$mod" )
+ {
+ //TODO: division by zero case
+ log("writing binary cell - %s\n", cstr(cell->type));
+ int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int();
+ bool l1_signed = cell->parameters.at(RTLIL::IdString("\\A_SIGNED")).as_bool();
+ bool l2_signed = cell->parameters.at(RTLIL::IdString("\\B_SIGNED")).as_bool();
+ int l1_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int();
+ int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int();
+
+ log_assert(l1_signed == l2_signed);
+ l1_width = l1_width > output_width ? l1_width : output_width;
+ l1_width = l1_width > l2_width ? l1_width : l2_width;
+ l2_width = l2_width > l1_width ? l2_width : l1_width;
+
+ int l1 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), l1_width);
+ int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), l2_width);
+
+ ++line_num;
+ std::string op = cell_type_translation.at(cell->type.str());
+ if(cell->type == "$div" && l1_signed)
+ op = s_cell_type_translation.at(cell->type.str());
+ else if(cell->type == "$mod")
+ {
+ if(l1_signed)
+ op = s_cell_type_translation.at("$modx");
+ else if(l2_signed)
+ op = s_cell_type_translation.at("$mody");
+ }
+ str = stringf ("%d %s %d %d %d", line_num, op.c_str(), l1_width, l1, l2);
+ f << stringf("%s\n", str.c_str());
+
+ if(output_width < l1_width)
+ {
+ ++line_num;
+ str = stringf ("%d slice %d %d %d %d;5", line_num, output_width, line_num-1, output_width-1, 0);
+ f << stringf("%s\n", str.c_str());
+ }
+ line_ref[cell->name]=line_num;
+ }
+ else if(cell->type == "$shr" || cell->type == "$shl" || cell->type == "$sshr" || cell->type == "$sshl" || cell->type == "$shift" || cell->type == "$shiftx")
+ {
+ log("writing binary cell - %s\n", cstr(cell->type));
+ int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int();
+ bool l1_signed = cell->parameters.at(RTLIL::IdString("\\A_SIGNED")).as_bool();
+ //bool l2_signed = cell->parameters.at(RTLIL::IdString("\\B_SIGNED")).as_bool();
+ int l1_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int();
+ l1_width = 1 << ceil_log2(l1_width);
+ int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int();
+ //log_assert(l2_width <= ceil_log2(l1_width)) );
+ int l1 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), l1_width);
+ int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), ceil_log2(l1_width));
+ int cell_output = ++line_num;
+ str = stringf ("%d %s %d %d %d", line_num, cell_type_translation.at(cell->type.str()).c_str(), l1_width, l1, l2);
+ f << stringf("%s\n", str.c_str());
+
+ if(l2_width > ceil_log2(l1_width))
+ {
+ int extra_width = l2_width - ceil_log2(l1_width);
+ l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), l2_width);
+ ++line_num;
+ str = stringf ("%d slice %d %d %d %d;6", line_num, extra_width, l2, l2_width-1, l2_width-extra_width);
+ f << stringf("%s\n", str.c_str());
+ ++line_num;
+ str = stringf ("%d one %d", line_num, extra_width);
+ f << stringf("%s\n", str.c_str());
+ int mux = ++line_num;
+ str = stringf ("%d %s %d %d %d", line_num, cell_type_translation.at("$gt").c_str(), 1, line_num-2, line_num-1);
+ f << stringf("%s\n", str.c_str());
+ ++line_num;
+ str = stringf("%d %s %d", line_num, l1_signed && cell->type == "$sshr" ? "ones":"zero", l1_width);
+ f << stringf("%s\n", str.c_str());
+ ++line_num;
+ str = stringf ("%d %s %d %d %d %d", line_num, cell_type_translation.at("$mux").c_str(), l1_width, mux, line_num-1, cell_output);
+ f << stringf("%s\n", str.c_str());
+ cell_output = line_num;
+ }
+
+ if(output_width < l1_width)
+ {
+ ++line_num;
+ str = stringf ("%d slice %d %d %d %d;5", line_num, output_width, cell_output, output_width-1, 0);
+ f << stringf("%s\n", str.c_str());
+ cell_output = line_num;
+ }
+ line_ref[cell->name] = cell_output;
+ }
+ else if(cell->type == "$logic_and" || cell->type == "$logic_or")//no direct translation in btor
+ {
+ log("writing binary cell - %s\n", cstr(cell->type));
+ int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int();
+ log_assert(output_width == 1);
+ int l1 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), output_width);
+ int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), output_width);
+ int l1_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int();
+ int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int();
+ if(l1_width >1)
+ {
+ ++line_num;
+ str = stringf ("%d %s %d %d", line_num, cell_type_translation.at("$reduce_or").c_str(), output_width, l1);
+ f << stringf("%s\n", str.c_str());
+ l1 = line_num;
+ }
+ if(l2_width > 1)
+ {
+ ++line_num;
+ str = stringf ("%d %s %d %d", line_num, cell_type_translation.at("$reduce_or").c_str(), output_width, l2);
+ f << stringf("%s\n", str.c_str());
+ l2 = line_num;
+ }
+ if(cell->type == "$logic_and")
+ {
+ ++line_num;
+ str = stringf ("%d %s %d %d %d", line_num, cell_type_translation.at("$and").c_str(), output_width, l1, l2);
+ }
+ else if(cell->type == "$logic_or")
+ {
+ ++line_num;
+ str = stringf ("%d %s %d %d %d", line_num, cell_type_translation.at("$or").c_str(), output_width, l1, l2);
+ }
+ f << stringf("%s\n", str.c_str());
+ line_ref[cell->name]=line_num;
+ }
+ //multiplexers
+ else if(cell->type == "$mux")
+ {
+ log("writing mux cell\n");
+ int output_width = cell->parameters.at(RTLIL::IdString("\\WIDTH")).as_int();
+ int l1 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), output_width);
+ int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), output_width);
+ int s = dump_sigspec(&cell->getPort(RTLIL::IdString("\\S")), 1);
+ ++line_num;
+ str = stringf ("%d %s %d %d %d %d",
+ line_num, cell_type_translation.at(cell->type.str()).c_str(), output_width, s, l2, l1);
+ //if s is 0 then l1, if s is 1 then l2 //according to the implementation of mux cell
+ f << stringf("%s\n", str.c_str());
+ line_ref[cell->name]=line_num;
+ }
+ else if(cell->type == "$pmux")
+ {
+ log("writing pmux cell\n");
+ int output_width = cell->parameters.at(RTLIL::IdString("\\WIDTH")).as_int();
+ int select_width = cell->parameters.at(RTLIL::IdString("\\S_WIDTH")).as_int();
+ int default_case = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), output_width);
+ int cases = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), output_width*select_width);
+ int select = dump_sigspec(&cell->getPort(RTLIL::IdString("\\S")), select_width);
+ int *c = new int[select_width];
+
+ for (int i=0; i<select_width; ++i)
+ {
+ ++line_num;
+ str = stringf ("%d slice 1 %d %d %d", line_num, select, i, i);
+ f << stringf("%s\n", str.c_str());
+ c[i] = line_num;
+ ++line_num;
+ str = stringf ("%d slice %d %d %d %d", line_num, output_width, cases, i*output_width+output_width-1,
+ i*output_width);
+ f << stringf("%s\n", str.c_str());
+ }
+
+ ++line_num;
+ str = stringf ("%d cond %d %d %d %d", line_num, output_width, c[select_width-1], c[select_width-1]+1, default_case);
+ f << stringf("%s\n", str.c_str());
+
+ for (int i=select_width-2; i>=0; --i)
+ {
+ ++line_num;
+ str = stringf ("%d cond %d %d %d %d", line_num, output_width, c[i], c[i]+1, line_num-1);
+ f << stringf("%s\n", str.c_str());
+ }
+
+ line_ref[cell->name]=line_num;
+ }
+ //registers
+ else if(cell->type == "$dff" || cell->type == "$adff" || cell->type == "$dffsr")
+ {
+ //TODO: remodelling of adff cells
+ log("writing cell - %s\n", cstr(cell->type));
+ int output_width = cell->parameters.at(RTLIL::IdString("\\WIDTH")).as_int();
+ log(" - width is %d\n", output_width);
+ int cond = dump_sigspec(&cell->getPort(RTLIL::IdString("\\CLK")), 1);
+ bool polarity = cell->parameters.at(RTLIL::IdString("\\CLK_POLARITY")).as_bool();
+ const RTLIL::SigSpec* cell_output = &cell->getPort(RTLIL::IdString("\\Q"));
+ int value = dump_sigspec(&cell->getPort(RTLIL::IdString("\\D")), output_width);
+ unsigned start_bit = 0;
+ for(unsigned i=0; i<cell_output->chunks().size(); ++i)
+ {
+ output_width = cell_output->chunks().at(i).width;
+ log_assert( output_width == cell_output->chunks().at(i).wire->width);//full reg is given the next value
+ int reg = dump_wire(cell_output->chunks().at(i).wire);//register
+ int slice = value;
+ if(cell_output->chunks().size()>1)
+ {
+ start_bit+=output_width;
+ slice = ++line_num;
+ str = stringf ("%d slice %d %d %d %d;", line_num, output_width, value, start_bit-1,
+ start_bit-output_width);
+ f << stringf("%s\n", str.c_str());
+ }
+ if(cell->type == "$dffsr")
+ {
+ int sync_reset = dump_sigspec(&cell->getPort(RTLIL::IdString("\\CLR")), 1);
+ bool sync_reset_pol = cell->parameters.at(RTLIL::IdString("\\CLR_POLARITY")).as_bool();
+ int sync_reset_value = dump_sigspec(&cell->getPort(RTLIL::IdString("\\SET")),
+ output_width);
+ bool sync_reset_value_pol = cell->parameters.at(RTLIL::IdString("\\SET_POLARITY")).as_bool();
+ ++line_num;
+ str = stringf ("%d %s %d %s%d %s%d %d", line_num, cell_type_translation.at("$mux").c_str(),
+ output_width, sync_reset_pol ? "":"-", sync_reset, sync_reset_value_pol? "":"-",
+ sync_reset_value, slice);
+ f << stringf("%s\n", str.c_str());
+ slice = line_num;
+ }
+ ++line_num;
+ str = stringf ("%d %s %d %s%d %d %d", line_num, cell_type_translation.at("$mux").c_str(),
+ output_width, polarity?"":"-", cond, slice, reg);
+
+ f << stringf("%s\n", str.c_str());
+ int next = line_num;
+ if(cell->type == "$adff")
+ {
+ int async_reset = dump_sigspec(&cell->getPort(RTLIL::IdString("\\ARST")), 1);
+ bool async_reset_pol = cell->parameters.at(RTLIL::IdString("\\ARST_POLARITY")).as_bool();
+ int async_reset_value = dump_const(&cell->parameters.at(RTLIL::IdString("\\ARST_VALUE")),
+ output_width, 0);
+ ++line_num;
+ str = stringf ("%d %s %d %s%d %d %d", line_num, cell_type_translation.at("$mux").c_str(),
+ output_width, async_reset_pol ? "":"-", async_reset, async_reset_value, next);
+ f << stringf("%s\n", str.c_str());
+ }
+ ++line_num;
+ str = stringf ("%d %s %d %d %d", line_num, cell_type_translation.at(cell->type.str()).c_str(),
+ output_width, reg, next);
+ f << stringf("%s\n", str.c_str());
+ }
+ line_ref[cell->name]=line_num;
+ }
+ //memories
+ else if(cell->type == "$memrd")
+ {
+ log("writing memrd cell\n");
+ if (cell->parameters.at("\\CLK_ENABLE").as_bool() == true)
+ log_error("The btor backen does not support $memrd cells with built-in registers. Run memory_dff with -wr_only.\n");
+ str = cell->parameters.at(RTLIL::IdString("\\MEMID")).decode_string();
+ int mem = dump_memory(module->memories.at(RTLIL::IdString(str.c_str())));
+ int address_width = cell->parameters.at(RTLIL::IdString("\\ABITS")).as_int();
+ int address = dump_sigspec(&cell->getPort(RTLIL::IdString("\\ADDR")), address_width);
+ int data_width = cell->parameters.at(RTLIL::IdString("\\WIDTH")).as_int();
+ ++line_num;
+ str = stringf("%d read %d %d %d", line_num, data_width, mem, address);
+ f << stringf("%s\n", str.c_str());
+ line_ref[cell->name]=line_num;
+ }
+ else if(cell->type == "$memwr")
+ {
+ log("writing memwr cell\n");
+ if (cell->parameters.at("\\CLK_ENABLE").as_bool() == false)
+ log_error("The btor backen does not support $memwr cells without built-in registers. Run memory_dff (but with -wr_only).\n");
+ int clk = dump_sigspec(&cell->getPort(RTLIL::IdString("\\CLK")), 1);
+ bool polarity = cell->parameters.at(RTLIL::IdString("\\CLK_POLARITY")).as_bool();
+ int enable = dump_sigspec(&cell->getPort(RTLIL::IdString("\\EN")), 1);
+ int address_width = cell->parameters.at(RTLIL::IdString("\\ABITS")).as_int();
+ int address = dump_sigspec(&cell->getPort(RTLIL::IdString("\\ADDR")), address_width);
+ int data_width = cell->parameters.at(RTLIL::IdString("\\WIDTH")).as_int();
+ int data = dump_sigspec(&cell->getPort(RTLIL::IdString("\\DATA")), data_width);
+ str = cell->parameters.at(RTLIL::IdString("\\MEMID")).decode_string();
+ int mem = dump_memory(module->memories.at(RTLIL::IdString(str.c_str())));
+ //check if the memory has already next
+ /*
+ auto it = mem_next.find(mem);
+ if(it != std::end(mem_next))
+ {
+ ++line_num;
+ str = cell->parameters.at(RTLIL::IdString("\\MEMID")).decode_string();
+ RTLIL::Memory *memory = module->memories.at(RTLIL::IdString(str.c_str()));
+ int address_bits = ceil_log2(memory->size);
+ str = stringf("%d array %d %d", line_num, memory->width, address_bits);
+ f << stringf("%s\n", str.c_str());
+ ++line_num;
+ str = stringf("%d eq 1 %d %d; mem invar", line_num, mem, line_num - 1);
+ f << stringf("%s\n", str.c_str());
+ mem = line_num - 1;
+ }
+ */
+ ++line_num;
+ if(polarity)
+ str = stringf("%d one 1", line_num);
+ else
+ str = stringf("%d zero 1", line_num);
+ f << stringf("%s\n", str.c_str());
+ ++line_num;
+ str = stringf("%d eq 1 %d %d", line_num, clk, line_num-1);
+ f << stringf("%s\n", str.c_str());
+ ++line_num;
+ str = stringf("%d and 1 %d %d", line_num, line_num-1, enable);
+ f << stringf("%s\n", str.c_str());
+ ++line_num;
+ str = stringf("%d write %d %d %d %d %d", line_num, data_width, address_width, mem, address, data);
+ f << stringf("%s\n", str.c_str());
+ /*
+ ++line_num;
+ str = stringf("%d acond %d %d %d %d %d", line_num, data_width, address_width, line_num-2, line_num-1, mem);
+ f << stringf("%s\n", str.c_str());
+ ++line_num;
+ str = stringf("%d anext %d %d %d %d", line_num, data_width, address_width, mem, line_num-1);
+ f << stringf("%s\n", str.c_str());
+ */
+ mem_next[mem].insert(std::make_pair(line_num-1, line_num));
+ }
+ else if(cell->type == "$slice")
+ {
+ log("writing slice cell\n");
+ const RTLIL::SigSpec* input = &cell->getPort(RTLIL::IdString("\\A"));
+ int input_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int();
+ log_assert(input->size() == input_width);
+ int input_line = dump_sigspec(input, input_width);
+ const RTLIL::SigSpec* output YS_ATTRIBUTE(unused) = &cell->getPort(RTLIL::IdString("\\Y"));
+ int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int();
+ log_assert(output->size() == output_width);
+ int offset = cell->parameters.at(RTLIL::IdString("\\OFFSET")).as_int();
+ ++line_num;
+ str = stringf("%d %s %d %d %d %d", line_num, cell_type_translation.at(cell->type.str()).c_str(), output_width, input_line, output_width+offset-1, offset);
+ f << stringf("%s\n", str.c_str());
+ line_ref[cell->name]=line_num;
+ }
+ else if(cell->type == "$concat")
+ {
+ log("writing concat cell\n");
+ const RTLIL::SigSpec* input_a = &cell->getPort(RTLIL::IdString("\\A"));
+ int input_a_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int();
+ log_assert(input_a->size() == input_a_width);
+ int input_a_line = dump_sigspec(input_a, input_a_width);
+ const RTLIL::SigSpec* input_b = &cell->getPort(RTLIL::IdString("\\B"));
+ int input_b_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int();
+ log_assert(input_b->size() == input_b_width);
+ int input_b_line = dump_sigspec(input_b, input_b_width);
+ ++line_num;
+ str = stringf("%d %s %d %d %d", line_num, cell_type_translation.at(cell->type.str()).c_str(), input_a_width+input_b_width,
+ input_a_line, input_b_line);
+ f << stringf("%s\n", str.c_str());
+ line_ref[cell->name]=line_num;
+ }
+ curr_cell.clear();
+ return line_num;
+ }
+ else
+ {
+ return it->second;
+ }
+ }
+
+ const RTLIL::SigSpec* get_cell_output(RTLIL::Cell* cell)
+ {
+ const RTLIL::SigSpec *output_sig = nullptr;
+ if (cell->type == "$memrd")
+ {
+ output_sig = &cell->getPort(RTLIL::IdString("\\DATA"));
+ }
+ else if(cell->type == "$memwr" || cell->type == "$assert")
+ {
+ //no output
+ }
+ else if(cell->type == "$dff" || cell->type == "$adff" || cell->type == "$dffsr")
+ {
+ output_sig = &cell->getPort(RTLIL::IdString("\\Q"));
+ }
+ else
+ {
+ output_sig = &cell->getPort(RTLIL::IdString("\\Y"));
+ }
+ return output_sig;
+ }
+
+ void dump_property(RTLIL::Wire *wire)
+ {
+ int l = dump_wire(wire);
+ ++line_num;
+ str = stringf("%d root 1 %d", line_num, l);
+ f << stringf("%s\n", str.c_str());
+ }
+
+ void dump()
+ {
+ f << stringf(";module %s\n", cstr(module->name));
+
+ log("creating intermediate wires map\n");
+ //creating map of intermediate wires as output of some cell
+ for (auto it = module->cells_.begin(); it != module->cells_.end(); ++it)
+ {
+ RTLIL::Cell *cell = it->second;
+ const RTLIL::SigSpec* output_sig = get_cell_output(cell);
+ if(output_sig==nullptr)
+ continue;
+ RTLIL::SigSpec s = sigmap(*output_sig);
+ output_sig = &s;
+ log(" - %s\n", cstr(it->second->type));
+ if (cell->type == "$memrd")
+ {
+ for(unsigned i=0; i<output_sig->chunks().size(); ++i)
+ {
+ RTLIL::Wire *w = output_sig->chunks().at(i).wire;
+ RTLIL::IdString wire_id = w->name;
+ inter_wire_map[wire_id].insert(WireInfo(cell->name,&output_sig->chunks().at(i)));
+ }
+ }
+ else if(cell->type == "$memwr")
+ {
+ continue;//nothing to do
+ }
+ else if(cell->type == "$dff" || cell->type == "$adff" || cell->type == "$dffsr")
+ {
+ RTLIL::IdString wire_id = output_sig->chunks().front().wire->name;
+ for(unsigned i=0; i<output_sig->chunks().size(); ++i)
+ {
+ RTLIL::Wire *w = output_sig->chunks().at(i).wire;
+ RTLIL::IdString wire_id = w->name;
+ inter_wire_map[wire_id].insert(WireInfo(cell->name,&output_sig->chunks().at(i)));
+ basic_wires[wire_id] = true;
+ }
+ }
+ else
+ {
+ for(unsigned i=0; i<output_sig->chunks().size(); ++i)
+ {
+ RTLIL::Wire *w = output_sig->chunks().at(i).wire;
+ RTLIL::IdString wire_id = w->name;
+ inter_wire_map[wire_id].insert(WireInfo(cell->name,&output_sig->chunks().at(i)));
+ }
+ }
+ }
+
+ log("writing input\n");
+ std::map<int, RTLIL::Wire*> inputs, outputs;
+ std::vector<RTLIL::Wire*> safety;
+
+ for (auto &wire_it : module->wires_) {
+ RTLIL::Wire *wire = wire_it.second;
+ if (wire->port_input)
+ inputs[wire->port_id] = wire;
+ if (wire->port_output) {
+ outputs[wire->port_id] = wire;
+ if (wire->name.str().find("safety") != std::string::npos )
+ safety.push_back(wire);
+ }
+ }
+
+ f << stringf(";inputs\n");
+ for (auto &it : inputs) {
+ RTLIL::Wire *wire = it.second;
+ dump_wire(wire);
+ }
+ f << stringf("\n");
+
+ log("writing memories\n");
+ for(auto mem_it = module->memories.begin(); mem_it != module->memories.end(); ++mem_it)
+ {
+ dump_memory(mem_it->second);
+ }
+
+ log("writing output wires\n");
+ for (auto &it : outputs) {
+ RTLIL::Wire *wire = it.second;
+ dump_wire(wire);
+ }
+
+ log("writing cells\n");
+ for(auto cell_it = module->cells_.begin(); cell_it != module->cells_.end(); ++cell_it)
+ {
+ dump_cell(cell_it->second);
+ }
+
+ log("writing memory next");
+ for(auto mem_it = module->memories.begin(); mem_it != module->memories.end(); ++mem_it)
+ {
+ dump_memory_next(mem_it->second);
+ }
+
+ for(auto it: safety)
+ dump_property(it);
+
+ f << stringf("\n");
+
+ log("writing outputs info\n");
+ f << stringf(";outputs\n");
+ for (auto &it : outputs) {
+ RTLIL::Wire *wire = it.second;
+ int l = dump_wire(wire);
+ f << stringf(";%d %s", l, cstr(wire->name));
+ }
+ f << stringf("\n");
+ }
+
+ static void dump(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, BtorDumperConfig &config)
+ {
+ BtorDumper dumper(f, module, design, &config);
+ dumper.dump();
+ }
+};
+
+struct BtorBackend : public Backend {
+ BtorBackend() : Backend("btor", "write design to BTOR file") { }
+
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" write_btor [filename]\n");
+ log("\n");
+ log("Write the current design to an BTOR file.\n");
+ }
+
+ virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::string top_module_name;
+ std::string buf_type, buf_in, buf_out;
+ std::string true_type, true_out;
+ std::string false_type, false_out;
+ BtorDumperConfig config;
+
+ log_header(design, "Executing BTOR backend.\n");
+
+ size_t argidx=1;
+ extra_args(f, filename, args, argidx);
+
+ if (top_module_name.empty())
+ for (auto & mod_it:design->modules_)
+ if (mod_it.second->get_bool_attribute("\\top"))
+ top_module_name = mod_it.first.str();
+
+ *f << stringf("; Generated by %s\n", yosys_version_str);
+ *f << stringf("; %s developed and maintained by Clifford Wolf <clifford@clifford.at>\n", yosys_version_str);
+ *f << stringf("; BTOR Backend developed by Ahmed Irfan <irfan@fbk.eu> - Fondazione Bruno Kessler, Trento, Italy\n");
+ *f << stringf(";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n");
+
+ std::vector<RTLIL::Module*> mod_list;
+
+ for (auto module_it : design->modules_)
+ {
+ RTLIL::Module *module = module_it.second;
+ if (module->get_bool_attribute("\\blackbox"))
+ continue;
+
+ if (module->processes.size() != 0)
+ log_error("Found unmapped processes in module %s: unmapped processes are not supported in BTOR backend!\n", RTLIL::id2cstr(module->name));
+
+ if (module->name == RTLIL::escape_id(top_module_name)) {
+ BtorDumper::dump(*f, module, design, config);
+ top_module_name.clear();
+ continue;
+ }
+
+ mod_list.push_back(module);
+ }
+
+ if (!top_module_name.empty())
+ log_error("Can't find top module `%s'!\n", top_module_name.c_str());
+
+ for (auto module : mod_list)
+ BtorDumper::dump(*f, module, design, config);
+ }
+} BtorBackend;
+
+PRIVATE_NAMESPACE_END
diff --git a/backends/btor/verilog2btor.sh b/backends/btor/verilog2btor.sh
new file mode 100755
index 00000000..dfd7f1a8
--- /dev/null
+++ b/backends/btor/verilog2btor.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+#
+# Script to write BTOR from Verilog design
+#
+
+if [ "$#" -ne 3 ]; then
+ echo "Usage: $0 input.v output.btor top-module-name" >&2
+ exit 1
+fi
+if ! [ -e "$1" ]; then
+ echo "$1 not found" >&2
+ exit 1
+fi
+
+FULL_PATH=$(readlink -f $1)
+DIR=$(dirname $FULL_PATH)
+
+./yosys -q -p "
+read_verilog -sv $1;
+hierarchy -top $3;
+hierarchy -libdir $DIR;
+hierarchy -check;
+proc;
+opt; opt_expr -mux_undef; opt;
+rename -hide;;;
+#techmap -map +/pmux2mux.v;;
+splice; opt;
+memory_dff -wr_only;
+memory_collect;;
+flatten;;
+memory_unpack;
+splitnets -driver;
+setundef -zero -undriven;
+opt;;;
+write_btor $2;"
+
diff --git a/backends/edif/Makefile.inc b/backends/edif/Makefile.inc
new file mode 100644
index 00000000..93de0e24
--- /dev/null
+++ b/backends/edif/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += backends/edif/edif.o
+
diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc
new file mode 100644
index 00000000..d16f1831
--- /dev/null
+++ b/backends/edif/edif.cc
@@ -0,0 +1,367 @@
+/*
+ * 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.
+ *
+ */
+
+// [[CITE]] EDIF Version 2 0 0 Grammar
+// http://web.archive.org/web/20050730021644/http://www.edif.org/documentation/BNF_GRAMMAR/index.html
+
+#include "kernel/rtlil.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
+#include "kernel/log.h"
+#include <string>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+#define EDIF_DEF(_id) edif_names(RTLIL::unescape_id(_id), true).c_str()
+#define EDIF_REF(_id) edif_names(RTLIL::unescape_id(_id), false).c_str()
+
+namespace
+{
+ struct EdifNames
+ {
+ int counter;
+ std::set<std::string> generated_names, used_names;
+ std::map<std::string, std::string> name_map;
+
+ EdifNames() : counter(1) { }
+
+ std::string operator()(std::string id, bool define)
+ {
+ if (define) {
+ std::string new_id = operator()(id, false);
+ return new_id != id ? stringf("(rename %s \"%s\")", new_id.c_str(), id.c_str()) : id;
+ }
+
+ if (name_map.count(id) > 0)
+ return name_map.at(id);
+ if (generated_names.count(id) > 0)
+ goto do_rename;
+ if (id == "GND" || id == "VCC")
+ goto do_rename;
+
+ for (size_t i = 0; i < id.size(); i++) {
+ if ('A' <= id[i] && id[i] <= 'Z')
+ continue;
+ if ('a' <= id[i] && id[i] <= 'z')
+ continue;
+ if ('0' <= id[i] && id[i] <= '9' && i > 0)
+ continue;
+ if (id[i] == '_' && i > 0 && i != id.size()-1)
+ continue;
+ goto do_rename;
+ }
+
+ used_names.insert(id);
+ return id;
+
+ do_rename:;
+ std::string gen_name;
+ while (1) {
+ gen_name = stringf("id%05d", counter++);
+ if (generated_names.count(gen_name) == 0 &&
+ used_names.count(gen_name) == 0)
+ break;
+ }
+ generated_names.insert(gen_name);
+ name_map[id] = gen_name;
+ return gen_name;
+ }
+ };
+}
+
+struct EdifBackend : public Backend {
+ EdifBackend() : Backend("edif", "write design to EDIF netlist file") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" write_edif [options] [filename]\n");
+ log("\n");
+ log("Write the current design to an EDIF netlist file.\n");
+ log("\n");
+ log(" -top top_module\n");
+ log(" set the specified module as design top module\n");
+ log("\n");
+ log(" -nogndvcc\n");
+ log(" do not create \"GND\" and \"VCC\" cells. (this will produce an error\n");
+ log(" if the design contains constant nets. use \"hilomap\" to map to custom\n");
+ log(" constant drivers first)\n");
+ log("\n");
+ log("Unfortunately there are different \"flavors\" of the EDIF file format. This\n");
+ log("command generates EDIF files for the Xilinx place&route tools. It might be\n");
+ log("necessary to make small modifications to this command when a different tool\n");
+ log("is targeted.\n");
+ log("\n");
+ }
+ virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing EDIF backend.\n");
+
+ std::string top_module_name;
+ std::map<RTLIL::IdString, std::map<RTLIL::IdString, int>> lib_cell_ports;
+ bool nogndvcc = false;
+ CellTypes ct(design);
+ EdifNames edif_names;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-top" && argidx+1 < args.size()) {
+ top_module_name = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-nogndvcc") {
+ nogndvcc = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ if (top_module_name.empty())
+ for (auto & mod_it:design->modules_)
+ if (mod_it.second->get_bool_attribute("\\top"))
+ top_module_name = mod_it.first.str();
+
+ for (auto module_it : design->modules_)
+ {
+ RTLIL::Module *module = module_it.second;
+ if (module->get_bool_attribute("\\blackbox"))
+ continue;
+
+ if (top_module_name.empty())
+ top_module_name = module->name.str();
+
+ if (module->processes.size() != 0)
+ log_error("Found unmapped processes in module %s: unmapped processes are not supported in EDIF backend!\n", RTLIL::id2cstr(module->name));
+ if (module->memories.size() != 0)
+ log_error("Found unmapped memories in module %s: unmapped memories are not supported in EDIF backend!\n", RTLIL::id2cstr(module->name));
+
+ for (auto cell_it : module->cells_)
+ {
+ RTLIL::Cell *cell = cell_it.second;
+ if (!design->modules_.count(cell->type) || design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) {
+ lib_cell_ports[cell->type];
+ for (auto p : cell->connections())
+ lib_cell_ports[cell->type][p.first] = GetSize(p.second);
+ }
+ }
+ }
+
+ if (top_module_name.empty())
+ log_error("No module found in design!\n");
+
+ *f << stringf("(edif %s\n", EDIF_DEF(top_module_name));
+ *f << stringf(" (edifVersion 2 0 0)\n");
+ *f << stringf(" (edifLevel 0)\n");
+ *f << stringf(" (keywordMap (keywordLevel 0))\n");
+ *f << stringf(" (comment \"Generated by %s\")\n", yosys_version_str);
+
+ *f << stringf(" (external LIB\n");
+ *f << stringf(" (edifLevel 0)\n");
+ *f << stringf(" (technology (numberDefinition))\n");
+
+ if (!nogndvcc)
+ {
+ *f << stringf(" (cell GND\n");
+ *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(" )\n");
+ *f << stringf(" )\n");
+
+ *f << stringf(" (cell VCC\n");
+ *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(" )\n");
+ *f << stringf(" )\n");
+ }
+
+ for (auto &cell_it : lib_cell_ports) {
+ *f << stringf(" (cell %s\n", EDIF_DEF(cell_it.first));
+ *f << stringf(" (cellType GENERIC)\n");
+ *f << stringf(" (view VIEW_NETLIST\n");
+ *f << stringf(" (viewType NETLIST)\n");
+ *f << stringf(" (interface\n");
+ for (auto &port_it : cell_it.second) {
+ const char *dir = "INOUT";
+ if (ct.cell_known(cell_it.first)) {
+ if (!ct.cell_output(cell_it.first, port_it.first))
+ dir = "INPUT";
+ else if (!ct.cell_input(cell_it.first, port_it.first))
+ dir = "OUTPUT";
+ }
+ if (port_it.second == 1)
+ *f << stringf(" (port %s (direction %s))\n", EDIF_DEF(port_it.first), dir);
+ else
+ *f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEF(port_it.first), port_it.second, dir);
+ }
+ *f << stringf(" )\n");
+ *f << stringf(" )\n");
+ *f << stringf(" )\n");
+ }
+ *f << stringf(" )\n");
+
+ std::vector<RTLIL::Module*> sorted_modules;
+
+ // extract module dependencies
+ std::map<RTLIL::Module*, std::set<RTLIL::Module*>> module_deps;
+ for (auto &mod_it : design->modules_) {
+ module_deps[mod_it.second] = std::set<RTLIL::Module*>();
+ for (auto &cell_it : mod_it.second->cells_)
+ if (design->modules_.count(cell_it.second->type) > 0)
+ module_deps[mod_it.second].insert(design->modules_.at(cell_it.second->type));
+ }
+
+ // simple good-enough topological sort
+ // (O(n*m) on n elements and depth m)
+ while (module_deps.size() > 0) {
+ size_t sorted_modules_idx = sorted_modules.size();
+ for (auto &it : module_deps) {
+ for (auto &dep : it.second)
+ if (module_deps.count(dep) > 0)
+ goto not_ready_yet;
+ // log("Next in topological sort: %s\n", RTLIL::id2cstr(it.first->name));
+ sorted_modules.push_back(it.first);
+ not_ready_yet:;
+ }
+ if (sorted_modules_idx == sorted_modules.size())
+ log_error("Cyclic dependency between modules found! Cycle includes module %s.\n", RTLIL::id2cstr(module_deps.begin()->first->name));
+ while (sorted_modules_idx < sorted_modules.size())
+ module_deps.erase(sorted_modules.at(sorted_modules_idx++));
+ }
+
+
+ *f << stringf(" (library DESIGN\n");
+ *f << stringf(" (edifLevel 0)\n");
+ *f << stringf(" (technology (numberDefinition))\n");
+ for (auto module : sorted_modules)
+ {
+ if (module->get_bool_attribute("\\blackbox"))
+ continue;
+
+ SigMap sigmap(module);
+ std::map<RTLIL::SigSpec, std::set<std::string>> net_join_db;
+
+ *f << stringf(" (cell %s\n", EDIF_DEF(module->name));
+ *f << stringf(" (cellType GENERIC)\n");
+ *f << stringf(" (view VIEW_NETLIST\n");
+ *f << stringf(" (viewType NETLIST)\n");
+ *f << stringf(" (interface\n");
+ for (auto &wire_it : module->wires_) {
+ RTLIL::Wire *wire = wire_it.second;
+ if (wire->port_id == 0)
+ continue;
+ const char *dir = "INOUT";
+ if (!wire->port_output)
+ dir = "INPUT";
+ else if (!wire->port_input)
+ dir = "OUTPUT";
+ if (wire->width == 1) {
+ *f << stringf(" (port %s (direction %s))\n", EDIF_DEF(wire->name), dir);
+ RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire));
+ net_join_db[sig].insert(stringf("(portRef %s)", EDIF_REF(wire->name)));
+ } else {
+ *f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEF(wire->name), wire->width, dir);
+ for (int i = 0; i < wire->width; i++) {
+ RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire, i));
+ net_join_db[sig].insert(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), i));
+ }
+ }
+ }
+ *f << stringf(" )\n");
+ *f << stringf(" (contents\n");
+ if (!nogndvcc) {
+ *f << stringf(" (instance GND (viewRef VIEW_NETLIST (cellRef GND (libraryRef LIB))))\n");
+ *f << stringf(" (instance VCC (viewRef VIEW_NETLIST (cellRef VCC (libraryRef LIB))))\n");
+ }
+ for (auto &cell_it : module->cells_) {
+ RTLIL::Cell *cell = cell_it.second;
+ *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());
+ else {
+ std::string hex_string = "";
+ for (size_t i = 0; i < p.second.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;
+ 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");
+ for (auto &p : cell->connections()) {
+ RTLIL::SigSpec sig = sigmap(p.second);
+ for (int i = 0; i < GetSize(sig); i++)
+ if (sig.size() == 1)
+ net_join_db[sig[i]].insert(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name)));
+ else
+ net_join_db[sig[i]].insert(stringf("(portRef (member %s %d) (instanceRef %s))", EDIF_REF(p.first), i, EDIF_REF(cell->name)));
+ }
+ }
+ for (auto &it : net_join_db) {
+ RTLIL::SigBit sig = it.first;
+ if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1)
+ continue;
+ std::string netname = log_signal(sig);
+ for (size_t i = 0; i < netname.size(); i++)
+ if (netname[i] == ' ' || netname[i] == '\\')
+ netname.erase(netname.begin() + i--);
+ *f << stringf(" (net %s (joined\n", EDIF_DEF(netname));
+ for (auto &ref : it.second)
+ *f << stringf(" %s\n", ref.c_str());
+ if (sig.wire == NULL) {
+ 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");
+ if (sig == RTLIL::State::S1)
+ *f << stringf(" (portRef P (instanceRef VCC))\n");
+ }
+ *f << stringf(" ))\n");
+ }
+ *f << stringf(" )\n");
+ *f << stringf(" )\n");
+ *f << stringf(" )\n");
+ }
+ *f << stringf(" )\n");
+
+ *f << stringf(" (design %s\n", EDIF_DEF(top_module_name));
+ *f << stringf(" (cellRef %s (libraryRef DESIGN))\n", EDIF_REF(top_module_name));
+ *f << stringf(" )\n");
+
+ *f << stringf(")\n");
+ }
+} EdifBackend;
+
+PRIVATE_NAMESPACE_END
diff --git a/backends/ilang/Makefile.inc b/backends/ilang/Makefile.inc
new file mode 100644
index 00000000..52fc2b89
--- /dev/null
+++ b/backends/ilang/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += backends/ilang/ilang_backend.o
+
diff --git a/backends/ilang/ilang_backend.cc b/backends/ilang/ilang_backend.cc
new file mode 100644
index 00000000..16d1a97f
--- /dev/null
+++ b/backends/ilang/ilang_backend.cc
@@ -0,0 +1,504 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * A very simple and straightforward backend for the RTLIL text
+ * representation (as understood by the 'ilang' frontend).
+ *
+ */
+
+#include "ilang_backend.h"
+#include "kernel/yosys.h"
+#include <errno.h>
+
+USING_YOSYS_NAMESPACE
+using namespace ILANG_BACKEND;
+YOSYS_NAMESPACE_BEGIN
+
+void ILANG_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int width, int offset, bool autoint)
+{
+ if (width < 0)
+ width = data.bits.size() - offset;
+ if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) {
+ if (width == 32 && autoint) {
+ int32_t val = 0;
+ for (int i = 0; i < width; i++) {
+ log_assert(offset+i < (int)data.bits.size());
+ switch (data.bits[offset+i]) {
+ case RTLIL::S0: break;
+ case RTLIL::S1: val |= 1 << i; break;
+ default: val = -1; break;
+ }
+ }
+ if (val >= 0) {
+ f << stringf("%d", val);
+ return;
+ }
+ }
+ f << stringf("%d'", width);
+ for (int i = offset+width-1; i >= offset; i--) {
+ log_assert(i < (int)data.bits.size());
+ switch (data.bits[i]) {
+ case RTLIL::S0: f << stringf("0"); break;
+ case RTLIL::S1: f << stringf("1"); break;
+ case RTLIL::Sx: f << stringf("x"); break;
+ case RTLIL::Sz: f << stringf("z"); break;
+ case RTLIL::Sa: f << stringf("-"); break;
+ case RTLIL::Sm: f << stringf("m"); break;
+ }
+ }
+ } else {
+ f << stringf("\"");
+ std::string str = data.decode_string();
+ for (size_t i = 0; i < str.size(); i++) {
+ if (str[i] == '\n')
+ f << stringf("\\n");
+ else if (str[i] == '\t')
+ f << stringf("\\t");
+ else if (str[i] < 32)
+ f << stringf("\\%03o", str[i]);
+ else if (str[i] == '"')
+ f << stringf("\\\"");
+ else if (str[i] == '\\')
+ f << stringf("\\\\");
+ else
+ f << str[i];
+ }
+ f << stringf("\"");
+ }
+}
+
+void ILANG_BACKEND::dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool autoint)
+{
+ if (chunk.wire == NULL) {
+ dump_const(f, chunk.data, chunk.width, chunk.offset, autoint);
+ } else {
+ if (chunk.width == chunk.wire->width && chunk.offset == 0)
+ f << stringf("%s", chunk.wire->name.c_str());
+ else if (chunk.width == 1)
+ f << stringf("%s [%d]", chunk.wire->name.c_str(), chunk.offset);
+ else
+ f << stringf("%s [%d:%d]", chunk.wire->name.c_str(), chunk.offset+chunk.width-1, chunk.offset);
+ }
+}
+
+void ILANG_BACKEND::dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, bool autoint)
+{
+ if (sig.is_chunk()) {
+ dump_sigchunk(f, sig.as_chunk(), autoint);
+ } else {
+ f << stringf("{ ");
+ for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); ++it) {
+ dump_sigchunk(f, *it, false);
+ f << stringf(" ");
+ }
+ f << stringf("}");
+ }
+}
+
+void ILANG_BACKEND::dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire)
+{
+ for (auto &it : wire->attributes) {
+ f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str());
+ dump_const(f, it.second);
+ f << stringf("\n");
+ }
+ f << stringf("%s" "wire ", indent.c_str());
+ if (wire->width != 1)
+ f << stringf("width %d ", wire->width);
+ if (wire->upto)
+ f << stringf("upto ");
+ if (wire->start_offset != 0)
+ f << stringf("offset %d ", wire->start_offset);
+ if (wire->port_input && !wire->port_output)
+ f << stringf("input %d ", wire->port_id);
+ if (!wire->port_input && wire->port_output)
+ f << stringf("output %d ", wire->port_id);
+ if (wire->port_input && wire->port_output)
+ f << stringf("inout %d ", wire->port_id);
+ f << stringf("%s\n", wire->name.c_str());
+}
+
+void ILANG_BACKEND::dump_memory(std::ostream &f, std::string indent, const RTLIL::Memory *memory)
+{
+ for (auto &it : memory->attributes) {
+ f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str());
+ dump_const(f, it.second);
+ f << stringf("\n");
+ }
+ f << stringf("%s" "memory ", indent.c_str());
+ if (memory->width != 1)
+ f << stringf("width %d ", memory->width);
+ if (memory->size != 0)
+ f << stringf("size %d ", memory->size);
+ if (memory->start_offset != 0)
+ f << stringf("offset %d ", memory->start_offset);
+ f << stringf("%s\n", memory->name.c_str());
+}
+
+void ILANG_BACKEND::dump_cell(std::ostream &f, std::string indent, const RTLIL::Cell *cell)
+{
+ for (auto &it : cell->attributes) {
+ f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str());
+ dump_const(f, it.second);
+ f << stringf("\n");
+ }
+ f << stringf("%s" "cell %s %s\n", indent.c_str(), cell->type.c_str(), cell->name.c_str());
+ for (auto &it : cell->parameters) {
+ f << stringf("%s parameter%s %s ", indent.c_str(), (it.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "", it.first.c_str());
+ dump_const(f, it.second);
+ f << stringf("\n");
+ }
+ for (auto &it : cell->connections()) {
+ f << stringf("%s connect %s ", indent.c_str(), it.first.c_str());
+ dump_sigspec(f, it.second);
+ f << stringf("\n");
+ }
+ f << stringf("%s" "end\n", indent.c_str());
+}
+
+void ILANG_BACKEND::dump_proc_case_body(std::ostream &f, std::string indent, const RTLIL::CaseRule *cs)
+{
+ for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it)
+ {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, it->first);
+ f << stringf(" ");
+ dump_sigspec(f, it->second);
+ f << stringf("\n");
+ }
+
+ for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
+ dump_proc_switch(f, indent, *it);
+}
+
+void ILANG_BACKEND::dump_proc_switch(std::ostream &f, std::string indent, const RTLIL::SwitchRule *sw)
+{
+ for (auto it = sw->attributes.begin(); it != sw->attributes.end(); ++it) {
+ f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str());
+ dump_const(f, it->second);
+ f << stringf("\n");
+ }
+
+ f << stringf("%s" "switch ", indent.c_str());
+ dump_sigspec(f, sw->signal);
+ f << stringf("\n");
+
+ for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it)
+ {
+ f << stringf("%s case ", indent.c_str());
+ for (size_t i = 0; i < (*it)->compare.size(); i++) {
+ if (i > 0)
+ f << stringf(", ");
+ dump_sigspec(f, (*it)->compare[i]);
+ }
+ f << stringf("\n");
+
+ dump_proc_case_body(f, indent + " ", *it);
+ }
+
+ f << stringf("%s" "end\n", indent.c_str());
+}
+
+void ILANG_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RTLIL::SyncRule *sy)
+{
+ f << stringf("%s" "sync ", indent.c_str());
+ switch (sy->type) {
+ if (0) case RTLIL::ST0: f << stringf("low ");
+ if (0) case RTLIL::ST1: f << stringf("high ");
+ if (0) case RTLIL::STp: f << stringf("posedge ");
+ if (0) case RTLIL::STn: f << stringf("negedge ");
+ if (0) case RTLIL::STe: f << stringf("edge ");
+ dump_sigspec(f, sy->signal);
+ f << stringf("\n");
+ break;
+ case RTLIL::STa: f << stringf("always\n"); break;
+ case RTLIL::STg: f << stringf("global\n"); break;
+ case RTLIL::STi: f << stringf("init\n"); break;
+ }
+
+ for (auto it = sy->actions.begin(); it != sy->actions.end(); ++it) {
+ f << stringf("%s update ", indent.c_str());
+ dump_sigspec(f, it->first);
+ f << stringf(" ");
+ dump_sigspec(f, it->second);
+ f << stringf("\n");
+ }
+}
+
+void ILANG_BACKEND::dump_proc(std::ostream &f, std::string indent, const RTLIL::Process *proc)
+{
+ for (auto it = proc->attributes.begin(); it != proc->attributes.end(); ++it) {
+ f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str());
+ dump_const(f, it->second);
+ f << stringf("\n");
+ }
+ f << stringf("%s" "process %s\n", indent.c_str(), proc->name.c_str());
+ dump_proc_case_body(f, indent + " ", &proc->root_case);
+ for (auto it = proc->syncs.begin(); it != proc->syncs.end(); ++it)
+ dump_proc_sync(f, indent + " ", *it);
+ f << stringf("%s" "end\n", indent.c_str());
+}
+
+void ILANG_BACKEND::dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
+{
+ f << stringf("%s" "connect ", indent.c_str());
+ dump_sigspec(f, left);
+ f << stringf(" ");
+ dump_sigspec(f, right);
+ f << stringf("\n");
+}
+
+void ILANG_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Module *module, RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n)
+{
+ bool print_header = flag_m || design->selected_whole_module(module->name);
+ bool print_body = !flag_n || !design->selected_whole_module(module->name);
+
+ if (print_header)
+ {
+ for (auto it = module->attributes.begin(); it != module->attributes.end(); ++it) {
+ f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str());
+ dump_const(f, it->second);
+ f << stringf("\n");
+ }
+
+ f << stringf("%s" "module %s\n", indent.c_str(), module->name.c_str());
+
+ if (!module->avail_parameters.empty()) {
+ if (only_selected)
+ f << stringf("\n");
+ for (auto &p : module->avail_parameters)
+ f << stringf("%s" " parameter %s\n", indent.c_str(), p.c_str());
+ }
+ }
+
+ if (print_body)
+ {
+ for (auto it : module->wires())
+ if (!only_selected || design->selected(module, it)) {
+ if (only_selected)
+ f << stringf("\n");
+ dump_wire(f, indent + " ", it);
+ }
+
+ for (auto it : module->memories)
+ if (!only_selected || design->selected(module, it.second)) {
+ if (only_selected)
+ f << stringf("\n");
+ dump_memory(f, indent + " ", it.second);
+ }
+
+ for (auto it : module->cells())
+ if (!only_selected || design->selected(module, it)) {
+ if (only_selected)
+ f << stringf("\n");
+ dump_cell(f, indent + " ", it);
+ }
+
+ for (auto it : module->processes)
+ if (!only_selected || design->selected(module, it.second)) {
+ if (only_selected)
+ f << stringf("\n");
+ dump_proc(f, indent + " ", it.second);
+ }
+
+ bool first_conn_line = true;
+ for (auto it = module->connections().begin(); it != module->connections().end(); ++it) {
+ bool show_conn = !only_selected;
+ if (only_selected) {
+ RTLIL::SigSpec sigs = it->first;
+ sigs.append(it->second);
+ for (auto &c : sigs.chunks()) {
+ if (c.wire == NULL || !design->selected(module, c.wire))
+ continue;
+ show_conn = true;
+ }
+ }
+ if (show_conn) {
+ if (only_selected && first_conn_line)
+ f << stringf("\n");
+ dump_conn(f, indent + " ", it->first, it->second);
+ first_conn_line = false;
+ }
+ }
+ }
+
+ if (print_header)
+ f << stringf("%s" "end\n", indent.c_str());
+}
+
+void ILANG_BACKEND::dump_design(std::ostream &f, RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n)
+{
+#ifndef NDEBUG
+ int init_autoidx = autoidx;
+#endif
+
+ if (!flag_m) {
+ int count_selected_mods = 0;
+ for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it) {
+ if (design->selected_whole_module(it->first))
+ flag_m = true;
+ if (design->selected(it->second))
+ count_selected_mods++;
+ }
+ if (count_selected_mods > 1)
+ flag_m = true;
+ }
+
+ if (!only_selected || flag_m) {
+ if (only_selected)
+ f << stringf("\n");
+ f << stringf("autoidx %d\n", autoidx);
+ }
+
+ for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it) {
+ if (!only_selected || design->selected(it->second)) {
+ if (only_selected)
+ f << stringf("\n");
+ dump_module(f, "", it->second, design, only_selected, flag_m, flag_n);
+ }
+ }
+
+ log_assert(init_autoidx == autoidx);
+}
+
+YOSYS_NAMESPACE_END
+PRIVATE_NAMESPACE_BEGIN
+
+struct IlangBackend : public Backend {
+ IlangBackend() : Backend("ilang", "write design to ilang file") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" write_ilang [filename]\n");
+ log("\n");
+ log("Write the current design to an 'ilang' file. (ilang is a text representation\n");
+ log("of a design in yosys's internal format.)\n");
+ log("\n");
+ log(" -selected\n");
+ log(" only write selected parts of the design.\n");
+ log("\n");
+ }
+ virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool selected = false;
+
+ log_header(design, "Executing ILANG backend.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-selected") {
+ selected = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ design->sort();
+
+ log("Output filename: %s\n", filename.c_str());
+ *f << stringf("# Generated by %s\n", yosys_version_str);
+ ILANG_BACKEND::dump_design(*f, design, selected, true, false);
+ }
+} IlangBackend;
+
+struct DumpPass : public Pass {
+ DumpPass() : Pass("dump", "print parts of the design in ilang format") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" dump [options] [selection]\n");
+ log("\n");
+ log("Write the selected parts of the design to the console or specified file in\n");
+ log("ilang format.\n");
+ log("\n");
+ log(" -m\n");
+ log(" also dump the module headers, even if only parts of a single\n");
+ log(" module is selected\n");
+ log("\n");
+ log(" -n\n");
+ log(" only dump the module headers if the entire module is selected\n");
+ log("\n");
+ log(" -o <filename>\n");
+ log(" write to the specified file.\n");
+ log("\n");
+ log(" -a <filename>\n");
+ log(" like -outfile but append instead of overwrite\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::string filename;
+ bool flag_m = false, flag_n = false, append = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if ((arg == "-o" || arg == "-outfile") && argidx+1 < args.size()) {
+ filename = args[++argidx];
+ append = false;
+ continue;
+ }
+ if ((arg == "-a" || arg == "-append") && argidx+1 < args.size()) {
+ filename = args[++argidx];
+ append = true;
+ continue;
+ }
+ if (arg == "-m") {
+ flag_m = true;
+ continue;
+ }
+ if (arg == "-n") {
+ flag_n = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ std::ostream *f;
+ std::stringstream buf;
+
+ if (!filename.empty()) {
+ std::ofstream *ff = new std::ofstream;
+ ff->open(filename.c_str(), append ? std::ofstream::app : std::ofstream::trunc);
+ if (ff->fail()) {
+ delete ff;
+ log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
+ }
+ f = ff;
+ } else {
+ f = &buf;
+ }
+
+ ILANG_BACKEND::dump_design(*f, design, true, flag_m, flag_n);
+
+ if (!filename.empty()) {
+ delete f;
+ } else {
+ log("%s", buf.str().c_str());
+ }
+ }
+} DumpPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/backends/ilang/ilang_backend.h b/backends/ilang/ilang_backend.h
new file mode 100644
index 00000000..97dcbb62
--- /dev/null
+++ b/backends/ilang/ilang_backend.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * A very simple and straightforward backend for the RTLIL text
+ * representation (as understood by the 'ilang' frontend).
+ *
+ */
+
+#ifndef ILANG_BACKEND_H
+#define ILANG_BACKEND_H
+
+#include "kernel/yosys.h"
+#include <stdio.h>
+
+YOSYS_NAMESPACE_BEGIN
+
+namespace ILANG_BACKEND {
+ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool autoint = true);
+ void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool autoint = true);
+ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, bool autoint = true);
+ void dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire);
+ void dump_memory(std::ostream &f, std::string indent, const RTLIL::Memory *memory);
+ void dump_cell(std::ostream &f, std::string indent, const RTLIL::Cell *cell);
+ void dump_proc_case_body(std::ostream &f, std::string indent, const RTLIL::CaseRule *cs);
+ void dump_proc_switch(std::ostream &f, std::string indent, const RTLIL::SwitchRule *sw);
+ void dump_proc_sync(std::ostream &f, std::string indent, const RTLIL::SyncRule *sy);
+ void dump_proc(std::ostream &f, std::string indent, const RTLIL::Process *proc);
+ void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right);
+ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module, RTLIL::Design *design, bool only_selected, bool flag_m = true, bool flag_n = false);
+ void dump_design(std::ostream &f, RTLIL::Design *design, bool only_selected, bool flag_m = true, bool flag_n = false);
+}
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/backends/intersynth/Makefile.inc b/backends/intersynth/Makefile.inc
new file mode 100644
index 00000000..85df1b39
--- /dev/null
+++ b/backends/intersynth/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += backends/intersynth/intersynth.o
+
diff --git a/backends/intersynth/intersynth.cc b/backends/intersynth/intersynth.cc
new file mode 100644
index 00000000..34cb52fb
--- /dev/null
+++ b/backends/intersynth/intersynth.cc
@@ -0,0 +1,220 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/rtlil.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
+#include "kernel/log.h"
+#include <string>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+static std::string netname(std::set<std::string> &conntypes_code, std::set<std::string> &celltypes_code, std::set<std::string> &constcells_code, RTLIL::SigSpec sig)
+{
+ if (!sig.is_fully_const() && !sig.is_wire())
+ log_error("Can't export composite or non-word-wide signal %s.\n", log_signal(sig));
+
+ conntypes_code.insert(stringf("conntype b%d %d 2 %d\n", sig.size(), sig.size(), sig.size()));
+
+ if (sig.is_fully_const()) {
+ celltypes_code.insert(stringf("celltype CONST_%d b%d *CONST cfg:%d VALUE\n", sig.size(), sig.size(), sig.size()));
+ constcells_code.insert(stringf("node CONST_%d_0x%x CONST_%d CONST CONST_%d_0x%x VALUE 0x%x\n",
+ sig.size(), sig.as_int(), sig.size(), sig.size(), sig.as_int(), sig.as_int()));
+ return stringf("CONST_%d_0x%x", sig.size(), sig.as_int());
+ }
+
+ return RTLIL::unescape_id(sig.as_wire()->name);
+}
+
+struct IntersynthBackend : public Backend {
+ IntersynthBackend() : Backend("intersynth", "write design to InterSynth netlist file") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" write_intersynth [options] [filename]\n");
+ log("\n");
+ log("Write the current design to an 'intersynth' netlist file. InterSynth is\n");
+ log("a tool for Coarse-Grain Example-Driven Interconnect Synthesis.\n");
+ log("\n");
+ log(" -notypes\n");
+ log(" do not generate celltypes and conntypes commands. i.e. just output\n");
+ log(" the netlists. this is used for postsilicon synthesis.\n");
+ log("\n");
+ log(" -lib <verilog_or_ilang_file>\n");
+ log(" Use the specified library file for determining whether cell ports are\n");
+ log(" inputs or outputs. This option can be used multiple times to specify\n");
+ log(" more than one library.\n");
+ log("\n");
+ log(" -selected\n");
+ log(" only write selected modules. modules must be selected entirely or\n");
+ log(" not at all.\n");
+ log("\n");
+ log("http://www.clifford.at/intersynth/\n");
+ log("\n");
+ }
+ virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing INTERSYNTH backend.\n");
+ log_push();
+
+ std::vector<std::string> libfiles;
+ std::vector<RTLIL::Design*> libs;
+ bool flag_notypes = false;
+ bool selected = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-notypes") {
+ flag_notypes = true;
+ continue;
+ }
+ if (args[argidx] == "-lib" && argidx+1 < args.size()) {
+ libfiles.push_back(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-selected") {
+ selected = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ log("Output filename: %s\n", filename.c_str());
+
+ for (auto filename : libfiles) {
+ std::ifstream f;
+ f.open(filename.c_str());
+ if (f.fail())
+ log_error("Can't open lib file `%s'.\n", filename.c_str());
+ RTLIL::Design *lib = new RTLIL::Design;
+ Frontend::frontend_call(lib, &f, filename, (filename.size() > 3 && filename.substr(filename.size()-3) == ".il") ? "ilang" : "verilog");
+ libs.push_back(lib);
+ }
+
+ if (libs.size() > 0)
+ log_header(design, "Continuing INTERSYNTH backend.\n");
+
+ std::set<std::string> conntypes_code, celltypes_code;
+ std::string netlists_code;
+ CellTypes ct(design);
+
+ for (auto lib : libs)
+ ct.setup_design(lib);
+
+ for (auto module_it : design->modules_)
+ {
+ RTLIL::Module *module = module_it.second;
+ SigMap sigmap(module);
+
+ if (module->get_bool_attribute("\\blackbox"))
+ continue;
+ if (module->memories.size() == 0 && module->processes.size() == 0 && module->cells_.size() == 0)
+ continue;
+
+ if (selected && !design->selected_whole_module(module->name)) {
+ if (design->selected_module(module->name))
+ log_cmd_error("Can't handle partially selected module %s!\n", RTLIL::id2cstr(module->name));
+ continue;
+ }
+
+ log("Generating netlist %s.\n", RTLIL::id2cstr(module->name));
+
+ if (module->memories.size() != 0 || module->processes.size() != 0)
+ log_error("Can't generate a netlist for a module with unprocessed memories or processes!\n");
+
+ std::set<std::string> constcells_code;
+ netlists_code += stringf("# Netlist of module %s\n", RTLIL::id2cstr(module->name));
+ netlists_code += stringf("netlist %s\n", RTLIL::id2cstr(module->name));
+
+ // Module Ports: "std::set<string> celltypes_code" prevents duplicate top level ports
+ for (auto wire_it : module->wires_) {
+ RTLIL::Wire *wire = wire_it.second;
+ if (wire->port_input || wire->port_output) {
+ celltypes_code.insert(stringf("celltype !%s b%d %sPORT\n" "%s %s %d %s PORT\n",
+ RTLIL::id2cstr(wire->name), wire->width, wire->port_input ? "*" : "",
+ wire->port_input ? "input" : "output", RTLIL::id2cstr(wire->name), wire->width, RTLIL::id2cstr(wire->name)));
+ netlists_code += stringf("node %s %s PORT %s\n", RTLIL::id2cstr(wire->name), RTLIL::id2cstr(wire->name),
+ netname(conntypes_code, celltypes_code, constcells_code, sigmap(wire)).c_str());
+ }
+ }
+
+ // Submodules: "std::set<string> celltypes_code" prevents duplicate cell types
+ for (auto cell_it : module->cells_)
+ {
+ RTLIL::Cell *cell = cell_it.second;
+ std::string celltype_code, node_code;
+
+ if (!ct.cell_known(cell->type))
+ log_error("Found unknown cell type %s in module!\n", RTLIL::id2cstr(cell->type));
+
+ celltype_code = stringf("celltype %s", RTLIL::id2cstr(cell->type));
+ node_code = stringf("node %s %s", RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
+ for (auto &port : cell->connections()) {
+ RTLIL::SigSpec sig = sigmap(port.second);
+ if (sig.size() != 0) {
+ conntypes_code.insert(stringf("conntype b%d %d 2 %d\n", sig.size(), sig.size(), sig.size()));
+ celltype_code += stringf(" b%d %s%s", sig.size(), ct.cell_output(cell->type, port.first) ? "*" : "", RTLIL::id2cstr(port.first));
+ node_code += stringf(" %s %s", RTLIL::id2cstr(port.first), netname(conntypes_code, celltypes_code, constcells_code, sig).c_str());
+ }
+ }
+ for (auto &param : cell->parameters) {
+ celltype_code += stringf(" cfg:%d %s", int(param.second.bits.size()), RTLIL::id2cstr(param.first));
+ if (param.second.bits.size() != 32) {
+ node_code += stringf(" %s '", RTLIL::id2cstr(param.first));
+ for (int i = param.second.bits.size()-1; i >= 0; i--)
+ node_code += param.second.bits[i] == RTLIL::S1 ? "1" : "0";
+ } else
+ node_code += stringf(" %s 0x%x", RTLIL::id2cstr(param.first), param.second.as_int());
+ }
+
+ celltypes_code.insert(celltype_code + "\n");
+ netlists_code += node_code + "\n";
+ }
+
+ if (constcells_code.size() > 0)
+ netlists_code += "# constant cells\n";
+ for (auto code : constcells_code)
+ netlists_code += code;
+ netlists_code += "\n";
+ }
+
+ if (!flag_notypes) {
+ *f << stringf("### Connection Types\n");
+ for (auto code : conntypes_code)
+ *f << stringf("%s", code.c_str());
+ *f << stringf("\n### Cell Types\n");
+ for (auto code : celltypes_code)
+ *f << stringf("%s", code.c_str());
+ }
+ *f << stringf("\n### Netlists\n");
+ *f << stringf("%s", netlists_code.c_str());
+
+ for (auto lib : libs)
+ delete lib;
+
+ log_pop();
+ }
+} IntersynthBackend;
+
+PRIVATE_NAMESPACE_END
diff --git a/backends/json/Makefile.inc b/backends/json/Makefile.inc
new file mode 100644
index 00000000..a463daf9
--- /dev/null
+++ b/backends/json/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += backends/json/json.o
+
diff --git a/backends/json/json.cc b/backends/json/json.cc
new file mode 100644
index 00000000..4baffa33
--- /dev/null
+++ b/backends/json/json.cc
@@ -0,0 +1,542 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/rtlil.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
+#include "kernel/cellaigs.h"
+#include "kernel/log.h"
+#include <string>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct JsonWriter
+{
+ std::ostream &f;
+ bool use_selection;
+ bool aig_mode;
+
+ Design *design;
+ Module *module;
+
+ SigMap sigmap;
+ int sigidcounter;
+ dict<SigBit, string> sigids;
+ pool<Aig> aig_models;
+
+ JsonWriter(std::ostream &f, bool use_selection, bool aig_mode) :
+ f(f), use_selection(use_selection), aig_mode(aig_mode) { }
+
+ string get_string(string str)
+ {
+ string newstr = "\"";
+ for (char c : str) {
+ if (c == '\\')
+ newstr += c;
+ newstr += c;
+ }
+ return newstr + "\"";
+ }
+
+ string get_name(IdString name)
+ {
+ return get_string(RTLIL::unescape_id(name));
+ }
+
+ string get_bits(SigSpec sig)
+ {
+ bool first = true;
+ string str = "[";
+ for (auto bit : sigmap(sig)) {
+ str += first ? " " : ", ";
+ first = false;
+ if (sigids.count(bit) == 0) {
+ string &s = sigids[bit];
+ if (bit.wire == nullptr) {
+ if (bit == State::S0) s = "\"0\"";
+ else if (bit == State::S1) s = "\"1\"";
+ else if (bit == State::Sz) s = "\"z\"";
+ else s = "\"x\"";
+ } else
+ s = stringf("%d", sigidcounter++);
+ }
+ str += sigids[bit];
+ }
+ return str + " ]";
+ }
+
+ void write_parameters(const dict<IdString, Const> &parameters, bool for_module=false)
+ {
+ bool first = true;
+ for (auto &param : parameters) {
+ f << stringf("%s\n", first ? "" : ",");
+ f << stringf(" %s%s: ", for_module ? "" : " ", get_name(param.first).c_str());
+ if ((param.second.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) != 0)
+ f << get_string(param.second.decode_string());
+ else if (GetSize(param.second.bits) > 32)
+ f << get_string(param.second.as_string());
+ else
+ f << stringf("%d", param.second.as_int());
+ first = false;
+ }
+ }
+
+ void write_module(Module *module_)
+ {
+ module = module_;
+ log_assert(module->design == design);
+ sigmap.set(module);
+ sigids.clear();
+
+ // reserve 0 and 1 to avoid confusion with "0" and "1"
+ sigidcounter = 2;
+
+ f << stringf(" %s: {\n", get_name(module->name).c_str());
+
+ f << stringf(" \"attributes\": {");
+ write_parameters(module->attributes, /*for_module=*/true);
+ f << stringf("\n },\n");
+
+ f << stringf(" \"ports\": {");
+ bool first = true;
+ for (auto n : module->ports) {
+ Wire *w = module->wire(n);
+ if (use_selection && !module->selected(w))
+ continue;
+ f << stringf("%s\n", first ? "" : ",");
+ f << stringf(" %s: {\n", get_name(n).c_str());
+ f << stringf(" \"direction\": \"%s\",\n", w->port_input ? w->port_output ? "inout" : "input" : "output");
+ f << stringf(" \"bits\": %s\n", get_bits(w).c_str());
+ f << stringf(" }");
+ first = false;
+ }
+ f << stringf("\n },\n");
+
+ f << stringf(" \"cells\": {");
+ first = true;
+ for (auto c : module->cells()) {
+ if (use_selection && !module->selected(c))
+ continue;
+ f << stringf("%s\n", first ? "" : ",");
+ f << stringf(" %s: {\n", get_name(c->name).c_str());
+ f << stringf(" \"hide_name\": %s,\n", c->name[0] == '$' ? "1" : "0");
+ f << stringf(" \"type\": %s,\n", get_name(c->type).c_str());
+ if (aig_mode) {
+ Aig aig(c);
+ if (!aig.name.empty()) {
+ f << stringf(" \"model\": \"%s\",\n", aig.name.c_str());
+ aig_models.insert(aig);
+ }
+ }
+ f << stringf(" \"parameters\": {");
+ write_parameters(c->parameters);
+ f << stringf("\n },\n");
+ f << stringf(" \"attributes\": {");
+ write_parameters(c->attributes);
+ f << stringf("\n },\n");
+ if (c->known()) {
+ f << stringf(" \"port_directions\": {");
+ bool first2 = true;
+ for (auto &conn : c->connections()) {
+ string direction = "output";
+ if (c->input(conn.first))
+ direction = c->output(conn.first) ? "inout" : "input";
+ f << stringf("%s\n", first2 ? "" : ",");
+ f << stringf(" %s: \"%s\"", get_name(conn.first).c_str(), direction.c_str());
+ first2 = false;
+ }
+ f << stringf("\n },\n");
+ }
+ f << stringf(" \"connections\": {");
+ bool first2 = true;
+ for (auto &conn : c->connections()) {
+ f << stringf("%s\n", first2 ? "" : ",");
+ f << stringf(" %s: %s", get_name(conn.first).c_str(), get_bits(conn.second).c_str());
+ first2 = false;
+ }
+ f << stringf("\n }\n");
+ f << stringf(" }");
+ first = false;
+ }
+ f << stringf("\n },\n");
+
+ f << stringf(" \"netnames\": {");
+ first = true;
+ for (auto w : module->wires()) {
+ if (use_selection && !module->selected(w))
+ continue;
+ f << stringf("%s\n", first ? "" : ",");
+ f << stringf(" %s: {\n", get_name(w->name).c_str());
+ f << stringf(" \"hide_name\": %s,\n", w->name[0] == '$' ? "1" : "0");
+ f << stringf(" \"bits\": %s,\n", get_bits(w).c_str());
+ f << stringf(" \"attributes\": {");
+ write_parameters(w->attributes);
+ f << stringf("\n }\n");
+ f << stringf(" }");
+ first = false;
+ }
+ f << stringf("\n }\n");
+
+ f << stringf(" }");
+ }
+
+ void write_design(Design *design_)
+ {
+ design = design_;
+ f << stringf("{\n");
+ f << stringf(" \"creator\": %s,\n", get_string(yosys_version_str).c_str());
+ f << stringf(" \"modules\": {\n");
+ vector<Module*> modules = use_selection ? design->selected_modules() : design->modules();
+ bool first_module = true;
+ for (auto mod : modules) {
+ if (!first_module)
+ f << stringf(",\n");
+ write_module(mod);
+ first_module = false;
+ }
+ f << stringf("\n }");
+ if (!aig_models.empty()) {
+ f << stringf(",\n \"models\": {\n");
+ bool first_model = true;
+ for (auto &aig : aig_models) {
+ if (!first_model)
+ f << stringf(",\n");
+ f << stringf(" \"%s\": [\n", aig.name.c_str());
+ int node_idx = 0;
+ for (auto &node : aig.nodes) {
+ if (node_idx != 0)
+ f << stringf(",\n");
+ f << stringf(" /* %3d */ [ ", node_idx);
+ if (node.portbit >= 0)
+ f << stringf("\"%sport\", \"%s\", %d", node.inverter ? "n" : "",
+ log_id(node.portname), node.portbit);
+ else if (node.left_parent < 0 && node.right_parent < 0)
+ f << stringf("\"%s\"", node.inverter ? "true" : "false");
+ else
+ f << stringf("\"%s\", %d, %d", node.inverter ? "nand" : "and", node.left_parent, node.right_parent);
+ for (auto &op : node.outports)
+ f << stringf(", \"%s\", %d", log_id(op.first), op.second);
+ f << stringf(" ]");
+ node_idx++;
+ }
+ f << stringf("\n ]");
+ first_model = false;
+ }
+ f << stringf("\n }");
+ }
+ f << stringf("\n}\n");
+ }
+};
+
+struct JsonBackend : public Backend {
+ JsonBackend() : Backend("json", "write design to a JSON file") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" write_json [options] [filename]\n");
+ log("\n");
+ log("Write a JSON netlist of the current design.\n");
+ log("\n");
+ log(" -aig\n");
+ log(" include AIG models for the different gate types\n");
+ log("\n");
+ log("\n");
+ log("The general syntax of the JSON output created by this command is as follows:\n");
+ log("\n");
+ log(" {\n");
+ log(" \"modules\": {\n");
+ log(" <module_name>: {\n");
+ log(" \"ports\": {\n");
+ log(" <port_name>: <port_details>,\n");
+ log(" ...\n");
+ log(" },\n");
+ log(" \"cells\": {\n");
+ log(" <cell_name>: <cell_details>,\n");
+ log(" ...\n");
+ log(" },\n");
+ log(" \"netnames\": {\n");
+ log(" <net_name>: <net_details>,\n");
+ log(" ...\n");
+ log(" }\n");
+ log(" }\n");
+ log(" },\n");
+ log(" \"models\": {\n");
+ log(" ...\n");
+ log(" },\n");
+ log(" }\n");
+ log("\n");
+ log("Where <port_details> is:\n");
+ log("\n");
+ log(" {\n");
+ log(" \"direction\": <\"input\" | \"output\" | \"inout\">,\n");
+ log(" \"bits\": <bit_vector>\n");
+ log(" }\n");
+ log("\n");
+ log("And <cell_details> is:\n");
+ log("\n");
+ log(" {\n");
+ log(" \"hide_name\": <1 | 0>,\n");
+ log(" \"type\": <cell_type>,\n");
+ log(" \"parameters\": {\n");
+ log(" <parameter_name>: <parameter_value>,\n");
+ log(" ...\n");
+ log(" },\n");
+ log(" \"attributes\": {\n");
+ log(" <attribute_name>: <attribute_value>,\n");
+ log(" ...\n");
+ log(" },\n");
+ log(" \"port_directions\": {\n");
+ log(" <port_name>: <\"input\" | \"output\" | \"inout\">,\n");
+ log(" ...\n");
+ log(" },\n");
+ log(" \"connections\": {\n");
+ log(" <port_name>: <bit_vector>,\n");
+ log(" ...\n");
+ log(" },\n");
+ log(" }\n");
+ log("\n");
+ log("And <net_details> is:\n");
+ log("\n");
+ log(" {\n");
+ log(" \"hide_name\": <1 | 0>,\n");
+ log(" \"bits\": <bit_vector>\n");
+ log(" }\n");
+ log("\n");
+ log("The \"hide_name\" fields are set to 1 when the name of this cell or net is\n");
+ log("automatically created and is likely not of interest for a regular user.\n");
+ log("\n");
+ log("The \"port_directions\" section is only included for cells for which the\n");
+ log("interface is known.\n");
+ log("\n");
+ log("Module and cell ports and nets can be single bit wide or vectors of multiple\n");
+ log("bits. Each individual signal bit is assigned a unique integer. The <bit_vector>\n");
+ log("values referenced above are vectors of this integers. Signal bits that are\n");
+ log("connected to a constant driver are denoted as string \"0\" or \"1\" instead of\n");
+ log("a number.\n");
+ log("\n");
+ log("For example the following Verilog code:\n");
+ log("\n");
+ log(" module test(input x, y);\n");
+ log(" (* keep *) foo #(.P(42), .Q(1337))\n");
+ log(" foo_inst (.A({x, y}), .B({y, x}), .C({4'd10, {4{x}}}));\n");
+ log(" endmodule\n");
+ log("\n");
+ log("Translates to the following JSON output:\n");
+ log("\n");
+ log(" {\n");
+ log(" \"modules\": {\n");
+ log(" \"test\": {\n");
+ log(" \"ports\": {\n");
+ log(" \"x\": {\n");
+ log(" \"direction\": \"input\",\n");
+ log(" \"bits\": [ 2 ]\n");
+ log(" },\n");
+ log(" \"y\": {\n");
+ log(" \"direction\": \"input\",\n");
+ log(" \"bits\": [ 3 ]\n");
+ log(" }\n");
+ log(" },\n");
+ log(" \"cells\": {\n");
+ log(" \"foo_inst\": {\n");
+ log(" \"hide_name\": 0,\n");
+ log(" \"type\": \"foo\",\n");
+ log(" \"parameters\": {\n");
+ log(" \"Q\": 1337,\n");
+ log(" \"P\": 42\n");
+ log(" },\n");
+ log(" \"attributes\": {\n");
+ log(" \"keep\": 1,\n");
+ log(" \"src\": \"test.v:2\"\n");
+ log(" },\n");
+ log(" \"connections\": {\n");
+ log(" \"C\": [ 2, 2, 2, 2, \"0\", \"1\", \"0\", \"1\" ],\n");
+ log(" \"B\": [ 2, 3 ],\n");
+ log(" \"A\": [ 3, 2 ]\n");
+ log(" }\n");
+ log(" }\n");
+ log(" },\n");
+ log(" \"netnames\": {\n");
+ log(" \"y\": {\n");
+ log(" \"hide_name\": 0,\n");
+ log(" \"bits\": [ 3 ],\n");
+ log(" \"attributes\": {\n");
+ log(" \"src\": \"test.v:1\"\n");
+ log(" }\n");
+ log(" },\n");
+ log(" \"x\": {\n");
+ log(" \"hide_name\": 0,\n");
+ log(" \"bits\": [ 2 ],\n");
+ log(" \"attributes\": {\n");
+ log(" \"src\": \"test.v:1\"\n");
+ log(" }\n");
+ log(" }\n");
+ log(" }\n");
+ log(" }\n");
+ log(" }\n");
+ log(" }\n");
+ log("\n");
+ log("The models are given as And-Inverter-Graphs (AIGs) in the following form:\n");
+ log("\n");
+ log(" \"models\": {\n");
+ log(" <model_name>: [\n");
+ log(" /* 0 */ [ <node-spec> ],\n");
+ log(" /* 1 */ [ <node-spec> ],\n");
+ log(" /* 2 */ [ <node-spec> ],\n");
+ log(" ...\n");
+ log(" ],\n");
+ log(" ...\n");
+ log(" },\n");
+ log("\n");
+ log("The following node-types may be used:\n");
+ log("\n");
+ log(" [ \"port\", <portname>, <bitindex>, <out-list> ]\n");
+ log(" - the value of the specified input port bit\n");
+ log("\n");
+ log(" [ \"nport\", <portname>, <bitindex>, <out-list> ]\n");
+ log(" - the inverted value of the specified input port bit\n");
+ log("\n");
+ log(" [ \"and\", <node-index>, <node-index>, <out-list> ]\n");
+ log(" - the ANDed value of the specified nodes\n");
+ log("\n");
+ log(" [ \"nand\", <node-index>, <node-index>, <out-list> ]\n");
+ log(" - the inverted ANDed value of the specified nodes\n");
+ log("\n");
+ log(" [ \"true\", <out-list> ]\n");
+ log(" - the constant value 1\n");
+ log("\n");
+ log(" [ \"false\", <out-list> ]\n");
+ log(" - the constant value 0\n");
+ log("\n");
+ log("All nodes appear in topological order. I.e. only nodes with smaller indices\n");
+ log("are referenced by \"and\" and \"nand\" nodes.\n");
+ log("\n");
+ log("The optional <out-list> at the end of a node specification is a list of\n");
+ log("output portname and bitindex pairs, specifying the outputs driven by this node.\n");
+ log("\n");
+ log("For example, the following is the model for a 3-input 3-output $reduce_and cell\n");
+ log("inferred by the following code:\n");
+ log("\n");
+ log(" module test(input [2:0] in, output [2:0] out);\n");
+ log(" assign in = &out;\n");
+ log(" endmodule\n");
+ log("\n");
+ log(" \"$reduce_and:3U:3\": [\n");
+ log(" /* 0 */ [ \"port\", \"A\", 0 ],\n");
+ log(" /* 1 */ [ \"port\", \"A\", 1 ],\n");
+ log(" /* 2 */ [ \"and\", 0, 1 ],\n");
+ log(" /* 3 */ [ \"port\", \"A\", 2 ],\n");
+ log(" /* 4 */ [ \"and\", 2, 3, \"Y\", 0 ],\n");
+ log(" /* 5 */ [ \"false\", \"Y\", 1, \"Y\", 2 ]\n");
+ log(" ]\n");
+ log("\n");
+ log("Future version of Yosys might add support for additional fields in the JSON\n");
+ log("format. A program processing this format must ignore all unknown fields.\n");
+ log("\n");
+ }
+ virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool aig_mode = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-aig") {
+ aig_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ log_header(design, "Executing JSON backend.\n");
+
+ JsonWriter json_writer(*f, false, aig_mode);
+ json_writer.write_design(design);
+ }
+} JsonBackend;
+
+struct JsonPass : public Pass {
+ JsonPass() : Pass("json", "write design in JSON format") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" json [options] [selection]\n");
+ log("\n");
+ log("Write a JSON netlist of all selected objects.\n");
+ log("\n");
+ log(" -o <filename>\n");
+ log(" write to the specified file.\n");
+ log("\n");
+ log(" -aig\n");
+ log(" also include AIG models for the different gate types\n");
+ log("\n");
+ log("See 'help write_json' for a description of the JSON format used.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::string filename;
+ bool aig_mode = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-o" && argidx+1 < args.size()) {
+ filename = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-aig") {
+ aig_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ std::ostream *f;
+ std::stringstream buf;
+
+ if (!filename.empty()) {
+ std::ofstream *ff = new std::ofstream;
+ ff->open(filename.c_str(), std::ofstream::trunc);
+ if (ff->fail()) {
+ delete ff;
+ log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
+ }
+ f = ff;
+ } else {
+ f = &buf;
+ }
+
+ JsonWriter json_writer(*f, true, aig_mode);
+ json_writer.write_design(design);
+
+ if (!filename.empty()) {
+ delete f;
+ } else {
+ log("%s", buf.str().c_str());
+ }
+ }
+} JsonPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/backends/smt2/.gitignore b/backends/smt2/.gitignore
new file mode 100644
index 00000000..313ea0a1
--- /dev/null
+++ b/backends/smt2/.gitignore
@@ -0,0 +1 @@
+test_cells
diff --git a/backends/smt2/Makefile.inc b/backends/smt2/Makefile.inc
new file mode 100644
index 00000000..eacda273
--- /dev/null
+++ b/backends/smt2/Makefile.inc
@@ -0,0 +1,16 @@
+
+OBJS += backends/smt2/smt2.o
+
+ifneq ($(CONFIG),mxe)
+ifneq ($(CONFIG),emcc)
+TARGETS += yosys-smtbmc
+
+yosys-smtbmc: backends/smt2/smtbmc.py
+ $(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(__file__) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' < $< > $@.new
+ $(Q) chmod +x $@.new
+ $(Q) mv $@.new $@
+
+$(eval $(call add_share_file,share/python3,backends/smt2/smtio.py))
+endif
+endif
+
diff --git a/backends/smt2/example.v b/backends/smt2/example.v
new file mode 100644
index 00000000..b195266e
--- /dev/null
+++ b/backends/smt2/example.v
@@ -0,0 +1,11 @@
+module main(input clk);
+ reg [3:0] counter = 0;
+ always @(posedge clk) begin
+ if (counter == 10)
+ counter <= 0;
+ else
+ counter <= counter + 1;
+ end
+ assert property (counter != 15);
+ // assert property (counter <= 10);
+endmodule
diff --git a/backends/smt2/example.ys b/backends/smt2/example.ys
new file mode 100644
index 00000000..6fccb344
--- /dev/null
+++ b/backends/smt2/example.ys
@@ -0,0 +1,3 @@
+read_verilog -formal example.v
+hierarchy; proc; opt; memory -nordff -nomap; opt -fast
+write_smt2 -bv -mem -wires example.smt2
diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc
new file mode 100644
index 00000000..ddac6900
--- /dev/null
+++ b/backends/smt2/smt2.cc
@@ -0,0 +1,1096 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/rtlil.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
+#include "kernel/log.h"
+#include <string>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct Smt2Worker
+{
+ CellTypes ct;
+ SigMap sigmap;
+ RTLIL::Module *module;
+ bool bvmode, memmode, wiresmode, verbose;
+ int idcounter;
+
+ std::vector<std::string> decls, trans, hier;
+ std::map<RTLIL::SigBit, RTLIL::Cell*> bit_driver;
+ std::set<RTLIL::Cell*> exported_cells, hiercells, hiercells_queue;
+ pool<Cell*> recursive_cells, registers;
+
+ std::map<RTLIL::SigBit, std::pair<int, int>> fcache;
+ std::map<Cell*, int> memarrays;
+ std::map<int, int> bvsizes;
+ dict<IdString, char*> ids;
+
+ const char *get_id(IdString n)
+ {
+ if (ids.count(n) == 0) {
+ std::string str = log_id(n);
+ for (int i = 0; i < GetSize(str); i++) {
+ if (str[i] == '\\')
+ str[i] = '/';
+ }
+ ids[n] = strdup(str.c_str());
+ }
+ return ids[n];
+ }
+
+ template<typename T>
+ const char *get_id(T *obj) {
+ return get_id(obj->name);
+ }
+
+ Smt2Worker(RTLIL::Module *module, bool bvmode, bool memmode, bool wiresmode, bool verbose) :
+ ct(module->design), sigmap(module), module(module), bvmode(bvmode), memmode(memmode),
+ wiresmode(wiresmode), verbose(verbose), idcounter(0)
+ {
+ decls.push_back(stringf("(declare-sort |%s_s| 0)\n", get_id(module)));
+ decls.push_back(stringf("(declare-fun |%s_is| (|%s_s|) Bool)\n", get_id(module), get_id(module)));
+
+ for (auto cell : module->cells())
+ for (auto &conn : cell->connections()) {
+ bool is_input = ct.cell_input(cell->type, conn.first);
+ bool is_output = ct.cell_output(cell->type, conn.first);
+ if (is_output && !is_input)
+ for (auto bit : sigmap(conn.second)) {
+ if (bit_driver.count(bit))
+ log_error("Found multiple drivers for %s.\n", log_signal(bit));
+ bit_driver[bit] = cell;
+ }
+ else if (is_output || !is_input)
+ log_error("Unsupported or unknown directionality on port %s of cell %s.%s (%s).\n",
+ log_id(conn.first), log_id(module), log_id(cell), log_id(cell->type));
+ }
+ }
+
+ ~Smt2Worker()
+ {
+ for (auto &it : ids)
+ free(it.second);
+ ids.clear();
+ }
+
+ const char *get_id(Module *m)
+ {
+ return get_id(m->name);
+ }
+
+ const char *get_id(Cell *c)
+ {
+ return get_id(c->name);
+ }
+
+ const char *get_id(Wire *w)
+ {
+ return get_id(w->name);
+ }
+
+ void register_bool(RTLIL::SigBit bit, int id)
+ {
+ if (verbose) log("%*s-> register_bool: %s %d\n", 2+2*GetSize(recursive_cells), "",
+ log_signal(bit), id);
+
+ sigmap.apply(bit);
+ log_assert(fcache.count(bit) == 0);
+ fcache[bit] = std::pair<int, int>(id, -1);
+ }
+
+ void register_bv(RTLIL::SigSpec sig, int id)
+ {
+ if (verbose) log("%*s-> register_bv: %s %d\n", 2+2*GetSize(recursive_cells), "",
+ log_signal(sig), id);
+
+ log_assert(bvmode);
+ sigmap.apply(sig);
+
+ log_assert(bvsizes.count(id) == 0);
+ bvsizes[id] = GetSize(sig);
+
+ for (int i = 0; i < GetSize(sig); i++) {
+ log_assert(fcache.count(sig[i]) == 0);
+ fcache[sig[i]] = std::pair<int, int>(id, i);
+ }
+ }
+
+ void register_boolvec(RTLIL::SigSpec sig, int id)
+ {
+ if (verbose) log("%*s-> register_boolvec: %s %d\n", 2+2*GetSize(recursive_cells), "",
+ log_signal(sig), id);
+
+ log_assert(bvmode);
+ sigmap.apply(sig);
+ register_bool(sig[0], id);
+
+ for (int i = 1; i < GetSize(sig); i++)
+ sigmap.add(sig[i], RTLIL::State::S0);
+ }
+
+ std::string get_bool(RTLIL::SigBit bit, const char *state_name = "state")
+ {
+ sigmap.apply(bit);
+
+ if (bit.wire == nullptr)
+ return bit == RTLIL::State::S1 ? "true" : "false";
+
+ if (bit_driver.count(bit))
+ export_cell(bit_driver.at(bit));
+ sigmap.apply(bit);
+
+ if (fcache.count(bit) == 0) {
+ if (verbose) log("%*s-> external bool: %s\n", 2+2*GetSize(recursive_cells), "",
+ log_signal(bit));
+ decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n",
+ get_id(module), idcounter, get_id(module), log_signal(bit)));
+ register_bool(bit, idcounter++);
+ }
+
+ auto f = fcache.at(bit);
+ if (f.second >= 0)
+ return stringf("(= ((_ extract %d %d) (|%s#%d| %s)) #b1)", f.second, f.second, get_id(module), f.first, state_name);
+ return stringf("(|%s#%d| %s)", get_id(module), f.first, state_name);
+ }
+
+ std::string get_bool(RTLIL::SigSpec sig, const char *state_name = "state")
+ {
+ return get_bool(sig.as_bit(), state_name);
+ }
+
+ std::string get_bv(RTLIL::SigSpec sig, const char *state_name = "state")
+ {
+ log_assert(bvmode);
+ sigmap.apply(sig);
+
+ std::vector<std::string> subexpr;
+
+ SigSpec orig_sig;
+ while (orig_sig != sig) {
+ for (auto bit : sig)
+ if (bit_driver.count(bit))
+ export_cell(bit_driver.at(bit));
+ orig_sig = sig;
+ sigmap.apply(sig);
+ }
+
+ for (int i = 0, j = 1; i < GetSize(sig); i += j, j = 1)
+ {
+ if (sig[i].wire == nullptr) {
+ while (i+j < GetSize(sig) && sig[i+j].wire == nullptr) j++;
+ subexpr.push_back("#b");
+ for (int k = i+j-1; k >= i; k--)
+ subexpr.back() += sig[k] == RTLIL::State::S1 ? "1" : "0";
+ continue;
+ }
+
+ if (fcache.count(sig[i]) && fcache.at(sig[i]).second == -1) {
+ subexpr.push_back(stringf("(ite %s #b1 #b0)", get_bool(sig[i], state_name).c_str()));
+ continue;
+ }
+
+ if (fcache.count(sig[i])) {
+ auto t1 = fcache.at(sig[i]);
+ while (i+j < GetSize(sig)) {
+ if (fcache.count(sig[i+j]) == 0)
+ break;
+ auto t2 = fcache.at(sig[i+j]);
+ if (t1.first != t2.first)
+ break;
+ if (t1.second+j != t2.second)
+ break;
+ j++;
+ }
+ if (t1.second == 0 && j == bvsizes.at(t1.first))
+ subexpr.push_back(stringf("(|%s#%d| %s)", get_id(module), t1.first, state_name));
+ else
+ subexpr.push_back(stringf("((_ extract %d %d) (|%s#%d| %s))",
+ t1.second + j - 1, t1.second, get_id(module), t1.first, state_name));
+ continue;
+ }
+
+ std::set<RTLIL::SigBit> seen_bits = { sig[i] };
+ while (i+j < GetSize(sig) && sig[i+j].wire && !fcache.count(sig[i+j]) && !seen_bits.count(sig[i+j]))
+ seen_bits.insert(sig[i+j]), j++;
+
+ if (verbose) log("%*s-> external bv: %s\n", 2+2*GetSize(recursive_cells), "",
+ log_signal(sig.extract(i, j)));
+ for (auto bit : sig.extract(i, j))
+ log_assert(bit_driver.count(bit) == 0);
+ decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n",
+ get_id(module), idcounter, get_id(module), j, log_signal(sig.extract(i, j))));
+ subexpr.push_back(stringf("(|%s#%d| %s)", get_id(module), idcounter, state_name));
+ register_bv(sig.extract(i, j), idcounter++);
+ }
+
+ if (GetSize(subexpr) > 1) {
+ std::string expr = "", end_str = "";
+ for (int i = GetSize(subexpr)-1; i >= 0; i--) {
+ if (i > 0) expr += " (concat", end_str += ")";
+ expr += " " + subexpr[i];
+ }
+ return expr.substr(1) + end_str;
+ } else {
+ log_assert(GetSize(subexpr) == 1);
+ return subexpr[0];
+ }
+ }
+
+ void export_gate(RTLIL::Cell *cell, std::string expr)
+ {
+ RTLIL::SigBit bit = sigmap(cell->getPort("\\Y").as_bit());
+ std::string processed_expr;
+
+ for (char ch : expr) {
+ if (ch == 'A') processed_expr += get_bool(cell->getPort("\\A"));
+ else if (ch == 'B') processed_expr += get_bool(cell->getPort("\\B"));
+ else if (ch == 'C') processed_expr += get_bool(cell->getPort("\\C"));
+ else if (ch == 'D') processed_expr += get_bool(cell->getPort("\\D"));
+ else if (ch == 'S') processed_expr += get_bool(cell->getPort("\\S"));
+ else processed_expr += ch;
+ }
+
+ if (verbose)
+ log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", log_id(cell));
+
+ decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool %s) ; %s\n",
+ get_id(module), idcounter, get_id(module), processed_expr.c_str(), log_signal(bit)));
+ register_bool(bit, idcounter++);
+ recursive_cells.erase(cell);
+ }
+
+ void export_bvop(RTLIL::Cell *cell, std::string expr, char type = 0)
+ {
+ RTLIL::SigSpec sig_a, sig_b;
+ RTLIL::SigSpec sig_y = sigmap(cell->getPort("\\Y"));
+ bool is_signed = cell->getParam("\\A_SIGNED").as_bool();
+ int width = GetSize(sig_y);
+
+ if (type == 's' || type == 'd' || type == 'b') {
+ width = max(width, GetSize(cell->getPort("\\A")));
+ width = max(width, GetSize(cell->getPort("\\B")));
+ }
+
+ if (cell->hasPort("\\A")) {
+ sig_a = cell->getPort("\\A");
+ sig_a.extend_u0(width, is_signed);
+ }
+
+ if (cell->hasPort("\\B")) {
+ sig_b = cell->getPort("\\B");
+ sig_b.extend_u0(width, is_signed && !(type == 's'));
+ }
+
+ std::string processed_expr;
+
+ for (char ch : expr) {
+ if (ch == 'A') processed_expr += get_bv(sig_a);
+ else if (ch == 'B') processed_expr += get_bv(sig_b);
+ else if (ch == 'L') processed_expr += is_signed ? "a" : "l";
+ else if (ch == 'U') processed_expr += is_signed ? "s" : "u";
+ else processed_expr += ch;
+ }
+
+ if (width != GetSize(sig_y) && type != 'b')
+ processed_expr = stringf("((_ extract %d 0) %s)", GetSize(sig_y)-1, processed_expr.c_str());
+
+ if (verbose)
+ log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", log_id(cell));
+
+ if (type == 'b') {
+ decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool %s) ; %s\n",
+ get_id(module), idcounter, get_id(module), processed_expr.c_str(), log_signal(sig_y)));
+ register_boolvec(sig_y, idcounter++);
+ } else {
+ decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
+ get_id(module), idcounter, get_id(module), GetSize(sig_y), processed_expr.c_str(), log_signal(sig_y)));
+ register_bv(sig_y, idcounter++);
+ }
+
+ recursive_cells.erase(cell);
+ }
+
+ void export_reduce(RTLIL::Cell *cell, std::string expr, bool identity_val)
+ {
+ RTLIL::SigSpec sig_y = sigmap(cell->getPort("\\Y"));
+ std::string processed_expr;
+
+ for (char ch : expr)
+ if (ch == 'A' || ch == 'B') {
+ RTLIL::SigSpec sig = sigmap(cell->getPort(stringf("\\%c", ch)));
+ for (auto bit : sig)
+ processed_expr += " " + get_bool(bit);
+ if (GetSize(sig) == 1)
+ processed_expr += identity_val ? " true" : " false";
+ } else
+ processed_expr += ch;
+
+ if (verbose)
+ log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", log_id(cell));
+
+ decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool %s) ; %s\n",
+ get_id(module), idcounter, get_id(module), processed_expr.c_str(), log_signal(sig_y)));
+ register_boolvec(sig_y, idcounter++);
+ recursive_cells.erase(cell);
+ }
+
+ void export_cell(RTLIL::Cell *cell)
+ {
+ if (verbose)
+ log("%*s=> export_cell %s (%s) [%s]\n", 2+2*GetSize(recursive_cells), "",
+ log_id(cell), log_id(cell->type), exported_cells.count(cell) ? "old" : "new");
+
+ if (recursive_cells.count(cell))
+ log_error("Found logic loop in module %s! See cell %s.\n", get_id(module), get_id(cell));
+
+ if (exported_cells.count(cell))
+ return;
+
+ exported_cells.insert(cell);
+ recursive_cells.insert(cell);
+
+ if (cell->type == "$initstate")
+ {
+ SigBit bit = sigmap(cell->getPort("\\Y").as_bit());
+ decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool (|%s_is| state)) ; %s\n",
+ get_id(module), idcounter, get_id(module), get_id(module), log_signal(bit)));
+ register_bool(bit, idcounter++);
+ recursive_cells.erase(cell);
+ return;
+ }
+
+ if (cell->type.in("$_FF_", "$_DFF_P_", "$_DFF_N_"))
+ {
+ registers.insert(cell);
+ decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n",
+ get_id(module), idcounter, get_id(module), log_signal(cell->getPort("\\Q"))));
+ register_bool(cell->getPort("\\Q"), idcounter++);
+ recursive_cells.erase(cell);
+ return;
+ }
+
+ if (cell->type == "$_BUF_") return export_gate(cell, "A");
+ if (cell->type == "$_NOT_") return export_gate(cell, "(not A)");
+ if (cell->type == "$_AND_") return export_gate(cell, "(and A B)");
+ if (cell->type == "$_NAND_") return export_gate(cell, "(not (and A B))");
+ if (cell->type == "$_OR_") return export_gate(cell, "(or A B)");
+ if (cell->type == "$_NOR_") return export_gate(cell, "(not (or A B))");
+ if (cell->type == "$_XOR_") return export_gate(cell, "(xor A B)");
+ if (cell->type == "$_XNOR_") return export_gate(cell, "(not (xor A B))");
+ if (cell->type == "$_MUX_") return export_gate(cell, "(ite S B A)");
+ if (cell->type == "$_AOI3_") return export_gate(cell, "(not (or (and A B) C))");
+ if (cell->type == "$_OAI3_") return export_gate(cell, "(not (and (or A B) C))");
+ if (cell->type == "$_AOI4_") return export_gate(cell, "(not (or (and A B) (and C D)))");
+ if (cell->type == "$_OAI4_") return export_gate(cell, "(not (and (or A B) (or C D)))");
+
+ // FIXME: $lut
+
+ if (bvmode)
+ {
+ if (cell->type.in("$ff", "$dff"))
+ {
+ registers.insert(cell);
+ decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n",
+ get_id(module), idcounter, get_id(module), GetSize(cell->getPort("\\Q")), log_signal(cell->getPort("\\Q"))));
+ register_bv(cell->getPort("\\Q"), idcounter++);
+ recursive_cells.erase(cell);
+ return;
+ }
+
+ if (cell->type.in("$anyconst", "$anyseq"))
+ {
+ registers.insert(cell);
+ decls.push_back(stringf("; yosys-smt2-%s %s#%d %s\n", cell->type.c_str() + 1, get_id(module), idcounter,
+ cell->attributes.count("\\src") ? cell->attributes.at("\\src").decode_string().c_str() : get_id(cell)));
+ decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n",
+ get_id(module), idcounter, get_id(module), GetSize(cell->getPort("\\Y")), log_signal(cell->getPort("\\Y"))));
+ register_bv(cell->getPort("\\Y"), idcounter++);
+ recursive_cells.erase(cell);
+ return;
+ }
+
+ if (cell->type == "$and") return export_bvop(cell, "(bvand A B)");
+ if (cell->type == "$or") return export_bvop(cell, "(bvor A B)");
+ if (cell->type == "$xor") return export_bvop(cell, "(bvxor A B)");
+ if (cell->type == "$xnor") return export_bvop(cell, "(bvxnor A B)");
+
+ if (cell->type == "$shl") return export_bvop(cell, "(bvshl A B)", 's');
+ if (cell->type == "$shr") return export_bvop(cell, "(bvlshr A B)", 's');
+ if (cell->type == "$sshl") return export_bvop(cell, "(bvshl A B)", 's');
+ if (cell->type == "$sshr") return export_bvop(cell, "(bvLshr A B)", 's');
+
+ if (cell->type.in("$shift", "$shiftx")) {
+ if (cell->getParam("\\B_SIGNED").as_bool()) {
+ /* FIXME */
+ } else {
+ return export_bvop(cell, "(bvlshr A B)", 's');
+ }
+ }
+
+ if (cell->type == "$lt") return export_bvop(cell, "(bvUlt A B)", 'b');
+ if (cell->type == "$le") return export_bvop(cell, "(bvUle A B)", 'b');
+ if (cell->type == "$ge") return export_bvop(cell, "(bvUge A B)", 'b');
+ if (cell->type == "$gt") return export_bvop(cell, "(bvUgt A B)", 'b');
+
+ if (cell->type == "$ne") return export_bvop(cell, "(distinct A B)", 'b');
+ if (cell->type == "$nex") return export_bvop(cell, "(distinct A B)", 'b');
+ if (cell->type == "$eq") return export_bvop(cell, "(= A B)", 'b');
+ if (cell->type == "$eqx") return export_bvop(cell, "(= A B)", 'b');
+
+ if (cell->type == "$not") return export_bvop(cell, "(bvnot A)");
+ if (cell->type == "$pos") return export_bvop(cell, "A");
+ if (cell->type == "$neg") return export_bvop(cell, "(bvneg A)");
+
+ if (cell->type == "$add") return export_bvop(cell, "(bvadd A B)");
+ if (cell->type == "$sub") return export_bvop(cell, "(bvsub A B)");
+ if (cell->type == "$mul") return export_bvop(cell, "(bvmul A B)");
+ if (cell->type == "$div") return export_bvop(cell, "(bvUdiv A B)", 'd');
+ if (cell->type == "$mod") return export_bvop(cell, "(bvUrem A B)", 'd');
+
+ if (cell->type == "$reduce_and") return export_reduce(cell, "(and A)", true);
+ if (cell->type == "$reduce_or") return export_reduce(cell, "(or A)", false);
+ if (cell->type == "$reduce_xor") return export_reduce(cell, "(xor A)", false);
+ if (cell->type == "$reduce_xnor") return export_reduce(cell, "(not (xor A))", false);
+ if (cell->type == "$reduce_bool") return export_reduce(cell, "(or A)", false);
+
+ if (cell->type == "$logic_not") return export_reduce(cell, "(not (or A))", false);
+ if (cell->type == "$logic_and") return export_reduce(cell, "(and (or A) (or B))", false);
+ if (cell->type == "$logic_or") return export_reduce(cell, "(or A B)", false);
+
+ if (cell->type == "$mux" || cell->type == "$pmux")
+ {
+ int width = GetSize(cell->getPort("\\Y"));
+ std::string processed_expr = get_bv(cell->getPort("\\A"));
+
+ RTLIL::SigSpec sig_b = cell->getPort("\\B");
+ RTLIL::SigSpec sig_s = cell->getPort("\\S");
+ get_bv(sig_b);
+ get_bv(sig_s);
+
+ for (int i = 0; i < GetSize(sig_s); i++)
+ processed_expr = stringf("(ite %s %s %s)", get_bool(sig_s[i]).c_str(),
+ get_bv(sig_b.extract(i*width, width)).c_str(), processed_expr.c_str());
+
+ if (verbose)
+ log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", log_id(cell));
+
+ RTLIL::SigSpec sig = sigmap(cell->getPort("\\Y"));
+ decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
+ get_id(module), idcounter, get_id(module), width, processed_expr.c_str(), log_signal(sig)));
+ register_bv(sig, idcounter++);
+ recursive_cells.erase(cell);
+ return;
+ }
+
+ // FIXME: $slice $concat
+ }
+
+ if (memmode && cell->type == "$mem")
+ {
+ int arrayid = idcounter++;
+ memarrays[cell] = arrayid;
+
+ int abits = cell->getParam("\\ABITS").as_int();
+ int width = cell->getParam("\\WIDTH").as_int();
+ int rd_ports = cell->getParam("\\RD_PORTS").as_int();
+
+ decls.push_back(stringf("(declare-fun |%s#%d#0| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
+ get_id(module), arrayid, get_id(module), abits, width, get_id(cell)));
+
+ decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d\n", get_id(cell), abits, width, rd_ports));
+ decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) (|%s#%d#0| state))\n",
+ get_id(module), get_id(cell), get_id(module), abits, width, get_id(module), arrayid));
+
+ for (int i = 0; i < rd_ports; i++)
+ {
+ SigSpec addr_sig = cell->getPort("\\RD_ADDR").extract(abits*i, abits);
+ SigSpec data_sig = cell->getPort("\\RD_DATA").extract(width*i, width);
+ std::string addr = get_bv(addr_sig);
+
+ if (cell->getParam("\\RD_CLK_ENABLE").extract(i).as_bool())
+ log_error("Read port %d (%s) of memory %s.%s is clocked. This is not supported by \"write_smt2\"! "
+ "Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(data_sig), log_id(cell), log_id(module));
+
+ decls.push_back(stringf("(define-fun |%s_m:%d %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
+ get_id(module), i, get_id(cell), get_id(module), abits, addr.c_str(), log_signal(addr_sig)));
+
+ decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) (select (|%s#%d#0| state) %s)) ; %s\n",
+ get_id(module), idcounter, get_id(module), width, get_id(module), arrayid, addr.c_str(), log_signal(data_sig)));
+ register_bv(data_sig, idcounter++);
+ }
+
+ registers.insert(cell);
+ recursive_cells.erase(cell);
+ return;
+ }
+
+ Module *m = module->design->module(cell->type);
+
+ if (m != nullptr)
+ {
+ decls.push_back(stringf("; yosys-smt2-cell %s %s\n", get_id(cell->type), get_id(cell->name)));
+ string cell_state = stringf("(|%s_h %s| state)", get_id(module), get_id(cell->name));
+
+ for (auto &conn : cell->connections())
+ {
+ Wire *w = m->wire(conn.first);
+ SigSpec sig = sigmap(conn.second);
+
+ if (w->port_output && !w->port_input) {
+ if (GetSize(w) > 1) {
+ if (bvmode) {
+ decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n",
+ get_id(module), idcounter, get_id(module), GetSize(w), log_signal(sig)));
+ register_bv(sig, idcounter++);
+ } else {
+ for (int i = 0; i < GetSize(w); i++) {
+ decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n",
+ get_id(module), idcounter, get_id(module), log_signal(sig[i])));
+ register_bool(sig[i], idcounter++);
+ }
+ }
+ } else {
+ decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n",
+ get_id(module), idcounter, get_id(module), log_signal(sig)));
+ register_bool(sig, idcounter++);
+ }
+ }
+ }
+
+ decls.push_back(stringf("(declare-fun |%s_h %s| (|%s_s|) |%s_s|)\n",
+ get_id(module), get_id(cell->name), get_id(module), get_id(cell->type)));
+
+ hiercells.insert(cell);
+ hiercells_queue.insert(cell);
+ recursive_cells.erase(cell);
+ return;
+ }
+
+ log_error("Unsupported cell type %s for cell %s.%s.\n",
+ log_id(cell->type), log_id(module), log_id(cell));
+ }
+
+ void run()
+ {
+ if (verbose) log("=> export logic driving outputs\n");
+
+ pool<SigBit> reg_bits;
+ for (auto cell : module->cells())
+ if (cell->type.in("$ff", "$dff", "$_FF_", "$_DFF_P_", "$_DFF_N_")) {
+ // not using sigmap -- we want the net directly at the dff output
+ for (auto bit : cell->getPort("\\Q"))
+ reg_bits.insert(bit);
+ }
+
+ for (auto wire : module->wires()) {
+ bool is_register = false;
+ for (auto bit : SigSpec(wire))
+ if (reg_bits.count(bit))
+ is_register = true;
+ if (wire->port_id || is_register || wire->get_bool_attribute("\\keep") || (wiresmode && wire->name[0] == '\\')) {
+ RTLIL::SigSpec sig = sigmap(wire);
+ if (wire->port_input)
+ decls.push_back(stringf("; yosys-smt2-input %s %d\n", get_id(wire), wire->width));
+ if (wire->port_output)
+ decls.push_back(stringf("; yosys-smt2-output %s %d\n", get_id(wire), wire->width));
+ if (is_register)
+ decls.push_back(stringf("; yosys-smt2-register %s %d\n", get_id(wire), wire->width));
+ if (wire->get_bool_attribute("\\keep") || (wiresmode && wire->name[0] == '\\'))
+ decls.push_back(stringf("; yosys-smt2-wire %s %d\n", get_id(wire), wire->width));
+ if (bvmode && GetSize(sig) > 1) {
+ decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) (_ BitVec %d) %s)\n",
+ get_id(module), get_id(wire), get_id(module), GetSize(sig), get_bv(sig).c_str()));
+ } else {
+ for (int i = 0; i < GetSize(sig); i++)
+ if (GetSize(sig) > 1)
+ decls.push_back(stringf("(define-fun |%s_n %s %d| ((state |%s_s|)) Bool %s)\n",
+ get_id(module), get_id(wire), i, get_id(module), get_bool(sig[i]).c_str()));
+ else
+ decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) Bool %s)\n",
+ get_id(module), get_id(wire), get_id(module), get_bool(sig[i]).c_str()));
+ }
+ }
+ }
+
+ if (verbose) log("=> export logic associated with the initial state\n");
+
+ vector<string> init_list;
+ for (auto wire : module->wires())
+ if (wire->attributes.count("\\init")) {
+ RTLIL::SigSpec sig = sigmap(wire);
+ Const val = wire->attributes.at("\\init");
+ val.bits.resize(GetSize(sig));
+ if (bvmode && GetSize(sig) > 1) {
+ init_list.push_back(stringf("(= %s #b%s) ; %s", get_bv(sig).c_str(), val.as_string().c_str(), get_id(wire)));
+ } else {
+ for (int i = 0; i < GetSize(sig); i++)
+ init_list.push_back(stringf("(= %s %s) ; %s", get_bool(sig[i]).c_str(), val.bits[i] == State::S1 ? "true" : "false", get_id(wire)));
+ }
+ }
+
+ if (verbose) log("=> export logic driving asserts\n");
+
+ vector<string> assert_list, assume_list;
+ for (auto cell : module->cells())
+ if (cell->type.in("$assert", "$assume")) {
+ string name_a = get_bool(cell->getPort("\\A"));
+ string name_en = get_bool(cell->getPort("\\EN"));
+ decls.push_back(stringf("; yosys-smt2-%s %s#%d %s\n", cell->type.c_str() + 1, get_id(module), idcounter,
+ cell->attributes.count("\\src") ? cell->attributes.at("\\src").decode_string().c_str() : get_id(cell)));
+ decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool (or %s (not %s))) ; %s\n",
+ get_id(module), idcounter, get_id(module), name_a.c_str(), name_en.c_str(), get_id(cell)));
+ if (cell->type == "$assert")
+ assert_list.push_back(stringf("(|%s#%d| state)", get_id(module), idcounter++));
+ else
+ assume_list.push_back(stringf("(|%s#%d| state)", get_id(module), idcounter++));
+ }
+
+ for (int iter = 1; !registers.empty(); iter++)
+ {
+ pool<Cell*> this_regs;
+ this_regs.swap(registers);
+
+ if (verbose) log("=> export logic driving registers [iteration %d]\n", iter);
+
+ for (auto cell : this_regs)
+ {
+ if (cell->type.in("$_FF_", "$_DFF_P_", "$_DFF_N_"))
+ {
+ std::string expr_d = get_bool(cell->getPort("\\D"));
+ std::string expr_q = get_bool(cell->getPort("\\Q"), "next_state");
+ trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort("\\Q"))));
+ }
+
+ if (cell->type.in("$ff", "$dff"))
+ {
+ std::string expr_d = get_bv(cell->getPort("\\D"));
+ std::string expr_q = get_bv(cell->getPort("\\Q"), "next_state");
+ trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort("\\Q"))));
+ }
+
+ if (cell->type == "$anyconst")
+ {
+ std::string expr_d = get_bv(cell->getPort("\\Y"));
+ std::string expr_q = get_bv(cell->getPort("\\Y"), "next_state");
+ trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort("\\Y"))));
+ }
+
+ if (cell->type == "$mem")
+ {
+ int arrayid = memarrays.at(cell);
+
+ int abits = cell->getParam("\\ABITS").as_int();
+ int width = cell->getParam("\\WIDTH").as_int();
+ int wr_ports = cell->getParam("\\WR_PORTS").as_int();
+
+ for (int i = 0; i < wr_ports; i++)
+ {
+ std::string addr = get_bv(cell->getPort("\\WR_ADDR").extract(abits*i, abits));
+ std::string data = get_bv(cell->getPort("\\WR_DATA").extract(width*i, width));
+ std::string mask = get_bv(cell->getPort("\\WR_EN").extract(width*i, width));
+
+ data = stringf("(bvor (bvand %s %s) (bvand (select (|%s#%d#%d| state) %s) (bvnot %s)))",
+ data.c_str(), mask.c_str(), get_id(module), arrayid, i, addr.c_str(), mask.c_str());
+
+ decls.push_back(stringf("(define-fun |%s#%d#%d| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) "
+ "(store (|%s#%d#%d| state) %s %s)) ; %s\n",
+ get_id(module), arrayid, i+1, get_id(module), abits, width,
+ get_id(module), arrayid, i, addr.c_str(), data.c_str(), get_id(cell)));
+ }
+
+ std::string expr_d = stringf("(|%s#%d#%d| state)", get_id(module), arrayid, wr_ports);
+ std::string expr_q = stringf("(|%s#%d#0| next_state)", get_id(module), arrayid);
+ trans.push_back(stringf(" (= %s %s) ; %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell)));
+
+ Const init_data = cell->getParam("\\INIT");
+ int memsize = cell->getParam("\\SIZE").as_int();
+
+ for (int i = 0; i < memsize; i++)
+ {
+ if (i*width >= GetSize(init_data))
+ break;
+
+ Const initword = init_data.extract(i*width, width, State::Sx);
+ bool gen_init_constr = false;
+
+ for (auto bit : initword.bits)
+ if (bit == State::S0 || bit == State::S1)
+ gen_init_constr = true;
+
+ if (gen_init_constr) {
+ init_list.push_back(stringf("(= (select (|%s#%d#0| state) #b%s) #b%s) ; %s[%d]",
+ get_id(module), arrayid, Const(i, abits).as_string().c_str(),
+ initword.as_string().c_str(), get_id(cell), i));
+ }
+ }
+ }
+ }
+ }
+
+ if (verbose) log("=> export logic driving hierarchical cells\n");
+
+ while (!hiercells_queue.empty())
+ {
+ std::set<RTLIL::Cell*> queue;
+ queue.swap(hiercells_queue);
+
+ for (auto cell : queue)
+ {
+ string cell_state = stringf("(|%s_h %s| state)", get_id(module), get_id(cell->name));
+ Module *m = module->design->module(cell->type);
+ log_assert(m != nullptr);
+
+ for (auto &conn : cell->connections())
+ {
+ Wire *w = m->wire(conn.first);
+ SigSpec sig = sigmap(conn.second);
+
+ if (bvmode || GetSize(w) == 1) {
+ hier.push_back(stringf(" (= %s (|%s_n %s| %s)) ; %s.%s\n", (GetSize(w) > 1 ? get_bv(sig) : get_bool(sig)).c_str(),
+ get_id(cell->type), get_id(w), cell_state.c_str(), get_id(cell->type), get_id(w)));
+ } else {
+ for (int i = 0; i < GetSize(w); i++)
+ hier.push_back(stringf(" (= %s (|%s_n %s %d| %s)) ; %s.%s[%d]\n", get_bool(sig[i]).c_str(),
+ get_id(cell->type), get_id(w), i, cell_state.c_str(), get_id(cell->type), get_id(w), i));
+ }
+ }
+ }
+ }
+
+ if (verbose) log("=> finalizing SMT2 representation of %s.\n", log_id(module));
+
+ for (auto c : hiercells) {
+ assert_list.push_back(stringf("(|%s_a| (|%s_h %s| state))", get_id(c->type), get_id(module), get_id(c->name)));
+ assume_list.push_back(stringf("(|%s_u| (|%s_h %s| state))", get_id(c->type), get_id(module), get_id(c->name)));
+ init_list.push_back(stringf("(|%s_i| (|%s_h %s| state))", get_id(c->type), get_id(module), get_id(c->name)));
+ hier.push_back(stringf(" (|%s_h| (|%s_h %s| state))\n", get_id(c->type), get_id(module), get_id(c->name)));
+ trans.push_back(stringf(" (|%s_t| (|%s_h %s| state) (|%s_h %s| next_state))\n",
+ get_id(c->type), get_id(module), get_id(c->name), get_id(module), get_id(c->name)));
+ }
+
+ string assert_expr = assert_list.empty() ? "true" : "(and";
+ if (!assert_list.empty()) {
+ if (GetSize(assert_list) == 1) {
+ assert_expr = "\n " + assert_list.front() + "\n";
+ } else {
+ for (auto &str : assert_list)
+ assert_expr += stringf("\n %s", str.c_str());
+ assert_expr += "\n)";
+ }
+ }
+ decls.push_back(stringf("(define-fun |%s_a| ((state |%s_s|)) Bool %s)\n",
+ get_id(module), get_id(module), assert_expr.c_str()));
+
+ string assume_expr = assume_list.empty() ? "true" : "(and";
+ if (!assume_list.empty()) {
+ if (GetSize(assume_list) == 1) {
+ assume_expr = "\n " + assume_list.front() + "\n";
+ } else {
+ for (auto &str : assume_list)
+ assume_expr += stringf("\n %s", str.c_str());
+ assume_expr += "\n)";
+ }
+ }
+ decls.push_back(stringf("(define-fun |%s_u| ((state |%s_s|)) Bool %s)\n",
+ get_id(module), get_id(module), assume_expr.c_str()));
+
+ string init_expr = init_list.empty() ? "true" : "(and";
+ if (!init_list.empty()) {
+ if (GetSize(init_list) == 1) {
+ init_expr = "\n " + init_list.front() + "\n";
+ } else {
+ for (auto &str : init_list)
+ init_expr += stringf("\n %s", str.c_str());
+ init_expr += "\n)";
+ }
+ }
+ decls.push_back(stringf("(define-fun |%s_i| ((state |%s_s|)) Bool %s)\n",
+ get_id(module), get_id(module), init_expr.c_str()));
+ }
+
+ void write(std::ostream &f)
+ {
+ f << stringf("; yosys-smt2-module %s\n", get_id(module));
+
+ for (auto it : decls)
+ f << it;
+
+ f << stringf("(define-fun |%s_h| ((state |%s_s|)) Bool ", get_id(module), get_id(module));
+ if (GetSize(hier) > 1) {
+ f << "(and\n";
+ for (auto it : hier)
+ f << it;
+ f << "))\n";
+ } else
+ if (GetSize(hier) == 1)
+ f << "\n" + hier.front() + ")\n";
+ else
+ f << "true)\n";
+
+ f << stringf("(define-fun |%s_t| ((state |%s_s|) (next_state |%s_s|)) Bool ", get_id(module), get_id(module), get_id(module));
+ if (GetSize(trans) > 1) {
+ f << "(and\n";
+ for (auto it : trans)
+ f << it;
+ f << "))";
+ } else
+ if (GetSize(trans) == 1)
+ f << "\n" + trans.front() + ")";
+ else
+ f << "true)";
+ f << stringf(" ; end of module %s\n", get_id(module));
+ }
+};
+
+struct Smt2Backend : public Backend {
+ Smt2Backend() : Backend("smt2", "write design to SMT-LIBv2 file") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" write_smt2 [options] [filename]\n");
+ log("\n");
+ log("Write a SMT-LIBv2 [1] description of the current design. For a module with name\n");
+ log("'<mod>' this will declare the sort '<mod>_s' (state of the module) and the\n");
+ log("functions operating on that state.\n");
+ log("\n");
+ log("The '<mod>_s' sort represents a module state. Additional '<mod>_n' functions\n");
+ log("are provided that can be used to access the values of the signals in the module.\n");
+ log("By default only ports, registers, and wires with the 'keep' attribute set are\n");
+ log("made available via such functions. With the -nobv option, multi-bit wires are\n");
+ log("exported as separate functions of type Bool for the individual bits. Without\n");
+ log("-nobv multi-bit wires are exported as single functions of type BitVec.\n");
+ log("\n");
+ log("The '<mod>_t' function evaluates to 'true' when the given pair of states\n");
+ log("describes a valid state transition.\n");
+ log("\n");
+ log("The '<mod>_a' function evaluates to 'true' when the given state satisfies\n");
+ log("the asserts in the module.\n");
+ log("\n");
+ log("The '<mod>_u' function evaluates to 'true' when the given state satisfies\n");
+ log("the assumptions in the module.\n");
+ log("\n");
+ log("The '<mod>_i' function evaluates to 'true' when the given state conforms\n");
+ log("to the initial state. Furthermore the '<mod>_is' function should be asserted\n");
+ log("to be true for initial states in addition to '<mod>_i', and should be\n");
+ log("asserted to be false for non-initial states.\n");
+ log("\n");
+ log("For hierarchical designs, the '<mod>_h' function must be asserted for each\n");
+ log("state to establish the design hierarchy. The '<mod>_h <cellname>' function\n");
+ log("evaluates to the state corresponding to the given cell within <mod>.\n");
+ log("\n");
+ log(" -verbose\n");
+ log(" this will print the recursive walk used to export the modules.\n");
+ log("\n");
+ log(" -nobv\n");
+ log(" disable support for BitVec (FixedSizeBitVectors theory). without this\n");
+ log(" option multi-bit wires are represented using the BitVec sort and\n");
+ log(" support for coarse grain cells (incl. arithmetic) is enabled.\n");
+ log("\n");
+ log(" -nomem\n");
+ log(" disable support for memories (via ArraysEx theory). this option is\n");
+ log(" implied by -nobv. only $mem cells without merged registers in\n");
+ log(" read ports are supported. call \"memory\" with -nordff to make sure\n");
+ log(" that no registers are merged into $mem read ports. '<mod>_m' functions\n");
+ log(" will be generated for accessing the arrays that are used to represent\n");
+ log(" memories.\n");
+ log("\n");
+ log(" -wires\n");
+ log(" create '<mod>_n' functions for all public wires. by default only ports,\n");
+ log(" registers, and wires with the 'keep' attribute are exported.\n");
+ log("\n");
+ log(" -tpl <template_file>\n");
+ log(" use the given template file. the line containing only the token '%%%%'\n");
+ log(" is replaced with the regular output of this command.\n");
+ log("\n");
+ log("[1] For more information on SMT-LIBv2 visit http://smt-lib.org/ or read David\n");
+ log("R. Cok's tutorial: http://www.grammatech.com/resources/smt/SMTLIBTutorial.pdf\n");
+ log("\n");
+ log("---------------------------------------------------------------------------\n");
+ log("\n");
+ log("Example:\n");
+ log("\n");
+ log("Consider the following module (test.v). We want to prove that the output can\n");
+ log("never transition from a non-zero value to a zero value.\n");
+ log("\n");
+ log(" module test(input clk, output reg [3:0] y);\n");
+ log(" always @(posedge clk)\n");
+ log(" y <= (y << 1) | ^y;\n");
+ log(" endmodule\n");
+ log("\n");
+ log("For this proof we create the following template (test.tpl).\n");
+ log("\n");
+ log(" ; we need QF_UFBV for this poof\n");
+ log(" (set-logic QF_UFBV)\n");
+ log("\n");
+ log(" ; insert the auto-generated code here\n");
+ log(" %%%%\n");
+ log("\n");
+ log(" ; declare two state variables s1 and s2\n");
+ log(" (declare-fun s1 () test_s)\n");
+ log(" (declare-fun s2 () test_s)\n");
+ log("\n");
+ log(" ; state s2 is the successor of state s1\n");
+ log(" (assert (test_t s1 s2))\n");
+ log("\n");
+ log(" ; we are looking for a model with y non-zero in s1\n");
+ log(" (assert (distinct (|test_n y| s1) #b0000))\n");
+ log("\n");
+ log(" ; we are looking for a model with y zero in s2\n");
+ log(" (assert (= (|test_n y| s2) #b0000))\n");
+ log("\n");
+ log(" ; is there such a model?\n");
+ log(" (check-sat)\n");
+ log("\n");
+ log("The following yosys script will create a 'test.smt2' file for our proof:\n");
+ log("\n");
+ log(" read_verilog test.v\n");
+ log(" hierarchy -check; proc; opt; check -assert\n");
+ log(" write_smt2 -bv -tpl test.tpl test.smt2\n");
+ log("\n");
+ log("Running 'cvc4 test.smt2' will print 'unsat' because y can never transition\n");
+ log("from non-zero to zero in the test design.\n");
+ log("\n");
+ }
+ virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::ifstream template_f;
+ bool bvmode = true, memmode = true, wiresmode = false, verbose = false;
+
+ log_header(design, "Executing SMT2 backend.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-tpl" && argidx+1 < args.size()) {
+ template_f.open(args[++argidx]);
+ if (template_f.fail())
+ log_error("Can't open template file `%s'.\n", args[argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-bv" || args[argidx] == "-mem") {
+ log_warning("Options -bv and -mem are now the default. Support for -bv and -mem will be removed in the future.\n");
+ continue;
+ }
+ if (args[argidx] == "-nobv") {
+ bvmode = false;
+ memmode = false;
+ continue;
+ }
+ if (args[argidx] == "-nomem") {
+ memmode = false;
+ continue;
+ }
+ if (args[argidx] == "-wires") {
+ wiresmode = true;
+ continue;
+ }
+ if (args[argidx] == "-verbose") {
+ verbose = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ if (template_f.is_open()) {
+ std::string line;
+ while (std::getline(template_f, line)) {
+ int indent = 0;
+ while (indent < GetSize(line) && (line[indent] == ' ' || line[indent] == '\t'))
+ indent++;
+ if (line.substr(indent, 2) == "%%")
+ break;
+ *f << line << std::endl;
+ }
+ }
+
+ *f << stringf("; SMT-LIBv2 description generated by %s\n", yosys_version_str);
+
+ if (!bvmode)
+ *f << stringf("; yosys-smt2-nobv\n");
+
+ if (!memmode)
+ *f << stringf("; yosys-smt2-nomem\n");
+
+ std::vector<RTLIL::Module*> sorted_modules;
+
+ // extract module dependencies
+ std::map<RTLIL::Module*, std::set<RTLIL::Module*>> module_deps;
+ for (auto &mod_it : design->modules_) {
+ module_deps[mod_it.second] = std::set<RTLIL::Module*>();
+ for (auto &cell_it : mod_it.second->cells_)
+ if (design->modules_.count(cell_it.second->type) > 0)
+ module_deps[mod_it.second].insert(design->modules_.at(cell_it.second->type));
+ }
+
+ // simple good-enough topological sort
+ // (O(n*m) on n elements and depth m)
+ while (module_deps.size() > 0) {
+ size_t sorted_modules_idx = sorted_modules.size();
+ for (auto &it : module_deps) {
+ for (auto &dep : it.second)
+ if (module_deps.count(dep) > 0)
+ goto not_ready_yet;
+ // log("Next in topological sort: %s\n", RTLIL::id2cstr(it.first->name));
+ sorted_modules.push_back(it.first);
+ not_ready_yet:;
+ }
+ if (sorted_modules_idx == sorted_modules.size())
+ log_error("Cyclic dependency between modules found! Cycle includes module %s.\n", RTLIL::id2cstr(module_deps.begin()->first->name));
+ while (sorted_modules_idx < sorted_modules.size())
+ module_deps.erase(sorted_modules.at(sorted_modules_idx++));
+ }
+
+ Module *topmod = design->top_module();
+ std::string topmod_id;
+
+ for (auto module : sorted_modules)
+ {
+ if (module->get_bool_attribute("\\blackbox") || module->has_memories_warn() || module->has_processes_warn())
+ continue;
+
+ log("Creating SMT-LIBv2 representation of module %s.\n", log_id(module));
+
+ Smt2Worker worker(module, bvmode, memmode, wiresmode, verbose);
+ worker.run();
+ worker.write(*f);
+
+ if (module == topmod)
+ topmod_id = worker.get_id(module);
+ }
+
+ if (topmod)
+ *f << stringf("; yosys-smt2-topmod %s\n", topmod_id.c_str());
+
+ *f << stringf("; end of yosys output\n");
+
+ if (template_f.is_open()) {
+ std::string line;
+ while (std::getline(template_f, line))
+ *f << line << std::endl;
+ }
+ }
+} Smt2Backend;
+
+PRIVATE_NAMESPACE_END
diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py
new file mode 100644
index 00000000..04c25f91
--- /dev/null
+++ b/backends/smt2/smtbmc.py
@@ -0,0 +1,734 @@
+#!/usr/bin/env python3
+#
+# 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.
+#
+
+import os, sys, getopt, re
+##yosys-sys-path##
+from smtio import SmtIo, SmtOpts, MkVcd
+from collections import defaultdict
+
+skip_steps = 0
+step_size = 1
+num_steps = 20
+vcdfile = None
+cexfile = None
+vlogtbfile = None
+inconstr = list()
+outconstr = None
+gentrace = False
+tempind = False
+dumpall = False
+assume_skipped = None
+final_only = False
+topmod = None
+noinfo = False
+so = SmtOpts()
+
+
+def usage():
+ print("""
+yosys-smtbmc [options] <yosys_smt2_output>
+
+ -t <num_steps>
+ -t <skip_steps>:<num_steps>
+ -t <skip_steps>:<step_size>:<num_steps>
+ default: skip_steps=0, step_size=1, num_steps=20
+
+ -g
+ generate an arbitrary trace that satisfies
+ all assertions and assumptions.
+
+ -i
+ instead of BMC run temporal induction
+
+ -m <module_name>
+ name of the top module
+
+ --smtc <constr_filename>
+ read constraints file
+
+ --cex <cex_filename>
+ read cex file as written by ABC's "write_cex -n"
+
+ --noinfo
+ only run the core proof, do not collect and print any
+ additional information (e.g. which assert failed)
+
+ --final-only
+ only check final constraints, assume base case
+
+ --assume-skipped <start_step>
+ assume asserts in skipped steps in BMC.
+ no assumptions are created for skipped steps
+ before <start_step>.
+
+ --dump-vcd <vcd_filename>
+ write trace to this VCD file
+ (hint: use 'write_smt2 -wires' for maximum
+ coverage of signals in generated VCD file)
+
+ --dump-vlogtb <verilog_filename>
+ write trace as Verilog test bench
+
+ --dump-smtc <constr_filename>
+ write trace as constraints file
+
+ --dump-all
+ when using -g or -i, create a dump file for each
+ step. The character '%' is replaces in all dump
+ filenames with the step number.
+""" + so.helpmsg())
+ sys.exit(1)
+
+
+try:
+ opts, args = getopt.getopt(sys.argv[1:], so.shortopts + "t:igm:", so.longopts +
+ ["final-only", "assume-skipped=", "smtc=", "cex=", "dump-vcd=", "dump-vlogtb=", "dump-smtc=", "dump-all", "noinfo"])
+except:
+ usage()
+
+for o, a in opts:
+ if o == "-t":
+ a = a.split(":")
+ if len(a) == 1:
+ num_steps = int(a[0])
+ elif len(a) == 2:
+ skip_steps = int(a[0])
+ num_steps = int(a[1])
+ elif len(a) == 3:
+ skip_steps = int(a[0])
+ step_size = int(a[1])
+ num_steps = int(a[2])
+ else:
+ assert 0
+ elif o == "--assume-skipped":
+ assume_skipped = int(a)
+ elif o == "--final-only":
+ final_only = True
+ elif o == "--smtc":
+ inconstr.append(a)
+ elif o == "--cex":
+ cexfile = a
+ elif o == "--dump-vcd":
+ vcdfile = a
+ elif o == "--dump-vlogtb":
+ vlogtbfile = a
+ elif o == "--dump-smtc":
+ outconstr = a
+ elif o == "--dump-all":
+ dumpall = True
+ elif o == "--noinfo":
+ noinfo = True
+ elif o == "-i":
+ tempind = True
+ elif o == "-g":
+ gentrace = True
+ elif o == "-m":
+ topmod = a
+ elif so.handle(o, a):
+ pass
+ else:
+ usage()
+
+if len(args) != 1:
+ usage()
+
+
+constr_final_start = None
+constr_asserts = defaultdict(list)
+constr_assumes = defaultdict(list)
+constr_write = list()
+
+for fn in inconstr:
+ current_states = None
+ current_line = 0
+
+ with open(fn, "r") as f:
+ for line in f:
+ current_line += 1
+
+ if line.startswith("#"):
+ continue
+
+ tokens = line.split()
+
+ if len(tokens) == 0:
+ continue
+
+ if tokens[0] == "initial":
+ current_states = set()
+ if not tempind:
+ current_states.add(0)
+ continue
+
+ if tokens[0] == "final":
+ constr_final = True
+ if len(tokens) == 1:
+ current_states = set(["final-%d" % i for i in range(0, num_steps+1)])
+ constr_final_start = 0
+ elif len(tokens) == 2:
+ i = int(tokens[1])
+ assert i < 0
+ current_states = set(["final-%d" % i for i in range(-i, num_steps+1)])
+ constr_final_start = -i if constr_final_start is None else min(constr_final_start, -i)
+ else:
+ assert 0
+ continue
+
+ if tokens[0] == "state":
+ current_states = set()
+ if not tempind:
+ for token in tokens[1:]:
+ tok = token.split(":")
+ if len(tok) == 1:
+ current_states.add(int(token))
+ elif len(tok) == 2:
+ lower = int(tok[0])
+ if tok[1] == "*":
+ upper = num_steps
+ else:
+ upper = int(tok[1])
+ for i in range(lower, upper+1):
+ current_states.add(i)
+ else:
+ assert 0
+ continue
+
+ if tokens[0] == "always":
+ if len(tokens) == 1:
+ current_states = set(range(0, num_steps+1))
+ elif len(tokens) == 2:
+ i = int(tokens[1])
+ assert i < 0
+ current_states = set(range(-i, num_steps+1))
+ else:
+ assert 0
+ continue
+
+ if tokens[0] == "assert":
+ assert current_states is not None
+
+ for state in current_states:
+ constr_asserts[state].append(("%s:%d" % (fn, current_line), " ".join(tokens[1:])))
+
+ continue
+
+ if tokens[0] == "assume":
+ assert current_states is not None
+
+ for state in current_states:
+ constr_assumes[state].append(("%s:%d" % (fn, current_line), " ".join(tokens[1:])))
+
+ continue
+
+ if tokens[0] == "write":
+ constr_write.append(" ".join(tokens[1:]))
+ continue
+
+ if tokens[0] == "logic":
+ so.logic = " ".join(tokens[1:])
+ continue
+
+ assert 0
+
+
+def get_constr_expr(db, state, final=False, getvalues=False):
+ if final:
+ if ("final-%d" % state) not in db:
+ return ([], [], []) if getvalues else "true"
+ else:
+ if state not in db:
+ return ([], [], []) if getvalues else "true"
+
+ netref_regex = re.compile(r'(^|[( ])\[(-?[0-9]+:|)([^\]]*)\](?=[ )]|$)')
+
+ def replace_netref(match):
+ state_sel = match.group(2)
+
+ if state_sel == "":
+ st = state
+ elif state_sel[0] == "-":
+ st = state + int(state_sel[:-1])
+ else:
+ st = int(state_sel[:-1])
+
+ expr = smt.net_expr(topmod, "s%d" % st, smt.get_path(topmod, match.group(3)))
+
+ return match.group(1) + expr
+
+ expr_list = list()
+ for loc, expr in db[("final-%d" % state) if final else state]:
+ actual_expr = netref_regex.sub(replace_netref, expr)
+ if getvalues:
+ expr_list.append((loc, expr, actual_expr))
+ else:
+ expr_list.append(actual_expr)
+
+ if getvalues:
+ loc_list, expr_list, acual_expr_list = zip(*expr_list)
+ value_list = smt.get_list(acual_expr_list)
+ return loc_list, expr_list, value_list
+
+ if len(expr_list) == 0:
+ return "true"
+
+ if len(expr_list) == 1:
+ return expr_list[0]
+
+ return "(and %s)" % " ".join(expr_list)
+
+
+smt = SmtIo(opts=so)
+
+if noinfo and vcdfile is None and vlogtbfile is None and outconstr is None:
+ smt.produce_models = False
+
+def print_msg(msg):
+ print("%s %s" % (smt.timestamp(), msg))
+ sys.stdout.flush()
+
+print_msg("Solver: %s" % (so.solver))
+
+with open(args[0], "r") as f:
+ for line in f:
+ smt.write(line)
+
+for line in constr_write:
+ smt.write(line)
+
+if topmod is None:
+ topmod = smt.topmod
+
+assert topmod is not None
+assert topmod in smt.modinfo
+
+if cexfile is not None:
+ with open(cexfile, "r") as f:
+ cex_regex = re.compile(r'([^\[@=]+)(\[\d+\])?([^@=]*)(@\d+)=([01])')
+ for entry in f.read().split():
+ match = cex_regex.match(entry)
+ assert match
+
+ name, bit, extra_name, step, val = match.group(1), match.group(2), match.group(3), match.group(4), match.group(5)
+
+ if extra_name != "":
+ continue
+
+ if name not in smt.modinfo[topmod].inputs:
+ continue
+
+ if bit is None:
+ bit = 0
+ else:
+ bit = int(bit[1:-1])
+
+ step = int(step[1:])
+ val = int(val)
+
+ if smt.modinfo[topmod].wsize[name] == 1:
+ assert bit == 0
+ smtexpr = "(= [%s] %s)" % (name, "true" if val else "false")
+ else:
+ smtexpr = "(= ((_ extract %d %d) [%s]) #b%d)" % (bit, bit, name, val)
+
+ # print("cex@%d: %s" % (step, smtexpr))
+ constr_assumes[step].append((cexfile, smtexpr))
+
+def write_vcd_trace(steps_start, steps_stop, index):
+ filename = vcdfile.replace("%", index)
+ print_msg("Writing trace to VCD file: %s" % (filename))
+
+ with open(filename, "w") as vcd_file:
+ vcd = MkVcd(vcd_file)
+ path_list = list()
+
+ for netpath in sorted(smt.hiernets(topmod)):
+ hidden_net = False
+ for n in netpath:
+ if n.startswith("$"):
+ hidden_net = True
+ if not hidden_net:
+ vcd.add_net([topmod] + netpath, smt.net_width(topmod, netpath))
+ path_list.append(netpath)
+
+ for i in range(steps_start, steps_stop):
+ vcd.set_time(i)
+ value_list = smt.get_net_bin_list(topmod, path_list, "s%d" % i)
+ for path, value in zip(path_list, value_list):
+ vcd.set_net([topmod] + path, value)
+
+ vcd.set_time(steps_stop)
+
+
+def write_vlogtb_trace(steps_start, steps_stop, index):
+ filename = vlogtbfile.replace("%", index)
+ print_msg("Writing trace to Verilog testbench: %s" % (filename))
+
+ with open(filename, "w") as f:
+ print("module testbench;", file=f)
+ print(" reg [4095:0] vcdfile;", file=f)
+ print(" reg clock = 0, genclock = 1;", file=f)
+
+ primary_inputs = list()
+ clock_inputs = set()
+
+ for name in smt.modinfo[topmod].inputs:
+ if name in ["clk", "clock", "CLK", "CLOCK"]:
+ clock_inputs.add(name)
+ width = smt.modinfo[topmod].wsize[name]
+ primary_inputs.append((name, width))
+
+ for name, width in primary_inputs:
+ if name in clock_inputs:
+ print(" wire [%d:0] PI_%s = clock;" % (width-1, name), file=f)
+ else:
+ print(" reg [%d:0] PI_%s;" % (width-1, name), file=f)
+
+ print(" %s UUT (" % topmod, file=f)
+ print(",\n".join(" .{name}(PI_{name})".format(name=name) for name, _ in primary_inputs), file=f)
+ print(" );", file=f)
+
+ print(" initial begin", file=f)
+ print(" if ($value$plusargs(\"vcd=%s\", vcdfile)) begin", file=f)
+ print(" $dumpfile(vcdfile);", file=f)
+ print(" $dumpvars(0, testbench);", file=f)
+ print(" end", file=f)
+ print(" while (genclock) begin", file=f)
+ print(" #5; clock = 0;", file=f)
+ print(" #5; clock = 1;", file=f)
+ print(" end", file=f)
+ print(" end", file=f)
+
+ print(" initial begin", file=f)
+
+ regs = sorted(smt.hiernets(topmod, regs_only=True))
+ regvals = smt.get_net_bin_list(topmod, regs, "s%d" % steps_start)
+
+ print(" #1;", file=f)
+ for reg, val in zip(regs, regvals):
+ hidden_net = False
+ for n in reg:
+ if n.startswith("$"):
+ hidden_net = True
+ print(" %sUUT.%s = %d'b%s;" % ("// " if hidden_net else "", ".".join(reg), len(val), val), file=f)
+
+ mems = sorted(smt.hiermems(topmod))
+ for mempath in mems:
+ abits, width, ports = smt.mem_info(topmod, "s%d" % steps_start, mempath)
+ mem = smt.mem_expr(topmod, "s%d" % steps_start, mempath)
+
+ addr_expr_list = list()
+ for i in range(steps_start, steps_stop):
+ for j in range(ports):
+ addr_expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, j))
+
+ addr_list = set()
+ for val in smt.get_list(addr_expr_list):
+ addr_list.add(smt.bv2int(val))
+
+ expr_list = list()
+ for i in addr_list:
+ expr_list.append("(select %s #b%s)" % (mem, format(i, "0%db" % abits)))
+
+ for i, val in zip(addr_list, smt.get_list(expr_list)):
+ val = smt.bv2bin(val)
+ print(" UUT.%s[%d] = %d'b%s;" % (".".join(mempath), i, len(val), val), file=f)
+
+ for i in range(steps_start, steps_stop):
+ pi_names = [[name] for name, _ in primary_inputs if name not in clock_inputs]
+ pi_values = smt.get_net_bin_list(topmod, pi_names, "s%d" % i)
+
+ print(" #1;", file=f)
+ print(" // state %d" % i, file=f)
+ if i > 0:
+ print(" @(posedge clock);", file=f)
+ for name, val in zip(pi_names, pi_values):
+ print(" PI_%s <= %d'b%s;" % (".".join(name), len(val), val), file=f)
+
+ print(" genclock = 0;", file=f)
+ print(" end", file=f)
+
+ print("endmodule", file=f)
+
+
+def write_constr_trace(steps_start, steps_stop, index):
+ filename = outconstr.replace("%", index)
+ print_msg("Writing trace to constraints file: %s" % (filename))
+
+ with open(filename, "w") as f:
+ primary_inputs = list()
+
+ for name in smt.modinfo[topmod].inputs:
+ width = smt.modinfo[topmod].wsize[name]
+ primary_inputs.append((name, width))
+
+ if steps_start == 0:
+ print("initial", file=f)
+ else:
+ print("state %d" % steps_start, file=f)
+
+ regnames = sorted(smt.hiernets(topmod, regs_only=True))
+ regvals = smt.get_net_list(topmod, regnames, "s%d" % steps_start)
+
+ for name, val in zip(regnames, regvals):
+ print("assume (= [%s] %s)" % (".".join(name), val), file=f)
+
+ mems = sorted(smt.hiermems(topmod))
+ for mempath in mems:
+ abits, width, ports = smt.mem_info(topmod, "s%d" % steps_start, mempath)
+ mem = smt.mem_expr(topmod, "s%d" % steps_start, mempath)
+
+ addr_expr_list = list()
+ for i in range(steps_start, steps_stop):
+ for j in range(ports):
+ addr_expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, j))
+
+ addr_list = set((smt.bv2int(val) for val in smt.get_list(addr_expr_list)))
+
+ expr_list = list()
+ for i in addr_list:
+ expr_list.append("(select %s #b%s)" % (mem, format(i, "0%db" % abits)))
+
+ for i, val in zip(addr_list, smt.get_list(expr_list)):
+ print("assume (= (select [%s] #b%s) %s)" % (".".join(mempath), format(i, "0%db" % abits), val), file=f)
+
+ for k in range(steps_start, steps_stop):
+ print("", file=f)
+ print("state %d" % k, file=f)
+
+ pi_names = [[name] for name, _ in sorted(primary_inputs)]
+ pi_values = smt.get_net_list(topmod, pi_names, "s%d" % k)
+
+ for name, val in zip(pi_names, pi_values):
+ print("assume (= [%s] %s)" % (".".join(name), val), file=f)
+
+
+def write_trace(steps_start, steps_stop, index):
+ if vcdfile is not None:
+ write_vcd_trace(steps_start, steps_stop, index)
+
+ if vlogtbfile is not None:
+ write_vlogtb_trace(steps_start, steps_stop, index)
+
+ if outconstr is not None:
+ write_constr_trace(steps_start, steps_stop, index)
+
+
+def print_failed_asserts_worker(mod, state, path):
+ assert mod in smt.modinfo
+
+ if smt.get("(|%s_a| %s)" % (mod, state)) in ["true", "#b1"]:
+ return
+
+ for cellname, celltype in smt.modinfo[mod].cells.items():
+ print_failed_asserts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), path + "." + cellname)
+
+ for assertfun, assertinfo in smt.modinfo[mod].asserts.items():
+ if smt.get("(|%s| %s)" % (assertfun, state)) in ["false", "#b0"]:
+ print_msg("Assert failed in %s: %s" % (path, assertinfo))
+
+
+def print_failed_asserts(state, final=False):
+ if noinfo: return
+ loc_list, expr_list, value_list = get_constr_expr(constr_asserts, state, final=final, getvalues=True)
+
+ for loc, expr, value in zip(loc_list, expr_list, value_list):
+ if smt.bv2int(value) == 0:
+ print_msg("Assert %s failed: %s" % (loc, expr))
+
+ if not final:
+ print_failed_asserts_worker(topmod, "s%d" % state, topmod)
+
+
+def print_anyconsts_worker(mod, state, path):
+ assert mod in smt.modinfo
+
+ for cellname, celltype in smt.modinfo[mod].cells.items():
+ print_anyconsts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), path + "." + cellname)
+
+ for fun, info in smt.modinfo[mod].anyconsts.items():
+ print_msg("Value for anyconst in %s (%s): %d" % (path, info, smt.bv2int(smt.get("(|%s| %s)" % (fun, state)))))
+
+
+def print_anyconsts(state):
+ if noinfo: return
+ print_anyconsts_worker(topmod, "s%d" % state, topmod)
+
+
+if tempind:
+ retstatus = False
+ skip_counter = step_size
+ for step in range(num_steps, -1, -1):
+ smt.write("(declare-fun s%d () |%s_s|)" % (step, topmod))
+ smt.write("(assert (|%s_u| s%d))" % (topmod, step))
+ smt.write("(assert (|%s_h| s%d))" % (topmod, step))
+ smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step))
+ smt.write("(assert %s)" % get_constr_expr(constr_assumes, step))
+
+ if step == num_steps:
+ smt.write("(assert (not (and (|%s_a| s%d) %s)))" % (topmod, step, get_constr_expr(constr_asserts, step)))
+
+ else:
+ smt.write("(assert (|%s_t| s%d s%d))" % (topmod, step, step+1))
+ smt.write("(assert (|%s_a| s%d))" % (topmod, step))
+ smt.write("(assert %s)" % get_constr_expr(constr_asserts, step))
+
+ if step > num_steps-skip_steps:
+ print_msg("Skipping induction in step %d.." % (step))
+ continue
+
+ skip_counter += 1
+ if skip_counter < step_size:
+ print_msg("Skipping induction in step %d.." % (step))
+ continue
+
+ skip_counter = 0
+ print_msg("Trying induction in step %d.." % (step))
+
+ if smt.check_sat() == "sat":
+ if step == 0:
+ print("%s Temporal induction failed!" % smt.timestamp())
+ print_anyconsts(num_steps)
+ print_failed_asserts(num_steps)
+ write_trace(step, num_steps+1, '%')
+
+ elif dumpall:
+ print_anyconsts(num_steps)
+ print_failed_asserts(num_steps)
+ write_trace(step, num_steps+1, "%d" % step)
+
+ else:
+ print("%s Temporal induction successful." % smt.timestamp())
+ retstatus = True
+ break
+
+
+else: # not tempind
+ step = 0
+ retstatus = True
+ while step < num_steps:
+ smt.write("(declare-fun s%d () |%s_s|)" % (step, topmod))
+ smt.write("(assert (|%s_u| s%d))" % (topmod, step))
+ smt.write("(assert (|%s_h| s%d))" % (topmod, step))
+ smt.write("(assert %s)" % get_constr_expr(constr_assumes, step))
+
+ if step == 0:
+ smt.write("(assert (|%s_i| s0))" % (topmod))
+ smt.write("(assert (|%s_is| s0))" % (topmod))
+
+ else:
+ smt.write("(assert (|%s_t| s%d s%d))" % (topmod, step-1, step))
+ smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step))
+
+ if step < skip_steps:
+ if assume_skipped is not None and step >= assume_skipped:
+ print_msg("Skipping step %d (and assuming pass).." % (step))
+ smt.write("(assert (|%s_a| s%d))" % (topmod, step))
+ smt.write("(assert %s)" % get_constr_expr(constr_asserts, step))
+ else:
+ print_msg("Skipping step %d.." % (step))
+ step += 1
+ continue
+
+ last_check_step = step
+ for i in range(1, step_size):
+ if step+i < num_steps:
+ smt.write("(declare-fun s%d () |%s_s|)" % (step+i, topmod))
+ smt.write("(assert (|%s_u| s%d))" % (topmod, step+i))
+ smt.write("(assert (|%s_h| s%d))" % (topmod, step+i))
+ smt.write("(assert (|%s_t| s%d s%d))" % (topmod, step+i-1, step+i))
+ smt.write("(assert %s)" % get_constr_expr(constr_assumes, step+i))
+ last_check_step = step+i
+
+ if not gentrace:
+ if not final_only:
+ if last_check_step == step:
+ print_msg("Checking asserts in step %d.." % (step))
+ else:
+ print_msg("Checking asserts in steps %d to %d.." % (step, last_check_step))
+ smt.write("(push 1)")
+
+ smt.write("(assert (not (and %s)))" % " ".join(["(|%s_a| s%d)" % (topmod, i) for i in range(step, last_check_step+1)] +
+ [get_constr_expr(constr_asserts, i) for i in range(step, last_check_step+1)]))
+
+ if smt.check_sat() == "sat":
+ print("%s BMC failed!" % smt.timestamp())
+ print_anyconsts(step)
+ for i in range(step, last_check_step+1):
+ print_failed_asserts(i)
+ write_trace(0, last_check_step+1, '%')
+ retstatus = False
+ break
+
+ smt.write("(pop 1)")
+
+ if (constr_final_start is not None) or (last_check_step+1 != num_steps):
+ for i in range(step, last_check_step+1):
+ smt.write("(assert (|%s_a| s%d))" % (topmod, i))
+ smt.write("(assert %s)" % get_constr_expr(constr_asserts, i))
+
+ if constr_final_start is not None:
+ for i in range(step, last_check_step+1):
+ if i < constr_final_start:
+ continue
+
+ print_msg("Checking final constraints in step %d.." % (i))
+ smt.write("(push 1)")
+
+ smt.write("(assert %s)" % get_constr_expr(constr_assumes, i, final=True))
+ smt.write("(assert (not %s))" % get_constr_expr(constr_asserts, i, final=True))
+
+ if smt.check_sat() == "sat":
+ print("%s BMC failed!" % smt.timestamp())
+ print_anyconsts(i)
+ print_failed_asserts(i, final=True)
+ write_trace(0, i+1, '%')
+ retstatus = False
+ break
+
+ smt.write("(pop 1)")
+ if not retstatus:
+ break
+
+ else: # gentrace
+ for i in range(step, last_check_step+1):
+ smt.write("(assert (|%s_a| s%d))" % (topmod, i))
+ smt.write("(assert %s)" % get_constr_expr(constr_asserts, i))
+
+ print_msg("Solving for step %d.." % (last_check_step))
+ if smt.check_sat() != "sat":
+ print("%s No solution found!" % smt.timestamp())
+ retstatus = False
+ break
+
+ elif dumpall:
+ print_anyconsts(0)
+ write_trace(0, last_check_step+1, "%d" % step)
+
+ step += step_size
+
+ if gentrace:
+ print_anyconsts(0)
+ write_trace(0, num_steps, '%')
+
+
+smt.write("(exit)")
+smt.wait()
+
+print_msg("Status: %s" % ("PASSED" if retstatus else "FAILED (!)"))
+sys.exit(0 if retstatus else 1)
diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py
new file mode 100644
index 00000000..865eed1f
--- /dev/null
+++ b/backends/smt2/smtio.py
@@ -0,0 +1,731 @@
+#!/usr/bin/env python3
+#
+# 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.
+#
+
+import sys, subprocess, re
+from copy import deepcopy
+from select import select
+from time import time
+
+
+hex_dict = {
+ "0": "0000", "1": "0001", "2": "0010", "3": "0011",
+ "4": "0100", "5": "0101", "6": "0110", "7": "0111",
+ "8": "1000", "9": "1001", "A": "1010", "B": "1011",
+ "C": "1100", "D": "1101", "E": "1110", "F": "1111",
+ "a": "1010", "b": "1011", "c": "1100", "d": "1101",
+ "e": "1110", "f": "1111"
+}
+
+
+class SmtModInfo:
+ def __init__(self):
+ self.inputs = set()
+ self.outputs = set()
+ self.registers = set()
+ self.memories = dict()
+ self.wires = set()
+ self.wsize = dict()
+ self.cells = dict()
+ self.asserts = dict()
+ self.anyconsts = dict()
+
+
+class SmtIo:
+ def __init__(self, opts=None):
+ self.logic = None
+ self.logic_qf = True
+ self.logic_ax = True
+ self.logic_uf = True
+ self.logic_bv = True
+ self.produce_models = True
+ self.smt2cache = [list()]
+ self.p = None
+
+ if opts is not None:
+ self.logic = opts.logic
+ self.solver = opts.solver
+ self.debug_print = opts.debug_print
+ self.debug_file = opts.debug_file
+ self.dummy_file = opts.dummy_file
+ self.timeinfo = opts.timeinfo
+ self.unroll = opts.unroll
+ self.noincr = opts.noincr
+ self.info_stmts = opts.info_stmts
+ self.nocomments = opts.nocomments
+
+ else:
+ self.solver = "z3"
+ self.debug_print = False
+ self.debug_file = None
+ self.dummy_file = None
+ self.timeinfo = True
+ self.unroll = False
+ self.noincr = False
+ self.info_stmts = list()
+ self.nocomments = False
+
+ if self.solver == "yices":
+ self.popen_vargs = ['yices-smt2', '--incremental']
+
+ if self.solver == "z3":
+ self.popen_vargs = ['z3', '-smt2', '-in']
+
+ if self.solver == "cvc4":
+ self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2']
+
+ if self.solver == "mathsat":
+ self.popen_vargs = ['mathsat']
+
+ if self.solver == "boolector":
+ self.popen_vargs = ['boolector', '--smt2', '-i']
+ self.unroll = True
+
+ if self.solver == "abc":
+ self.popen_vargs = ['yosys-abc', '-S', '%blast; &sweep -C 5000; &syn4; &cec -s -m -C 2000']
+ self.logic_ax = False
+ self.unroll = True
+ self.noincr = True
+
+ if self.solver == "dummy":
+ assert self.dummy_file is not None
+ self.dummy_fd = open(self.dummy_file, "r")
+ else:
+ if self.dummy_file is not None:
+ self.dummy_fd = open(self.dummy_file, "w")
+ if not self.noincr:
+ self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
+ if self.unroll:
+ self.logic_uf = False
+ self.unroll_idcnt = 0
+ self.unroll_buffer = ""
+ self.unroll_sorts = set()
+ self.unroll_objs = set()
+ self.unroll_decls = dict()
+ self.unroll_cache = dict()
+ self.unroll_stack = list()
+
+ self.start_time = time()
+
+ self.modinfo = dict()
+ self.curmod = None
+ self.topmod = None
+ self.setup_done = False
+
+ def setup(self):
+ assert not self.setup_done
+
+ if self.logic is None:
+ self.logic = ""
+ if self.logic_qf: self.logic += "QF_"
+ if self.logic_ax: self.logic += "A"
+ if self.logic_uf: self.logic += "UF"
+ if self.logic_bv: self.logic += "BV"
+
+ self.setup_done = True
+
+ if self.produce_models:
+ self.write("(set-option :produce-models true)")
+
+ self.write("(set-logic %s)" % self.logic)
+
+ for stmt in self.info_stmts:
+ self.write(stmt)
+
+ def timestamp(self):
+ secs = int(time() - self.start_time)
+ return "## %6d %3d:%02d:%02d " % (secs, secs // (60*60), (secs // 60) % 60, secs % 60)
+
+ def replace_in_stmt(self, stmt, pat, repl):
+ if stmt == pat:
+ return repl
+
+ if isinstance(stmt, list):
+ return [self.replace_in_stmt(s, pat, repl) for s in stmt]
+
+ return stmt
+
+ def unroll_stmt(self, stmt):
+ if not isinstance(stmt, list):
+ return stmt
+
+ stmt = [self.unroll_stmt(s) for s in stmt]
+
+ if len(stmt) >= 2 and not isinstance(stmt[0], list) and stmt[0] in self.unroll_decls:
+ assert stmt[1] in self.unroll_objs
+
+ key = tuple(stmt)
+ if key not in self.unroll_cache:
+ decl = deepcopy(self.unroll_decls[key[0]])
+
+ self.unroll_cache[key] = "|UNROLL#%d|" % self.unroll_idcnt
+ decl[1] = self.unroll_cache[key]
+ self.unroll_idcnt += 1
+
+ if decl[0] == "declare-fun":
+ if isinstance(decl[3], list) or decl[3] not in self.unroll_sorts:
+ self.unroll_objs.add(decl[1])
+ decl[2] = list()
+ else:
+ self.unroll_objs.add(decl[1])
+ decl = list()
+
+ elif decl[0] == "define-fun":
+ arg_index = 1
+ for arg_name, arg_sort in decl[2]:
+ decl[4] = self.replace_in_stmt(decl[4], arg_name, key[arg_index])
+ arg_index += 1
+ decl[2] = list()
+
+ if len(decl) > 0:
+ decl = self.unroll_stmt(decl)
+ self.write(self.unparse(decl), unroll=False)
+
+ return self.unroll_cache[key]
+
+ return stmt
+
+ def write(self, stmt, unroll=True):
+ if stmt.startswith(";"):
+ self.info(stmt)
+ elif not self.setup_done:
+ self.setup()
+
+ stmt = stmt.strip()
+
+ if self.nocomments or self.unroll:
+ if stmt.startswith(";"):
+ return
+ stmt = re.sub(r" ;.*", "", stmt)
+
+ if unroll and self.unroll:
+ stmt = self.unroll_buffer + stmt
+ self.unroll_buffer = ""
+
+ s = re.sub(r"\|[^|]*\|", "", stmt)
+ if s.count("(") != s.count(")"):
+ self.unroll_buffer = stmt + " "
+ return
+
+ s = self.parse(stmt)
+
+ if self.debug_print:
+ print("-> %s" % s)
+
+ if len(s) == 3 and s[0] == "declare-sort" and s[2] == "0":
+ self.unroll_sorts.add(s[1])
+ return
+
+ elif len(s) == 4 and s[0] == "declare-fun" and s[2] == [] and s[3] in self.unroll_sorts:
+ self.unroll_objs.add(s[1])
+ return
+
+ elif len(s) >= 4 and s[0] == "declare-fun":
+ for arg_sort in s[2]:
+ if arg_sort in self.unroll_sorts:
+ self.unroll_decls[s[1]] = s
+ return
+
+ elif len(s) >= 4 and s[0] == "define-fun":
+ for arg_name, arg_sort in s[2]:
+ if arg_sort in self.unroll_sorts:
+ self.unroll_decls[s[1]] = s
+ return
+
+ stmt = self.unparse(self.unroll_stmt(s))
+
+ if stmt == "(push 1)":
+ self.unroll_stack.append((
+ deepcopy(self.unroll_sorts),
+ deepcopy(self.unroll_objs),
+ deepcopy(self.unroll_decls),
+ deepcopy(self.unroll_cache),
+ ))
+
+ if stmt == "(pop 1)":
+ self.unroll_sorts, self.unroll_objs, self.unroll_decls, self.unroll_cache = self.unroll_stack.pop()
+
+ if self.debug_print:
+ print("> %s" % stmt)
+
+ if self.debug_file:
+ print(stmt, file=self.debug_file)
+ self.debug_file.flush()
+
+ if self.solver != "dummy":
+ if self.noincr:
+ if self.p is not None and not stmt.startswith("(get-"):
+ self.p.stdin.close()
+ self.p = None
+ if stmt == "(push 1)":
+ self.smt2cache.append(list())
+ elif stmt == "(pop 1)":
+ self.smt2cache.pop()
+ else:
+ if self.p is not None:
+ self.p.stdin.write(bytes(stmt + "\n", "ascii"))
+ self.p.stdin.flush()
+ self.smt2cache[-1].append(stmt)
+ else:
+ self.p.stdin.write(bytes(stmt + "\n", "ascii"))
+ self.p.stdin.flush()
+
+ def info(self, stmt):
+ if not stmt.startswith("; yosys-smt2-"):
+ return
+
+ fields = stmt.split()
+
+ if fields[1] == "yosys-smt2-nomem":
+ if self.logic is None:
+ self.logic_ax = False
+
+ if fields[1] == "yosys-smt2-nobv":
+ if self.logic is None:
+ self.logic_bv = False
+
+ if fields[1] == "yosys-smt2-module":
+ self.curmod = fields[2]
+ self.modinfo[self.curmod] = SmtModInfo()
+
+ if fields[1] == "yosys-smt2-cell":
+ self.modinfo[self.curmod].cells[fields[3]] = fields[2]
+
+ if fields[1] == "yosys-smt2-topmod":
+ self.topmod = fields[2]
+
+ if fields[1] == "yosys-smt2-input":
+ self.modinfo[self.curmod].inputs.add(fields[2])
+ self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
+
+ if fields[1] == "yosys-smt2-output":
+ self.modinfo[self.curmod].outputs.add(fields[2])
+ self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
+
+ if fields[1] == "yosys-smt2-register":
+ self.modinfo[self.curmod].registers.add(fields[2])
+ self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
+
+ if fields[1] == "yosys-smt2-memory":
+ self.modinfo[self.curmod].memories[fields[2]] = (int(fields[3]), int(fields[4]), int(fields[5]))
+
+ if fields[1] == "yosys-smt2-wire":
+ self.modinfo[self.curmod].wires.add(fields[2])
+ self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
+
+ if fields[1] == "yosys-smt2-assert":
+ self.modinfo[self.curmod].asserts[fields[2]] = fields[3]
+
+ if fields[1] == "yosys-smt2-anyconst":
+ self.modinfo[self.curmod].anyconsts[fields[2]] = fields[3]
+
+ def hiernets(self, top, regs_only=False):
+ def hiernets_worker(nets, mod, cursor):
+ for netname in sorted(self.modinfo[mod].wsize.keys()):
+ if not regs_only or netname in self.modinfo[mod].registers:
+ nets.append(cursor + [netname])
+ for cellname, celltype in sorted(self.modinfo[mod].cells.items()):
+ hiernets_worker(nets, celltype, cursor + [cellname])
+
+ nets = list()
+ hiernets_worker(nets, top, [])
+ return nets
+
+ def hiermems(self, top):
+ def hiermems_worker(mems, mod, cursor):
+ for memname in sorted(self.modinfo[mod].memories.keys()):
+ mems.append(cursor + [memname])
+ for cellname, celltype in sorted(self.modinfo[mod].cells.items()):
+ hiermems_worker(mems, celltype, cursor + [cellname])
+
+ mems = list()
+ hiermems_worker(mems, top, [])
+ return mems
+
+ def read(self):
+ stmt = []
+ count_brackets = 0
+
+ while True:
+ if self.solver == "dummy":
+ line = self.dummy_fd.readline().strip()
+ else:
+ line = self.p.stdout.readline().decode("ascii").strip()
+ if self.dummy_file is not None:
+ self.dummy_fd.write(line + "\n")
+
+ count_brackets += line.count("(")
+ count_brackets -= line.count(")")
+ stmt.append(line)
+
+ if self.debug_print:
+ print("< %s" % line)
+ if count_brackets == 0:
+ break
+ if self.solver != "dummy" and self.p.poll():
+ print("SMT Solver terminated unexpectedly: %s" % "".join(stmt))
+ sys.exit(1)
+
+ stmt = "".join(stmt)
+ if stmt.startswith("(error"):
+ print("SMT Solver Error: %s" % stmt, file=sys.stderr)
+ sys.exit(1)
+
+ return stmt
+
+ def check_sat(self):
+ if self.debug_print:
+ print("> (check-sat)")
+ if self.debug_file and not self.nocomments:
+ print("; running check-sat..", file=self.debug_file)
+ self.debug_file.flush()
+
+ if self.solver != "dummy":
+ if self.noincr:
+ if self.p is not None:
+ self.p.stdin.close()
+ self.p = None
+ self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ for cache_ctx in self.smt2cache:
+ for cache_stmt in cache_ctx:
+ self.p.stdin.write(bytes(cache_stmt + "\n", "ascii"))
+
+ self.p.stdin.write(bytes("(check-sat)\n", "ascii"))
+ self.p.stdin.flush()
+
+ if self.timeinfo:
+ i = 0
+ s = "/-\|"
+
+ count = 0
+ num_bs = 0
+ while select([self.p.stdout], [], [], 0.1) == ([], [], []):
+ count += 1
+
+ if count < 25:
+ continue
+
+ if count % 10 == 0 or count == 25:
+ secs = count // 10
+
+ if secs < 60:
+ m = "(%d seconds)" % secs
+ elif secs < 60*60:
+ m = "(%d seconds -- %d:%02d)" % (secs, secs // 60, secs % 60)
+ else:
+ m = "(%d seconds -- %d:%02d:%02d)" % (secs, secs // (60*60), (secs // 60) % 60, secs % 60)
+
+ print("%s %s %c" % ("\b \b" * num_bs, m, s[i]), end="", file=sys.stderr)
+ num_bs = len(m) + 3
+
+ else:
+ print("\b" + s[i], end="", file=sys.stderr)
+
+ sys.stderr.flush()
+ i = (i + 1) % len(s)
+
+ if num_bs != 0:
+ print("\b \b" * num_bs, end="", file=sys.stderr)
+ sys.stderr.flush()
+
+ result = self.read()
+ if self.debug_file:
+ print("(set-info :status %s)" % result, file=self.debug_file)
+ print("(check-sat)", file=self.debug_file)
+ self.debug_file.flush()
+ return result
+
+ def parse(self, stmt):
+ def worker(stmt):
+ if stmt[0] == '(':
+ expr = []
+ cursor = 1
+ while stmt[cursor] != ')':
+ el, le = worker(stmt[cursor:])
+ expr.append(el)
+ cursor += le
+ return expr, cursor+1
+
+ if stmt[0] == '|':
+ expr = "|"
+ cursor = 1
+ while stmt[cursor] != '|':
+ expr += stmt[cursor]
+ cursor += 1
+ expr += "|"
+ return expr, cursor+1
+
+ if stmt[0] in [" ", "\t", "\r", "\n"]:
+ el, le = worker(stmt[1:])
+ return el, le+1
+
+ expr = ""
+ cursor = 0
+ while stmt[cursor] not in ["(", ")", "|", " ", "\t", "\r", "\n"]:
+ expr += stmt[cursor]
+ cursor += 1
+ return expr, cursor
+ return worker(stmt)[0]
+
+ def unparse(self, stmt):
+ if isinstance(stmt, list):
+ return "(" + " ".join([self.unparse(s) for s in stmt]) + ")"
+ return stmt
+
+ def bv2hex(self, v):
+ h = ""
+ v = self.bv2bin(v)
+ while len(v) > 0:
+ d = 0
+ if len(v) > 0 and v[-1] == "1": d += 1
+ if len(v) > 1 and v[-2] == "1": d += 2
+ if len(v) > 2 and v[-3] == "1": d += 4
+ if len(v) > 3 and v[-4] == "1": d += 8
+ h = hex(d)[2:] + h
+ if len(v) < 4: break
+ v = v[:-4]
+ return h
+
+ def bv2bin(self, v):
+ if v == "true": return "1"
+ if v == "false": return "0"
+ if v.startswith("#b"):
+ return v[2:]
+ if v.startswith("#x"):
+ return "".join(hex_dict.get(x) for x in v[2:])
+ assert False
+
+ def bv2int(self, v):
+ return int(self.bv2bin(v), 2)
+
+ def get(self, expr):
+ self.write("(get-value (%s))" % (expr))
+ return self.parse(self.read())[0][1]
+
+ def get_list(self, expr_list):
+ if len(expr_list) == 0:
+ return []
+ self.write("(get-value (%s))" % " ".join(expr_list))
+ return [n[1] for n in self.parse(self.read())]
+
+ def get_path(self, mod, path):
+ assert mod in self.modinfo
+ path = path.split(".")
+
+ for i in range(len(path)-1):
+ first = ".".join(path[0:i+1])
+ second = ".".join(path[i+1:])
+
+ if first in self.modinfo[mod].cells:
+ nextmod = self.modinfo[mod].cells[first]
+ return [first] + self.get_path(nextmod, second)
+
+ return [".".join(path)]
+
+ def net_expr(self, mod, base, path):
+ if len(path) == 1:
+ assert mod in self.modinfo
+ if path[0] == "":
+ return base
+ if path[0] in self.modinfo[mod].cells:
+ return "(|%s_h %s| %s)" % (mod, path[0], base)
+ if path[0] in self.modinfo[mod].wsize:
+ return "(|%s_n %s| %s)" % (mod, path[0], base)
+ if path[0] in self.modinfo[mod].memories:
+ return "(|%s_m %s| %s)" % (mod, path[0], base)
+ assert 0
+
+ assert mod in self.modinfo
+ assert path[0] in self.modinfo[mod].cells
+
+ nextmod = self.modinfo[mod].cells[path[0]]
+ nextbase = "(|%s_h %s| %s)" % (mod, path[0], base)
+ return self.net_expr(nextmod, nextbase, path[1:])
+
+ def net_width(self, mod, net_path):
+ for i in range(len(net_path)-1):
+ assert mod in self.modinfo
+ assert net_path[i] in self.modinfo[mod].cells
+ mod = self.modinfo[mod].cells[net_path[i]]
+
+ assert mod in self.modinfo
+ assert net_path[-1] in self.modinfo[mod].wsize
+ return self.modinfo[mod].wsize[net_path[-1]]
+
+ def mem_expr(self, mod, base, path, portidx=None, infomode=False):
+ if len(path) == 1:
+ assert mod in self.modinfo
+ assert path[0] in self.modinfo[mod].memories
+ if infomode:
+ return self.modinfo[mod].memories[path[0]]
+ return "(|%s_m%s %s| %s)" % (mod, "" if portidx is None else ":%d" % portidx, path[0], base)
+
+ assert mod in self.modinfo
+ assert path[0] in self.modinfo[mod].cells
+
+ nextmod = self.modinfo[mod].cells[path[0]]
+ nextbase = "(|%s_h %s| %s)" % (mod, path[0], base)
+ return self.mem_expr(nextmod, nextbase, path[1:], portidx=portidx, infomode=infomode)
+
+ def mem_info(self, mod, base, path):
+ return self.mem_expr(mod, base, path, infomode=True)
+
+ def get_net(self, mod_name, net_path, state_name):
+ return self.get(self.net_expr(mod_name, state_name, net_path))
+
+ def get_net_list(self, mod_name, net_path_list, state_name):
+ return self.get_list([self.net_expr(mod_name, state_name, n) for n in net_path_list])
+
+ def get_net_hex(self, mod_name, net_path, state_name):
+ return self.bv2hex(self.get_net(mod_name, net_path, state_name))
+
+ def get_net_hex_list(self, mod_name, net_path_list, state_name):
+ return [self.bv2hex(v) for v in self.get_net_list(mod_name, net_path_list, state_name)]
+
+ def get_net_bin(self, mod_name, net_path, state_name):
+ return self.bv2bin(self.get_net(mod_name, net_path, state_name))
+
+ def get_net_bin_list(self, mod_name, net_path_list, state_name):
+ return [self.bv2bin(v) for v in self.get_net_list(mod_name, net_path_list, state_name)]
+
+ def wait(self):
+ if self.p is not None:
+ self.p.wait()
+
+
+class SmtOpts:
+ def __init__(self):
+ self.shortopts = "s:v"
+ self.longopts = ["unroll", "noincr", "noprogress", "dump-smt2=", "logic=", "dummy=", "info=", "nocomments"]
+ self.solver = "z3"
+ self.debug_print = False
+ self.debug_file = None
+ self.dummy_file = None
+ self.unroll = False
+ self.noincr = False
+ self.timeinfo = True
+ self.logic = None
+ self.info_stmts = list()
+ self.nocomments = False
+
+ def handle(self, o, a):
+ if o == "-s":
+ self.solver = a
+ elif o == "-v":
+ self.debug_print = True
+ elif o == "--unroll":
+ self.unroll = True
+ elif o == "--noincr":
+ self.noincr = True
+ elif o == "--noprogress":
+ self.timeinfo = True
+ elif o == "--dump-smt2":
+ self.debug_file = open(a, "w")
+ elif o == "--logic":
+ self.logic = a
+ elif o == "--dummy":
+ self.dummy_file = a
+ elif o == "--info":
+ self.info_stmts.append(a)
+ elif o == "--nocomments":
+ self.nocomments = True
+ else:
+ return False
+ return True
+
+ def helpmsg(self):
+ return """
+ -s <solver>
+ set SMT solver: z3, cvc4, yices, mathsat, boolector, dummy
+ default: z3
+
+ --logic <smt2_logic>
+ use the specified SMT2 logic (e.g. QF_AUFBV)
+
+ --dummy <filename>
+ if solver is "dummy", read solver output from that file
+ otherwise: write solver output to that file
+
+ -v
+ enable debug output
+
+ --unroll
+ unroll uninterpreted functions
+
+ --noincr
+ don't use incremental solving, instead restart solver for
+ each (check-sat). This also avoids (push) and (pop).
+
+ --noprogress
+ disable timer display during solving
+
+ --dump-smt2 <filename>
+ write smt2 statements to file
+
+ --info <smt2-info-stmt>
+ include the specified smt2 info statement in the smt2 output
+
+ --nocomments
+ strip all comments from the generated smt2 code
+"""
+
+
+class MkVcd:
+ def __init__(self, f):
+ self.f = f
+ self.t = -1
+ self.nets = dict()
+
+ def add_net(self, path, width):
+ path = tuple(path)
+ assert self.t == -1
+ key = "n%d" % len(self.nets)
+ self.nets[path] = (key, width)
+
+ def set_net(self, path, bits):
+ path = tuple(path)
+ assert self.t >= 0
+ assert path in self.nets
+ print("b%s %s" % (bits, self.nets[path][0]), file=self.f)
+
+ def set_time(self, t):
+ assert t >= self.t
+ if t != self.t:
+ if self.t == -1:
+ print("$var integer 32 t smt_step $end", file=self.f)
+ print("$var event 1 ! smt_clock $end", file=self.f)
+ scope = []
+ for path in sorted(self.nets):
+ while len(scope)+1 > len(path) or (len(scope) > 0 and scope[-1] != path[len(scope)-1]):
+ print("$upscope $end", file=self.f)
+ scope = scope[:-1]
+ while len(scope)+1 < len(path):
+ print("$scope module %s $end" % path[len(scope)], file=self.f)
+ scope.append(path[len(scope)-1])
+ key, width = self.nets[path]
+ print("$var wire %d %s %s $end" % (width, key, path[-1]), file=self.f)
+ for i in range(len(scope)):
+ print("$upscope $end", file=self.f)
+ print("$enddefinitions $end", file=self.f)
+ self.t = t
+ assert self.t >= 0
+ print("#%d" % (10 * self.t), file=self.f)
+ print("1!", file=self.f)
+ print("b%s t" % format(self.t, "032b"), file=self.f)
+
diff --git a/backends/smt2/test_cells.sh b/backends/smt2/test_cells.sh
new file mode 100644
index 00000000..34adb7af
--- /dev/null
+++ b/backends/smt2/test_cells.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+set -ex
+
+rm -rf test_cells
+mkdir test_cells
+cd test_cells
+
+../../../yosys -p 'test_cell -muxdiv -w test all /$alu /$macc /$fa /$lcu /$lut /$shift /$shiftx'
+
+cat > miter.tpl <<- EOT
+; #model# (set-option :produce-models true)
+(set-logic QF_UFBV)
+%%
+(declare-fun s () miter_s)
+(assert (|miter_n trigger| s))
+(check-sat)
+; #model# (get-value ((|miter_n in_A| s) (|miter_n in_B| s) (|miter_n gold_Y| s) (|miter_n gate_Y| s) (|miter_n trigger| s)))
+EOT
+
+for x in $(set +x; ls test_*.il | sort -R); do
+ x=${x%.il}
+ cat > $x.ys <<- EOT
+ read_ilang $x.il
+ copy gold gate
+
+ cd gate
+ techmap; opt; abc;;
+ cd ..
+
+ miter -equiv -flatten -make_outputs gold gate miter
+ hierarchy -check -top miter
+
+ dump
+ write_smt2 -bv -tpl miter.tpl $x.smt2
+ EOT
+ ../../../yosys -q $x.ys
+ if ! cvc4 $x.smt2 > $x.result; then
+ cat $x.result
+ exit 1
+ fi
+ if ! grep unsat $x.result; then
+ echo "Proof failed! Extracting model..."
+ sed -i 's/^; #model# //' $x.smt2
+ cvc4 $x.smt2
+ exit 1
+ fi
+done
+
+set +x
+echo ""
+echo " All tests passed."
+echo ""
+exit 0
+
diff --git a/backends/smv/Makefile.inc b/backends/smv/Makefile.inc
new file mode 100644
index 00000000..66c192d8
--- /dev/null
+++ b/backends/smv/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += backends/smv/smv.o
+
diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc
new file mode 100644
index 00000000..162ce490
--- /dev/null
+++ b/backends/smv/smv.cc
@@ -0,0 +1,784 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/rtlil.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
+#include "kernel/log.h"
+#include <string>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct SmvWorker
+{
+ CellTypes ct;
+ SigMap sigmap;
+ RTLIL::Module *module;
+ std::ostream &f;
+ bool verbose;
+
+ int idcounter;
+ dict<IdString, shared_str> idcache;
+ pool<shared_str> used_names;
+ vector<shared_str> strbuf;
+
+ pool<Wire*> partial_assignment_wires;
+ dict<SigBit, std::pair<const char*, int>> partial_assignment_bits;
+ vector<string> assignments, invarspecs;
+
+ const char *cid()
+ {
+ while (true) {
+ shared_str s(stringf("_%d", idcounter++));
+ if (!used_names.count(s)) {
+ used_names.insert(s);
+ return s.c_str();
+ }
+ }
+ }
+
+ const char *cid(IdString id, bool precache = false)
+ {
+ if (!idcache.count(id))
+ {
+ string name = stringf("_%s", id.c_str());
+
+ if (name.substr(0, 2) == "_\\")
+ name = "_" + name.substr(2);
+
+ for (auto &c : name) {
+ if (c == '|' || c == '$' || c == '_') continue;
+ if (c >= 'a' && c <='z') continue;
+ if (c >= 'A' && c <='Z') continue;
+ if (c >= '0' && c <='9') continue;
+ if (precache) return nullptr;
+ c = '#';
+ }
+
+ if (name == "_main")
+ name = "main";
+
+ while (used_names.count(name))
+ name += "_";
+
+ shared_str s(name);
+ used_names.insert(s);
+ idcache[id] = s;
+ }
+
+ return idcache.at(id).c_str();
+ }
+
+ SmvWorker(RTLIL::Module *module, bool verbose, std::ostream &f) :
+ ct(module->design), sigmap(module), module(module), f(f), verbose(verbose), idcounter(0)
+ {
+ for (auto mod : module->design->modules())
+ cid(mod->name, true);
+
+ for (auto wire : module->wires())
+ cid(wire->name, true);
+
+ for (auto cell : module->cells()) {
+ cid(cell->name, true);
+ cid(cell->type, true);
+ for (auto &conn : cell->connections())
+ cid(conn.first, true);
+ }
+ }
+
+ const char *rvalue(SigSpec sig, int width = -1, bool is_signed = false)
+ {
+ string s;
+ int count_chunks = 0;
+ sigmap.apply(sig);
+
+ for (int i = 0; i < GetSize(sig); i++)
+ if (partial_assignment_bits.count(sig[i]))
+ {
+ int width = 1;
+ const auto &bit_a = partial_assignment_bits.at(sig[i]);
+
+ while (i+width < GetSize(sig))
+ {
+ if (!partial_assignment_bits.count(sig[i+width]))
+ break;
+
+ const auto &bit_b = partial_assignment_bits.at(sig[i+width]);
+ if (strcmp(bit_a.first, bit_b.first))
+ break;
+ if (bit_a.second+width != bit_b.second)
+ break;
+
+ width++;
+ }
+
+ if (i+width < GetSize(sig))
+ s = stringf("%s :: ", rvalue(sig.extract(i+width, GetSize(sig)-(width+i))));
+
+ s += stringf("%s[%d:%d]", bit_a.first, bit_a.second+width-1, bit_a.second);
+
+ if (i > 0)
+ s += stringf(" :: %s", rvalue(sig.extract(0, i)));
+
+ count_chunks = 3;
+ goto continue_with_resize;
+ }
+
+ for (auto &c : sig.chunks()) {
+ count_chunks++;
+ if (!s.empty())
+ s = " :: " + s;
+ if (c.wire) {
+ if (c.offset != 0 || c.width != c.wire->width)
+ s = stringf("%s[%d:%d]", cid(c.wire->name), c.offset+c.width-1, c.offset) + s;
+ else
+ s = cid(c.wire->name) + s;
+ } else {
+ string v = stringf("0ub%d_", c.width);
+ for (int i = c.width-1; i >= 0; i--)
+ v += c.data.at(i) == State::S1 ? '1' : '0';
+ s = v + s;
+ }
+ }
+
+ continue_with_resize:;
+ if (width >= 0) {
+ if (is_signed) {
+ if (GetSize(sig) > width)
+ s = stringf("signed(resize(%s, %d))", s.c_str(), width);
+ else
+ s = stringf("resize(signed(%s), %d)", s.c_str(), width);
+ } else
+ s = stringf("resize(%s, %d)", s.c_str(), width);
+ } else if (is_signed)
+ s = stringf("signed(%s)", s.c_str());
+ else if (count_chunks > 1)
+ s = stringf("(%s)", s.c_str());
+
+ strbuf.push_back(s);
+ return strbuf.back().c_str();
+ }
+
+ const char *rvalue_u(SigSpec sig, int width = -1)
+ {
+ return rvalue(sig, width, false);
+ }
+
+ const char *rvalue_s(SigSpec sig, int width = -1, bool is_signed = true)
+ {
+ return rvalue(sig, width, is_signed);
+ }
+
+ const char *lvalue(SigSpec sig)
+ {
+ sigmap.apply(sig);
+
+ if (sig.is_wire())
+ return rvalue(sig);
+
+ const char *temp_id = cid();
+ f << stringf(" %s : unsigned word[%d]; -- %s\n", temp_id, GetSize(sig), log_signal(sig));
+
+ int offset = 0;
+ for (auto bit : sig) {
+ log_assert(bit.wire != nullptr);
+ partial_assignment_wires.insert(bit.wire);
+ partial_assignment_bits[bit] = std::pair<const char*, int>(temp_id, offset++);
+ }
+
+ return temp_id;
+ }
+
+ 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->attributes.count("\\init"))
+ assignments.push_back(stringf("init(%s) := %s;", lvalue(wire), rvalue(wire->attributes.at("\\init"))));
+ }
+
+ for (auto cell : module->cells())
+ {
+ // FIXME: $slice, $concat, $mem
+
+ if (cell->type.in("$assert"))
+ {
+ SigSpec sig_a = cell->getPort("\\A");
+ SigSpec sig_en = cell->getPort("\\EN");
+
+ invarspecs.push_back(stringf("!bool(%s) | bool(%s);", rvalue(sig_en), rvalue(sig_a)));
+
+ continue;
+ }
+
+ if (cell->type.in("$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx"))
+ {
+ SigSpec sig_a = cell->getPort("\\A");
+ SigSpec sig_b = cell->getPort("\\B");
+
+ int width_y = GetSize(cell->getPort("\\Y"));
+ int shift_b_width = GetSize(sig_b);
+ int width_ay = max(GetSize(sig_a), width_y);
+ int width = width_ay;
+
+ for (int i = 1, j = 0;; i <<= 1, j++)
+ if (width_ay < i) {
+ width = i-1;
+ shift_b_width = min(shift_b_width, j);
+ break;
+ }
+
+ bool signed_a = cell->getParam("\\A_SIGNED").as_bool();
+ bool signed_b = cell->getParam("\\B_SIGNED").as_bool();
+ string op = cell->type.in("$shl", "$sshl") ? "<<" : ">>";
+ string expr, expr_a;
+
+ if (cell->type == "$sshr" && signed_a)
+ {
+ expr_a = rvalue_s(sig_a, width);
+ expr = stringf("resize(unsigned(%s %s %s), %d)", expr_a.c_str(), op.c_str(), rvalue(sig_b.extract(0, shift_b_width)), width_y);
+ if (shift_b_width < GetSize(sig_b))
+ expr = stringf("%s != 0ud%d_0 ? (bool(%s) ? !0ud%d_0 : 0ud%d_0) : %s",
+ rvalue(sig_b.extract(shift_b_width, GetSize(sig_b) - shift_b_width)), GetSize(sig_b) - shift_b_width,
+ rvalue(sig_a[GetSize(sig_a)-1]), width_y, width_y, expr.c_str());
+ }
+ else if (cell->type.in("$shift", "$shiftx") && signed_b)
+ {
+ expr_a = rvalue_u(sig_a, width);
+
+ 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)));
+
+ 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);
+
+ if (shift_b_width < GetSize(sig_b)) {
+ expr_shl = stringf("%s[%d:%d] != 0ud%d_0 ? 0ud%d_0 : %s", b_shl, GetSize(sig_b)-1, shift_b_width,
+ GetSize(sig_b)-shift_b_width, width_y, expr_shl.c_str());
+ expr_shr = stringf("%s[%d:%d] != 0ud%d_0 ? 0ud%d_0 : %s", b_shr, GetSize(sig_b)-1, shift_b_width,
+ GetSize(sig_b)-shift_b_width, width_y, expr_shr.c_str());
+ }
+
+ expr = stringf("bool(%s) ? %s : %s", rvalue(sig_b[GetSize(sig_b)-1]), expr_shl.c_str(), expr_shr.c_str());
+ }
+ else
+ {
+ if (cell->type.in("$shift", "$shiftx") || !signed_a)
+ expr_a = rvalue_u(sig_a, width);
+ else
+ expr_a = stringf("resize(unsigned(%s), %d)", rvalue_s(sig_a, width_ay), width);
+
+ expr = stringf("resize(%s %s %s[%d:0], %d)", expr_a.c_str(), op.c_str(), rvalue_u(sig_b), shift_b_width-1, width_y);
+ if (shift_b_width < GetSize(sig_b))
+ expr = stringf("%s[%d:%d] != 0ud%d_0 ? 0ud%d_0 : %s", rvalue_u(sig_b), GetSize(sig_b)-1, shift_b_width,
+ GetSize(sig_b)-shift_b_width, width_y, expr.c_str());
+ }
+
+ assignments.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str()));
+
+ continue;
+ }
+
+ if (cell->type.in("$not", "$pos", "$neg"))
+ {
+ int width = GetSize(cell->getPort("\\Y"));
+ string expr_a, op;
+
+ if (cell->type == "$not") op = "!";
+ if (cell->type == "$pos") op = "";
+ if (cell->type == "$neg") op = "-";
+
+ if (cell->getParam("\\A_SIGNED").as_bool())
+ {
+ assignments.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")),
+ op.c_str(), rvalue_u(cell->getPort("\\A"), width)));
+ }
+
+ continue;
+ }
+
+ if (cell->type.in("$add", "$sub", "$mul", "$and", "$or", "$xor", "$xnor"))
+ {
+ int width = GetSize(cell->getPort("\\Y"));
+ string expr_a, expr_b, op;
+
+ if (cell->type == "$add") op = "+";
+ if (cell->type == "$sub") op = "-";
+ if (cell->type == "$mul") op = "*";
+ if (cell->type == "$and") op = "&";
+ if (cell->type == "$or") op = "|";
+ if (cell->type == "$xor") op = "xor";
+ if (cell->type == "$xnor") op = "xnor";
+
+ if (cell->getParam("\\A_SIGNED").as_bool())
+ {
+ assignments.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")),
+ rvalue_u(cell->getPort("\\A"), width), op.c_str(), rvalue_u(cell->getPort("\\B"), width)));
+ }
+
+ continue;
+ }
+
+ if (cell->type.in("$div", "$mod"))
+ {
+ int width_y = GetSize(cell->getPort("\\Y"));
+ int width = max(width_y, GetSize(cell->getPort("\\A")));
+ width = max(width, GetSize(cell->getPort("\\B")));
+ string expr_a, expr_b, op;
+
+ if (cell->type == "$div") op = "/";
+ if (cell->type == "$mod") op = "mod";
+
+ if (cell->getParam("\\A_SIGNED").as_bool())
+ {
+ assignments.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")),
+ rvalue_u(cell->getPort("\\A"), width), op.c_str(), rvalue_u(cell->getPort("\\B"), width), width_y));
+ }
+
+ continue;
+ }
+
+ if (cell->type.in("$eq", "$ne", "$eqx", "$nex", "$lt", "$le", "$ge", "$gt"))
+ {
+ int width = max(GetSize(cell->getPort("\\A")), GetSize(cell->getPort("\\B")));
+ string expr_a, expr_b, op;
+
+ if (cell->type == "$eq") op = "=";
+ if (cell->type == "$ne") op = "!=";
+ if (cell->type == "$eqx") op = "=";
+ if (cell->type == "$nex") op = "!=";
+ if (cell->type == "$lt") op = "<";
+ if (cell->type == "$le") op = "<=";
+ if (cell->type == "$ge") op = ">=";
+ if (cell->type == "$gt") op = ">";
+
+ if (cell->getParam("\\A_SIGNED").as_bool())
+ {
+ expr_a = stringf("resize(signed(%s), %d)", rvalue(cell->getPort("\\A")), width);
+ expr_b = stringf("resize(signed(%s), %d)", rvalue(cell->getPort("\\B")), width);
+ }
+ else
+ {
+ expr_a = stringf("resize(%s, %d)", rvalue(cell->getPort("\\A")), width);
+ expr_b = stringf("resize(%s, %d)", rvalue(cell->getPort("\\B")), width);
+ }
+
+ assignments.push_back(stringf("%s := resize(word1(%s %s %s), %d);", lvalue(cell->getPort("\\Y")),
+ expr_a.c_str(), op.c_str(), expr_b.c_str(), GetSize(cell->getPort("\\Y"))));
+
+ continue;
+ }
+
+ if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool"))
+ {
+ int width_a = GetSize(cell->getPort("\\A"));
+ int width_y = GetSize(cell->getPort("\\Y"));
+ const char *expr_a = rvalue(cell->getPort("\\A"));
+ const char *expr_y = lvalue(cell->getPort("\\Y"));
+ string expr;
+
+ if (cell->type == "$reduce_and") expr = stringf("%s = !0ub%d_0", expr_a, width_a);
+ if (cell->type == "$reduce_or") expr = stringf("%s != 0ub%d_0", expr_a, width_a);
+ if (cell->type == "$reduce_bool") expr = stringf("%s != 0ub%d_0", expr_a, width_a);
+
+ assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y));
+ continue;
+ }
+
+ if (cell->type.in("$reduce_xor", "$reduce_xnor"))
+ {
+ int width_y = GetSize(cell->getPort("\\Y"));
+ const char *expr_y = lvalue(cell->getPort("\\Y"));
+ string expr;
+
+ for (auto bit : cell->getPort("\\A")) {
+ if (!expr.empty())
+ expr += " xor ";
+ expr += rvalue(bit);
+ }
+
+ if (cell->type == "$reduce_xnor")
+ expr = "!(" + expr + ")";
+
+ assignments.push_back(stringf("%s := resize(%s, %d);", expr_y, expr.c_str(), width_y));
+ continue;
+ }
+
+ if (cell->type.in("$logic_and", "$logic_or"))
+ {
+ int width_a = GetSize(cell->getPort("\\A"));
+ int width_b = GetSize(cell->getPort("\\B"));
+ int width_y = GetSize(cell->getPort("\\Y"));
+
+ string expr_a = stringf("(%s != 0ub%d_0)", rvalue(cell->getPort("\\A")), width_a);
+ string expr_b = stringf("(%s != 0ub%d_0)", rvalue(cell->getPort("\\B")), width_b);
+ const char *expr_y = lvalue(cell->getPort("\\Y"));
+
+ string expr;
+ 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));
+ continue;
+ }
+
+ if (cell->type.in("$logic_not"))
+ {
+ int width_a = GetSize(cell->getPort("\\A"));
+ int width_y = GetSize(cell->getPort("\\Y"));
+
+ 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));
+ continue;
+ }
+
+ if (cell->type.in("$mux", "$pmux"))
+ {
+ int width = GetSize(cell->getPort("\\Y"));
+ SigSpec sig_a = cell->getPort("\\A");
+ SigSpec sig_b = cell->getPort("\\B");
+ SigSpec sig_s = cell->getPort("\\S");
+
+ string expr;
+ for (int i = 0; i < GetSize(sig_s); i++)
+ 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()));
+ continue;
+ }
+
+ if (cell->type == "$dff")
+ {
+ assignments.push_back(stringf("next(%s) := %s;", lvalue(cell->getPort("\\Q")), rvalue(cell->getPort("\\D"))));
+ continue;
+ }
+
+ 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"))));
+ continue;
+ }
+
+ if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_"))
+ {
+ string op;
+
+ if (cell->type.in("$_AND_", "$_NAND_")) op = "&";
+ if (cell->type.in("$_OR_", "$_NOR_")) op = "|";
+ if (cell->type.in("$_XOR_")) op = "xor";
+ if (cell->type.in("$_XNOR_")) op = "xnor";
+
+ if (cell->type.in("$_NAND_", "$_NOR_"))
+ assignments.push_back(stringf("%s := !(%s %s %s);", lvalue(cell->getPort("\\Y")),
+ rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B"))));
+ else
+ assignments.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")),
+ 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")),
+ 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")),
+ 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")),
+ 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")),
+ rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")), rvalue(cell->getPort("\\D"))));
+ continue;
+ }
+
+ 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));
+
+ 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)));
+ else
+ assignments.push_back(stringf("%s.%s := %s;", cid(cell->name), cid(conn.first), rvalue(conn.second)));
+ }
+
+ for (Wire *wire : partial_assignment_wires)
+ {
+ string expr;
+
+ for (int i = 0; i < wire->width; i++)
+ {
+ if (!expr.empty())
+ expr = " :: " + expr;
+
+ if (partial_assignment_bits.count(sigmap(SigBit(wire, i))))
+ {
+ int width = 1;
+ const auto &bit_a = partial_assignment_bits.at(sigmap(SigBit(wire, i)));
+
+ while (i+1 < wire->width)
+ {
+ SigBit next_bit = sigmap(SigBit(wire, i+1));
+
+ if (!partial_assignment_bits.count(next_bit))
+ break;
+
+ const auto &bit_b = partial_assignment_bits.at(next_bit);
+ if (strcmp(bit_a.first, bit_b.first))
+ break;
+ if (bit_a.second+width != bit_b.second)
+ break;
+
+ width++, i++;
+ }
+
+ expr = stringf("%s[%d:%d]", bit_a.first, bit_a.second+width-1, bit_a.second) + expr;
+ }
+ else if (sigmap(SigBit(wire, i)).wire == nullptr)
+ {
+ string bits;
+ SigSpec sig = sigmap(SigSpec(wire, i));
+
+ while (i+1 < wire->width) {
+ SigBit next_bit = sigmap(SigBit(wire, i+1));
+ if (next_bit.wire != nullptr)
+ break;
+ sig.append(next_bit);
+ i++;
+ }
+
+ for (int k = GetSize(sig)-1; k >= 0; k--)
+ bits += sig[k] == State::S1 ? '1' : '0';
+
+ expr = stringf("0ub%d_%s", GetSize(bits), bits.c_str()) + expr;
+ }
+ else if (sigmap(SigBit(wire, i)) == SigBit(wire, i))
+ {
+ int length = 1;
+
+ while (i+1 < wire->width) {
+ if (partial_assignment_bits.count(sigmap(SigBit(wire, i+1))))
+ break;
+ if (sigmap(SigBit(wire, i+1)) != SigBit(wire, i+1))
+ break;
+ i++, length++;
+ }
+
+ expr = stringf("0ub%d_0", length) + expr;
+ }
+ else
+ {
+ string bits;
+ SigSpec sig = sigmap(SigSpec(wire, i));
+
+ while (i+1 < wire->width) {
+ SigBit next_bit = sigmap(SigBit(wire, i+1));
+ if (next_bit.wire == nullptr || partial_assignment_bits.count(next_bit))
+ break;
+ sig.append(next_bit);
+ i++;
+ }
+
+ expr = rvalue(sig) + expr;
+ }
+ }
+
+ assignments.push_back(stringf("%s := %s;", cid(wire->name), expr.c_str()));
+ }
+
+ if (!assignments.empty()) {
+ f << stringf(" ASSIGN\n");
+ for (const string &line : assignments)
+ f << stringf(" %s\n", line.c_str());
+ }
+
+ if (!invarspecs.empty()) {
+ for (const string &line : invarspecs)
+ f << stringf(" INVARSPEC %s\n", line.c_str());
+ }
+ }
+};
+
+struct SmvBackend : public Backend {
+ SmvBackend() : Backend("smv", "write design to SMV file") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" write_smv [options] [filename]\n");
+ log("\n");
+ log("Write an SMV description of the current design.\n");
+ log("\n");
+ log(" -verbose\n");
+ log(" this will print the recursive walk used to export the modules.\n");
+ log("\n");
+ log(" -tpl <template_file>\n");
+ log(" use the given template file. the line containing only the token '%%%%'\n");
+ log(" is replaced with the regular output of this command.\n");
+ log("\n");
+ log("THIS COMMAND IS UNDER CONSTRUCTION\n");
+ log("\n");
+ }
+ virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::ifstream template_f;
+ bool verbose = false;
+
+ log_header(design, "Executing SMV backend.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-tpl" && argidx+1 < args.size()) {
+ template_f.open(args[++argidx]);
+ if (template_f.fail())
+ log_error("Can't open template file `%s'.\n", args[argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-verbose") {
+ verbose = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ pool<Module*> modules;
+
+ for (auto module : design->modules())
+ if (!module->get_bool_attribute("\\blackbox") && !module->has_memories_warn() && !module->has_processes_warn())
+ modules.insert(module);
+
+ if (template_f.is_open())
+ {
+ std::string line;
+ while (std::getline(template_f, line))
+ {
+ int indent = 0;
+ while (indent < GetSize(line) && (line[indent] == ' ' || line[indent] == '\t'))
+ indent++;
+
+ if (line[indent] == '%')
+ {
+ vector<string> stmt = split_tokens(line);
+
+ if (GetSize(stmt) == 1 && stmt[0] == "%%")
+ break;
+
+ if (GetSize(stmt) == 2 && stmt[0] == "%module")
+ {
+ Module *module = design->module(RTLIL::escape_id(stmt[1]));
+ modules.erase(module);
+
+ if (module == nullptr)
+ log_error("Module '%s' not found.\n", stmt[1].c_str());
+
+ *f << stringf("-- SMV description generated by %s\n", yosys_version_str);
+
+ log("Creating SMV representation of module %s.\n", log_id(module));
+ SmvWorker worker(module, verbose, *f);
+ worker.run();
+
+ *f << stringf("-- end of yosys output\n");
+ continue;
+ }
+
+ log_error("Unknown template statement: '%s'", line.c_str() + indent);
+ }
+
+ *f << line << std::endl;
+ }
+ }
+
+ if (!modules.empty())
+ {
+ *f << stringf("-- SMV description generated by %s\n", yosys_version_str);
+
+ for (auto module : modules) {
+ log("Creating SMV representation of module %s.\n", log_id(module));
+ SmvWorker worker(module, verbose, *f);
+ worker.run();
+ }
+
+ *f << stringf("-- end of yosys output\n");
+ }
+
+ if (template_f.is_open()) {
+ std::string line;
+ while (std::getline(template_f, line))
+ *f << line << std::endl;
+ }
+ }
+} SmvBackend;
+
+PRIVATE_NAMESPACE_END
diff --git a/backends/smv/test_cells.sh b/backends/smv/test_cells.sh
new file mode 100644
index 00000000..63de465c
--- /dev/null
+++ b/backends/smv/test_cells.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+set -ex
+
+rm -rf test_cells.tmp
+mkdir -p test_cells.tmp
+cd test_cells.tmp
+
+# don't test $mul to reduce runtime
+# don't test $div and $mod to reduce runtime and avoid "div by zero" message
+../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$macc /$mul /$div /$mod'
+
+cat > template.txt << "EOT"
+%module main
+ INVARSPEC ! bool(_trigger);
+EOT
+
+for fn in test_*.il; do
+ ../../../yosys -p "
+ read_ilang $fn
+ rename gold gate
+ synth
+
+ read_ilang $fn
+ miter -equiv -flatten gold gate main
+ hierarchy -top main
+ write_smv -tpl template.txt ${fn#.il}.smv
+ "
+ nuXmv -dynamic ${fn#.il}.smv > ${fn#.il}.out
+done
+
+grep '^-- invariant .* is false' *.out || echo 'All OK.'
+
diff --git a/backends/spice/Makefile.inc b/backends/spice/Makefile.inc
new file mode 100644
index 00000000..9c8530cb
--- /dev/null
+++ b/backends/spice/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += backends/spice/spice.o
+
diff --git a/backends/spice/spice.cc b/backends/spice/spice.cc
new file mode 100644
index 00000000..4101cbf9
--- /dev/null
+++ b/backends/spice/spice.cc
@@ -0,0 +1,266 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/rtlil.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
+#include "kernel/log.h"
+#include <string>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+static string spice_id2str(IdString id)
+{
+ static const char *escape_chars = "$\\[]()<>=";
+ string s = RTLIL::unescape_id(id);
+
+ for (auto &ch : s)
+ if (strchr(escape_chars, ch) != nullptr) ch = '_';
+
+ return s;
+}
+
+static string spice_id2str(IdString id, bool use_inames, idict<IdString, 1> &inums)
+{
+ if (!use_inames && *id.c_str() == '$')
+ return stringf("%d", inums(id));
+ return spice_id2str(id);
+}
+
+static void print_spice_net(std::ostream &f, RTLIL::SigBit s, std::string &neg, std::string &pos, std::string &ncpf, int &nc_counter, bool use_inames, idict<IdString, 1> &inums)
+{
+ if (s.wire) {
+ if (s.wire->port_id)
+ use_inames = true;
+ if (s.wire->width > 1)
+ f << stringf(" %s.%d", spice_id2str(s.wire->name, use_inames, inums).c_str(), s.offset);
+ else
+ f << stringf(" %s", spice_id2str(s.wire->name, use_inames, inums).c_str());
+ } else {
+ if (s == RTLIL::State::S0)
+ f << stringf(" %s", neg.c_str());
+ else if (s == RTLIL::State::S1)
+ f << stringf(" %s", pos.c_str());
+ else
+ f << stringf(" %s%d", ncpf.c_str(), nc_counter++);
+ }
+}
+
+static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, std::string &neg, std::string &pos, std::string &ncpf, bool big_endian, bool use_inames)
+{
+ SigMap sigmap(module);
+ idict<IdString, 1> inums;
+ int cell_counter = 0, conn_counter = 0, nc_counter = 0;
+
+ for (auto &cell_it : module->cells_)
+ {
+ RTLIL::Cell *cell = cell_it.second;
+ f << stringf("X%d", cell_counter++);
+
+ std::vector<RTLIL::SigSpec> port_sigs;
+
+ if (design->modules_.count(cell->type) == 0)
+ {
+ log_warning("no (blackbox) module for cell type `%s' (%s.%s) found! Guessing order of ports.\n",
+ log_id(cell->type), log_id(module), log_id(cell));
+ for (auto &conn : cell->connections()) {
+ RTLIL::SigSpec sig = sigmap(conn.second);
+ port_sigs.push_back(sig);
+ }
+ }
+ else
+ {
+ RTLIL::Module *mod = design->modules_.at(cell->type);
+
+ std::vector<RTLIL::Wire*> ports;
+ for (auto wire_it : mod->wires_) {
+ RTLIL::Wire *wire = wire_it.second;
+ if (wire->port_id == 0)
+ continue;
+ while (int(ports.size()) < wire->port_id)
+ ports.push_back(NULL);
+ ports.at(wire->port_id-1) = wire;
+ }
+
+ for (RTLIL::Wire *wire : ports) {
+ log_assert(wire != NULL);
+ RTLIL::SigSpec sig(RTLIL::State::Sz, wire->width);
+ if (cell->hasPort(wire->name)) {
+ sig = sigmap(cell->getPort(wire->name));
+ sig.extend_u0(wire->width, false);
+ }
+ port_sigs.push_back(sig);
+ }
+ }
+
+ for (auto &sig : port_sigs) {
+ for (int i = 0; i < sig.size(); i++) {
+ RTLIL::SigSpec s = sig.extract(big_endian ? sig.size() - 1 - i : i, 1);
+ print_spice_net(f, s, neg, pos, ncpf, nc_counter, use_inames, inums);
+ }
+ }
+
+ f << stringf(" %s\n", spice_id2str(cell->type).c_str());
+ }
+
+ for (auto &conn : module->connections())
+ for (int i = 0; i < conn.first.size(); i++) {
+ f << stringf("V%d", conn_counter++);
+ print_spice_net(f, conn.first.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums);
+ print_spice_net(f, conn.second.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums);
+ f << stringf(" DC 0\n");
+ }
+}
+
+struct SpiceBackend : public Backend {
+ SpiceBackend() : Backend("spice", "write design to SPICE netlist file") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" write_spice [options] [filename]\n");
+ log("\n");
+ log("Write the current design to an SPICE netlist file.\n");
+ log("\n");
+ log(" -big_endian\n");
+ log(" generate multi-bit ports in MSB first order\n");
+ log(" (default is LSB first)\n");
+ log("\n");
+ log(" -neg net_name\n");
+ log(" set the net name for constant 0 (default: Vss)\n");
+ log("\n");
+ log(" -pos net_name\n");
+ log(" set the net name for constant 1 (default: Vdd)\n");
+ log("\n");
+ log(" -nc_prefix\n");
+ log(" prefix for not-connected nets (default: _NC)\n");
+ log("\n");
+ log(" -inames\n");
+ log(" include names of internal ($-prefixed) nets in outputs\n");
+ log(" (default is to use net numbers instead)\n");
+ log("\n");
+ log(" -top top_module\n");
+ log(" set the specified module as design top module\n");
+ log("\n");
+ }
+ virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::string top_module_name;
+ RTLIL::Module *top_module = NULL;
+ bool big_endian = false, use_inames = false;
+ std::string neg = "Vss", pos = "Vdd", ncpf = "_NC";
+
+ log_header(design, "Executing SPICE backend.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-big_endian") {
+ big_endian = true;
+ continue;
+ }
+ if (args[argidx] == "-inames") {
+ use_inames = true;
+ continue;
+ }
+ if (args[argidx] == "-neg" && argidx+1 < args.size()) {
+ neg = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-pos" && argidx+1 < args.size()) {
+ pos = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-nc_prefix" && argidx+1 < args.size()) {
+ ncpf = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-top" && argidx+1 < args.size()) {
+ top_module_name = args[++argidx];
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ if (top_module_name.empty())
+ for (auto & mod_it:design->modules_)
+ if (mod_it.second->get_bool_attribute("\\top"))
+ top_module_name = mod_it.first.str();
+
+ *f << stringf("* SPICE netlist generated by %s\n", yosys_version_str);
+ *f << stringf("\n");
+
+ for (auto module_it : design->modules_)
+ {
+ RTLIL::Module *module = module_it.second;
+ if (module->get_bool_attribute("\\blackbox"))
+ continue;
+
+ if (module->processes.size() != 0)
+ log_error("Found unmapped processes in module %s: unmapped processes are not supported in SPICE backend!\n", log_id(module));
+ if (module->memories.size() != 0)
+ log_error("Found unmapped memories in module %s: unmapped memories are not supported in SPICE backend!\n", log_id(module));
+
+ if (module->name == RTLIL::escape_id(top_module_name)) {
+ top_module = module;
+ continue;
+ }
+
+ std::vector<RTLIL::Wire*> ports;
+ for (auto wire_it : module->wires_) {
+ RTLIL::Wire *wire = wire_it.second;
+ if (wire->port_id == 0)
+ continue;
+ while (int(ports.size()) < wire->port_id)
+ ports.push_back(NULL);
+ ports.at(wire->port_id-1) = wire;
+ }
+
+ *f << stringf(".SUBCKT %s", spice_id2str(module->name).c_str());
+ for (RTLIL::Wire *wire : ports) {
+ log_assert(wire != NULL);
+ if (wire->width > 1) {
+ for (int i = 0; i < wire->width; i++)
+ *f << stringf(" %s.%d", spice_id2str(wire->name).c_str(), big_endian ? wire->width - 1 - i : i);
+ } else
+ *f << stringf(" %s", spice_id2str(wire->name).c_str());
+ }
+ *f << stringf("\n");
+ print_spice_module(*f, module, design, neg, pos, ncpf, big_endian, use_inames);
+ *f << stringf(".ENDS %s\n\n", spice_id2str(module->name).c_str());
+ }
+
+ if (!top_module_name.empty()) {
+ if (top_module == NULL)
+ log_error("Can't find top module `%s'!\n", top_module_name.c_str());
+ print_spice_module(*f, top_module, design, neg, pos, ncpf, big_endian, use_inames);
+ *f << stringf("\n");
+ }
+
+ *f << stringf("************************\n");
+ *f << stringf("* end of SPICE netlist *\n");
+ *f << stringf("************************\n");
+ *f << stringf("\n");
+ }
+} SpiceBackend;
+
+PRIVATE_NAMESPACE_END
diff --git a/backends/verilog/Makefile.inc b/backends/verilog/Makefile.inc
new file mode 100644
index 00000000..c2dffef7
--- /dev/null
+++ b/backends/verilog/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += backends/verilog/verilog_backend.o
+
diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc
new file mode 100644
index 00000000..a617215f
--- /dev/null
+++ b/backends/verilog/verilog_backend.cc
@@ -0,0 +1,1507 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * A simple and straightforward Verilog backend.
+ *
+ */
+
+#include "kernel/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/log.h"
+#include "kernel/sigtools.h"
+#include <string>
+#include <sstream>
+#include <set>
+#include <map>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+bool verbose, norename, noattr, attr2comment, noexpr, nodec, nostr, defparam;
+int auto_name_counter, auto_name_offset, auto_name_digits;
+std::map<RTLIL::IdString, int> auto_name_map;
+std::set<RTLIL::IdString> reg_wires, reg_ct;
+std::string auto_prefix;
+
+RTLIL::Module *active_module;
+
+void reset_auto_counter_id(RTLIL::IdString id, bool may_rename)
+{
+ const char *str = id.c_str();
+
+ if (*str == '$' && may_rename && !norename)
+ auto_name_map[id] = auto_name_counter++;
+
+ if (str[0] != '\\' || str[1] != '_' || str[2] == 0)
+ return;
+
+ for (int i = 2; str[i] != 0; i++) {
+ if (str[i] == '_' && str[i+1] == 0)
+ continue;
+ if (str[i] < '0' || str[i] > '9')
+ return;
+ }
+
+ int num = atoi(str+2);
+ if (num >= auto_name_offset)
+ auto_name_offset = num + 1;
+}
+
+void reset_auto_counter(RTLIL::Module *module)
+{
+ auto_name_map.clear();
+ auto_name_counter = 0;
+ auto_name_offset = 0;
+
+ reset_auto_counter_id(module->name, false);
+
+ for (auto it = module->wires_.begin(); it != module->wires_.end(); ++it)
+ reset_auto_counter_id(it->second->name, true);
+
+ for (auto it = module->cells_.begin(); it != module->cells_.end(); ++it) {
+ reset_auto_counter_id(it->second->name, true);
+ reset_auto_counter_id(it->second->type, false);
+ }
+
+ for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
+ reset_auto_counter_id(it->second->name, false);
+
+ auto_name_digits = 1;
+ for (size_t i = 10; i < auto_name_offset + auto_name_map.size(); i = i*10)
+ auto_name_digits++;
+
+ if (verbose)
+ for (auto it = auto_name_map.begin(); it != auto_name_map.end(); ++it)
+ log(" renaming `%s' to `%s_%0*d_'.\n", it->first.c_str(), auto_prefix.c_str(), auto_name_digits, auto_name_offset + it->second);
+}
+
+std::string next_auto_id()
+{
+ return stringf("%s_%0*d_", auto_prefix.c_str(), auto_name_digits, auto_name_offset + auto_name_counter++);
+}
+
+std::string id(RTLIL::IdString internal_id, bool may_rename = true)
+{
+ const char *str = internal_id.c_str();
+ bool do_escape = false;
+
+ if (may_rename && auto_name_map.count(internal_id) != 0)
+ return stringf("%s_%0*d_", auto_prefix.c_str(), auto_name_digits, auto_name_offset + auto_name_map[internal_id]);
+
+ if (*str == '\\')
+ str++;
+
+ if ('0' <= *str && *str <= '9')
+ do_escape = true;
+
+ for (int i = 0; str[i]; i++)
+ {
+ if ('0' <= str[i] && str[i] <= '9')
+ continue;
+ if ('a' <= str[i] && str[i] <= 'z')
+ continue;
+ if ('A' <= str[i] && str[i] <= 'Z')
+ continue;
+ if (str[i] == '_')
+ continue;
+ do_escape = true;
+ break;
+ }
+
+ if (do_escape)
+ return "\\" + std::string(str) + " ";
+ return std::string(str);
+}
+
+bool is_reg_wire(RTLIL::SigSpec sig, std::string &reg_name)
+{
+ if (!sig.is_chunk() || sig.as_chunk().wire == NULL)
+ return false;
+
+ RTLIL::SigChunk chunk = sig.as_chunk();
+
+ if (reg_wires.count(chunk.wire->name) == 0)
+ return false;
+
+ reg_name = id(chunk.wire->name);
+ if (sig.size() != chunk.wire->width) {
+ if (sig.size() == 1)
+ reg_name += stringf("[%d]", chunk.wire->start_offset + chunk.offset);
+ else if (chunk.wire->upto)
+ reg_name += stringf("[%d:%d]", (chunk.wire->width - (chunk.offset + chunk.width - 1) - 1) + chunk.wire->start_offset,
+ (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset);
+ else
+ reg_name += stringf("[%d:%d]", chunk.wire->start_offset + chunk.offset + chunk.width - 1,
+ chunk.wire->start_offset + chunk.offset);
+ }
+
+ return true;
+}
+
+void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool set_signed = false, bool escape_comment = false)
+{
+ if (width < 0)
+ width = data.bits.size() - offset;
+ if (nostr)
+ goto dump_bits;
+ if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) {
+ if (width == 32 && !no_decimal && !nodec) {
+ int32_t val = 0;
+ for (int i = offset+width-1; i >= offset; i--) {
+ log_assert(i < (int)data.bits.size());
+ if (data.bits[i] != RTLIL::S0 && data.bits[i] != RTLIL::S1)
+ goto dump_bits;
+ if (data.bits[i] == RTLIL::S1)
+ val |= 1 << (i - offset);
+ }
+ if (set_signed && val < 0)
+ f << stringf("-32'sd%u", -val);
+ else
+ f << stringf("32'%sd%u", set_signed ? "s" : "", val);
+ } else {
+ dump_bits:
+ f << stringf("%d'%sb", width, set_signed ? "s" : "");
+ if (width == 0)
+ f << stringf("0");
+ for (int i = offset+width-1; i >= offset; i--) {
+ log_assert(i < (int)data.bits.size());
+ switch (data.bits[i]) {
+ case RTLIL::S0: f << stringf("0"); break;
+ case RTLIL::S1: f << stringf("1"); break;
+ case RTLIL::Sx: f << stringf("x"); break;
+ case RTLIL::Sz: f << stringf("z"); break;
+ case RTLIL::Sa: f << stringf("z"); break;
+ case RTLIL::Sm: log_error("Found marker state in final netlist.");
+ }
+ }
+ }
+ } else {
+ f << stringf("\"");
+ std::string str = data.decode_string();
+ for (size_t i = 0; i < str.size(); i++) {
+ if (str[i] == '\n')
+ f << stringf("\\n");
+ else if (str[i] == '\t')
+ f << stringf("\\t");
+ else if (str[i] < 32)
+ f << stringf("\\%03o", str[i]);
+ else if (str[i] == '"')
+ f << stringf("\\\"");
+ else if (str[i] == '\\')
+ f << stringf("\\\\");
+ else if (str[i] == '/' && escape_comment && i > 0 && str[i-1] == '*')
+ f << stringf("\\/");
+ else
+ f << str[i];
+ }
+ f << stringf("\"");
+ }
+}
+
+void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decimal = false)
+{
+ if (chunk.wire == NULL) {
+ dump_const(f, chunk.data, chunk.width, chunk.offset, no_decimal);
+ } else {
+ if (chunk.width == chunk.wire->width && chunk.offset == 0) {
+ f << stringf("%s", id(chunk.wire->name).c_str());
+ } else if (chunk.width == 1) {
+ if (chunk.wire->upto)
+ f << stringf("%s[%d]", id(chunk.wire->name).c_str(), (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset);
+ else
+ f << stringf("%s[%d]", id(chunk.wire->name).c_str(), chunk.offset + chunk.wire->start_offset);
+ } else {
+ if (chunk.wire->upto)
+ f << stringf("%s[%d:%d]", id(chunk.wire->name).c_str(),
+ (chunk.wire->width - (chunk.offset + chunk.width - 1) - 1) + chunk.wire->start_offset,
+ (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset);
+ else
+ f << stringf("%s[%d:%d]", id(chunk.wire->name).c_str(),
+ (chunk.offset + chunk.width - 1) + chunk.wire->start_offset,
+ chunk.offset + chunk.wire->start_offset);
+ }
+ }
+}
+
+void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig)
+{
+ if (sig.is_chunk()) {
+ dump_sigchunk(f, sig.as_chunk());
+ } else {
+ f << stringf("{ ");
+ for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); ++it) {
+ if (it != sig.chunks().rbegin())
+ f << stringf(", ");
+ dump_sigchunk(f, *it, true);
+ }
+ f << stringf(" }");
+ }
+}
+
+void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n', bool modattr = false)
+{
+ if (noattr)
+ return;
+ for (auto it = attributes.begin(); it != attributes.end(); ++it) {
+ f << stringf("%s" "%s %s", indent.c_str(), attr2comment ? "/*" : "(*", id(it->first).c_str());
+ f << stringf(" = ");
+ if (modattr && (it->second == Const(0, 1) || it->second == Const(0)))
+ f << stringf(" 0 ");
+ else if (modattr && (it->second == Const(1, 1) || it->second == Const(1)))
+ f << stringf(" 1 ");
+ else
+ dump_const(f, it->second, -1, 0, false, false, attr2comment);
+ f << stringf(" %s%c", attr2comment ? "*/" : "*)", term);
+ }
+}
+
+void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire)
+{
+ dump_attributes(f, indent, wire->attributes);
+#if 0
+ if (wire->port_input && !wire->port_output)
+ f << stringf("%s" "input %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
+ else if (!wire->port_input && wire->port_output)
+ f << stringf("%s" "output %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
+ else if (wire->port_input && wire->port_output)
+ f << stringf("%s" "inout %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
+ else
+ f << stringf("%s" "%s ", indent.c_str(), reg_wires.count(wire->name) ? "reg" : "wire");
+ if (wire->width != 1)
+ f << stringf("[%d:%d] ", wire->width - 1 + wire->start_offset, wire->start_offset);
+ f << stringf("%s;\n", id(wire->name).c_str());
+#else
+ // do not use Verilog-2k "output reg" syntax in Verilog export
+ std::string range = "";
+ if (wire->width != 1) {
+ if (wire->upto)
+ range = stringf(" [%d:%d]", wire->start_offset, wire->width - 1 + wire->start_offset);
+ else
+ range = stringf(" [%d:%d]", wire->width - 1 + wire->start_offset, wire->start_offset);
+ }
+ if (wire->port_input && !wire->port_output)
+ f << stringf("%s" "input%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
+ if (!wire->port_input && wire->port_output)
+ f << stringf("%s" "output%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
+ if (wire->port_input && wire->port_output)
+ f << stringf("%s" "inout%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
+ if (reg_wires.count(wire->name)) {
+ f << stringf("%s" "reg%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
+ if (wire->attributes.count("\\init")) {
+ f << stringf("%s" "initial %s = ", indent.c_str(), id(wire->name).c_str());
+ dump_const(f, wire->attributes.at("\\init"));
+ f << stringf(";\n");
+ }
+ } else if (!wire->port_input && !wire->port_output)
+ f << stringf("%s" "wire%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
+#endif
+}
+
+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);
+}
+
+void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true)
+{
+ if (gen_signed && cell->parameters.count("\\" + port + "_SIGNED") > 0 && cell->parameters["\\" + port + "_SIGNED"].as_bool()) {
+ f << stringf("$signed(");
+ dump_sigspec(f, cell->getPort("\\" + port));
+ f << stringf(")");
+ } else
+ dump_sigspec(f, cell->getPort("\\" + port));
+}
+
+std::string cellname(RTLIL::Cell *cell)
+{
+ if (!norename && cell->name[0] == '$' && reg_ct.count(cell->type) && cell->hasPort("\\Q"))
+ {
+ RTLIL::SigSpec sig = cell->getPort("\\Q");
+ if (GetSize(sig) != 1 || sig.is_fully_const())
+ goto no_special_reg_name;
+
+ RTLIL::Wire *wire = sig[0].wire;
+
+ if (wire->name[0] != '\\')
+ goto no_special_reg_name;
+
+ std::string cell_name = wire->name.str();
+
+ size_t pos = cell_name.find('[');
+ if (pos != std::string::npos)
+ cell_name = cell_name.substr(0, pos) + "_reg" + cell_name.substr(pos);
+ else
+ cell_name = cell_name + "_reg";
+
+ if (wire->width != 1)
+ cell_name += stringf("[%d]", wire->start_offset + sig[0].offset);
+
+ if (active_module && active_module->count_id(cell_name) > 0)
+ goto no_special_reg_name;
+
+ return id(cell_name);
+ }
+ else
+ {
+no_special_reg_name:
+ return id(cell->name).c_str();
+ }
+}
+
+void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op)
+{
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = %s ", op.c_str());
+ dump_attributes(f, "", cell->attributes, ' ');
+ dump_cell_expr_port(f, cell, "A", true);
+ f << stringf(";\n");
+}
+
+void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op)
+{
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = ");
+ dump_cell_expr_port(f, cell, "A", true);
+ f << stringf(" %s ", op.c_str());
+ dump_attributes(f, "", cell->attributes, ' ');
+ dump_cell_expr_port(f, cell, "B", true);
+ f << stringf(";\n");
+}
+
+bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
+{
+ if (cell->type == "$_NOT_") {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = ");
+ f << stringf("~");
+ dump_attributes(f, "", cell->attributes, ' ');
+ dump_cell_expr_port(f, cell, "A", false);
+ f << stringf(";\n");
+ return true;
+ }
+
+ if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_")) {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = ");
+ if (cell->type.in("$_NAND_", "$_NOR_", "$_XNOR_"))
+ f << stringf("~(");
+ dump_cell_expr_port(f, cell, "A", false);
+ f << stringf(" ");
+ if (cell->type.in("$_AND_", "$_NAND_"))
+ f << stringf("&");
+ if (cell->type.in("$_OR_", "$_NOR_"))
+ f << stringf("|");
+ if (cell->type.in("$_XOR_", "$_XNOR_"))
+ f << stringf("^");
+ dump_attributes(f, "", cell->attributes, ' ');
+ f << stringf(" ");
+ dump_cell_expr_port(f, cell, "B", false);
+ if (cell->type.in("$_NAND_", "$_NOR_", "$_XNOR_"))
+ f << stringf(")");
+ f << stringf(";\n");
+ return true;
+ }
+
+ if (cell->type == "$_MUX_") {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = ");
+ dump_cell_expr_port(f, cell, "S", false);
+ f << stringf(" ? ");
+ dump_attributes(f, "", cell->attributes, ' ');
+ dump_cell_expr_port(f, cell, "B", false);
+ f << stringf(" : ");
+ dump_cell_expr_port(f, cell, "A", false);
+ f << stringf(";\n");
+ return true;
+ }
+
+ if (cell->type.in("$_AOI3_", "$_OAI3_")) {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = ~((");
+ dump_cell_expr_port(f, cell, "A", false);
+ f << stringf(cell->type == "$_AOI3_" ? " & " : " | ");
+ dump_cell_expr_port(f, cell, "B", false);
+ f << stringf(cell->type == "$_AOI3_" ? ") |" : ") &");
+ dump_attributes(f, "", cell->attributes, ' ');
+ f << stringf(" ");
+ dump_cell_expr_port(f, cell, "C", false);
+ f << stringf(");\n");
+ return true;
+ }
+
+ if (cell->type.in("$_AOI4_", "$_OAI4_")) {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = ~((");
+ dump_cell_expr_port(f, cell, "A", false);
+ f << stringf(cell->type == "$_AOI4_" ? " & " : " | ");
+ dump_cell_expr_port(f, cell, "B", false);
+ f << stringf(cell->type == "$_AOI4_" ? ") |" : ") &");
+ dump_attributes(f, "", cell->attributes, ' ');
+ f << stringf(" (");
+ dump_cell_expr_port(f, cell, "C", false);
+ f << stringf(cell->type == "$_AOI4_" ? " & " : " | ");
+ dump_cell_expr_port(f, cell, "D", false);
+ f << stringf("));\n");
+ return true;
+ }
+
+ if (cell->type.substr(0, 6) == "$_DFF_")
+ {
+ std::string reg_name = cellname(cell);
+ bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
+
+ if (!out_is_reg_wire)
+ f << stringf("%s" "reg %s;\n", indent.c_str(), reg_name.c_str());
+
+ dump_attributes(f, indent, cell->attributes);
+ f << stringf("%s" "always @(%sedge ", indent.c_str(), cell->type[6] == 'P' ? "pos" : "neg");
+ dump_sigspec(f, cell->getPort("\\C"));
+ if (cell->type[7] != '_') {
+ f << stringf(" or %sedge ", cell->type[7] == 'P' ? "pos" : "neg");
+ dump_sigspec(f, cell->getPort("\\R"));
+ }
+ f << stringf(")\n");
+
+ if (cell->type[7] != '_') {
+ f << stringf("%s" " if (%s", indent.c_str(), cell->type[7] == 'P' ? "" : "!");
+ dump_sigspec(f, cell->getPort("\\R"));
+ f << stringf(")\n");
+ f << stringf("%s" " %s <= %c;\n", indent.c_str(), reg_name.c_str(), cell->type[8]);
+ f << stringf("%s" " else\n", indent.c_str());
+ }
+
+ f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str());
+ dump_cell_expr_port(f, cell, "D", false);
+ f << stringf(";\n");
+
+ if (!out_is_reg_wire) {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Q"));
+ f << stringf(" = %s;\n", reg_name.c_str());
+ }
+
+ return true;
+ }
+
+ if (cell->type.substr(0, 8) == "$_DFFSR_")
+ {
+ char pol_c = cell->type[8], pol_s = cell->type[9], pol_r = cell->type[10];
+
+ std::string reg_name = cellname(cell);
+ bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
+
+ if (!out_is_reg_wire)
+ f << stringf("%s" "reg %s;\n", indent.c_str(), reg_name.c_str());
+
+ dump_attributes(f, indent, cell->attributes);
+ f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_c == 'P' ? "pos" : "neg");
+ dump_sigspec(f, cell->getPort("\\C"));
+ f << stringf(" or %sedge ", pol_s == 'P' ? "pos" : "neg");
+ dump_sigspec(f, cell->getPort("\\S"));
+ f << stringf(" or %sedge ", pol_r == 'P' ? "pos" : "neg");
+ dump_sigspec(f, cell->getPort("\\R"));
+ f << stringf(")\n");
+
+ f << stringf("%s" " if (%s", indent.c_str(), pol_r == 'P' ? "" : "!");
+ dump_sigspec(f, cell->getPort("\\R"));
+ f << stringf(")\n");
+ f << stringf("%s" " %s <= 0;\n", indent.c_str(), reg_name.c_str());
+
+ f << stringf("%s" " else if (%s", indent.c_str(), pol_s == 'P' ? "" : "!");
+ dump_sigspec(f, cell->getPort("\\S"));
+ f << stringf(")\n");
+ f << stringf("%s" " %s <= 1;\n", indent.c_str(), reg_name.c_str());
+
+ f << stringf("%s" " else\n", indent.c_str());
+ f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str());
+ dump_cell_expr_port(f, cell, "D", false);
+ f << stringf(";\n");
+
+ if (!out_is_reg_wire) {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Q"));
+ f << stringf(" = %s;\n", reg_name.c_str());
+ }
+
+ return true;
+ }
+
+#define HANDLE_UNIOP(_type, _operator) \
+ if (cell->type ==_type) { dump_cell_expr_uniop(f, indent, cell, _operator); return true; }
+#define HANDLE_BINOP(_type, _operator) \
+ if (cell->type ==_type) { dump_cell_expr_binop(f, indent, cell, _operator); return true; }
+
+ HANDLE_UNIOP("$not", "~")
+ HANDLE_UNIOP("$pos", "+")
+ HANDLE_UNIOP("$neg", "-")
+
+ HANDLE_BINOP("$and", "&")
+ HANDLE_BINOP("$or", "|")
+ HANDLE_BINOP("$xor", "^")
+ HANDLE_BINOP("$xnor", "~^")
+
+ HANDLE_UNIOP("$reduce_and", "&")
+ HANDLE_UNIOP("$reduce_or", "|")
+ HANDLE_UNIOP("$reduce_xor", "^")
+ HANDLE_UNIOP("$reduce_xnor", "~^")
+ HANDLE_UNIOP("$reduce_bool", "|")
+
+ HANDLE_BINOP("$shl", "<<")
+ HANDLE_BINOP("$shr", ">>")
+ HANDLE_BINOP("$sshl", "<<<")
+ HANDLE_BINOP("$sshr", ">>>")
+
+ HANDLE_BINOP("$lt", "<")
+ HANDLE_BINOP("$le", "<=")
+ HANDLE_BINOP("$eq", "==")
+ HANDLE_BINOP("$ne", "!=")
+ HANDLE_BINOP("$eqx", "===")
+ HANDLE_BINOP("$nex", "!==")
+ HANDLE_BINOP("$ge", ">=")
+ HANDLE_BINOP("$gt", ">")
+
+ HANDLE_BINOP("$add", "+")
+ HANDLE_BINOP("$sub", "-")
+ HANDLE_BINOP("$mul", "*")
+ HANDLE_BINOP("$div", "/")
+ HANDLE_BINOP("$mod", "%")
+ HANDLE_BINOP("$pow", "**")
+
+ HANDLE_UNIOP("$logic_not", "!")
+ HANDLE_BINOP("$logic_and", "&&")
+ HANDLE_BINOP("$logic_or", "||")
+
+#undef HANDLE_UNIOP
+#undef HANDLE_BINOP
+
+ if (cell->type == "$mux")
+ {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = ");
+ dump_sigspec(f, cell->getPort("\\S"));
+ f << stringf(" ? ");
+ dump_attributes(f, "", cell->attributes, ' ');
+ dump_sigspec(f, cell->getPort("\\B"));
+ f << stringf(" : ");
+ dump_sigspec(f, cell->getPort("\\A"));
+ f << stringf(";\n");
+ return true;
+ }
+
+ if (cell->type == "$pmux" || cell->type == "$pmux_safe")
+ {
+ int width = cell->parameters["\\WIDTH"].as_int();
+ int s_width = cell->getPort("\\S").size();
+ std::string func_name = cellname(cell);
+
+ f << stringf("%s" "function [%d:0] %s;\n", indent.c_str(), width-1, func_name.c_str());
+ f << stringf("%s" " input [%d:0] a;\n", indent.c_str(), width-1);
+ f << stringf("%s" " input [%d:0] b;\n", indent.c_str(), s_width*width-1);
+ f << stringf("%s" " input [%d:0] s;\n", indent.c_str(), s_width-1);
+
+ dump_attributes(f, indent + " ", cell->attributes);
+ if (cell->type != "$pmux_safe" && !noattr)
+ f << stringf("%s" " (* parallel_case *)\n", indent.c_str());
+ f << stringf("%s" " casez (s)", indent.c_str());
+ if (cell->type != "$pmux_safe")
+ f << stringf(noattr ? " // synopsys parallel_case\n" : "\n");
+
+ for (int i = 0; i < s_width; i++)
+ {
+ f << stringf("%s" " %d'b", indent.c_str(), s_width);
+
+ for (int j = s_width-1; j >= 0; j--)
+ f << stringf("%c", j == i ? '1' : cell->type == "$pmux_safe" ? '0' : '?');
+
+ f << stringf(":\n");
+ f << stringf("%s" " %s = b[%d:%d];\n", indent.c_str(), func_name.c_str(), (i+1)*width-1, i*width);
+ }
+
+ f << stringf("%s" " default:\n", indent.c_str());
+ f << stringf("%s" " %s = a;\n", indent.c_str(), func_name.c_str());
+
+ f << stringf("%s" " endcase\n", indent.c_str());
+ f << stringf("%s" "endfunction\n", indent.c_str());
+
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = %s(", func_name.c_str());
+ dump_sigspec(f, cell->getPort("\\A"));
+ f << stringf(", ");
+ dump_sigspec(f, cell->getPort("\\B"));
+ f << stringf(", ");
+ dump_sigspec(f, cell->getPort("\\S"));
+ f << stringf(");\n");
+ return true;
+ }
+
+ if (cell->type == "$slice")
+ {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = ");
+ dump_sigspec(f, cell->getPort("\\A"));
+ f << stringf(" >> %d;\n", cell->parameters.at("\\OFFSET").as_int());
+ return true;
+ }
+
+ if (cell->type == "$concat")
+ {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = { ");
+ dump_sigspec(f, cell->getPort("\\B"));
+ f << stringf(" , ");
+ dump_sigspec(f, cell->getPort("\\A"));
+ f << stringf(" };\n");
+ return true;
+ }
+
+ if (cell->type == "$dffsr")
+ {
+ SigSpec sig_clk = cell->getPort("\\CLK");
+ SigSpec sig_set = cell->getPort("\\SET");
+ SigSpec sig_clr = cell->getPort("\\CLR");
+ SigSpec sig_d = cell->getPort("\\D");
+ SigSpec sig_q = cell->getPort("\\Q");
+
+ int width = cell->parameters["\\WIDTH"].as_int();
+ bool pol_clk = cell->parameters["\\CLK_POLARITY"].as_bool();
+ bool pol_set = cell->parameters["\\SET_POLARITY"].as_bool();
+ bool pol_clr = cell->parameters["\\CLR_POLARITY"].as_bool();
+
+ std::string reg_name = cellname(cell);
+ bool out_is_reg_wire = is_reg_wire(sig_q, reg_name);
+
+ if (!out_is_reg_wire)
+ f << stringf("%s" "reg [%d:0] %s;\n", indent.c_str(), width-1, reg_name.c_str());
+
+ for (int i = 0; i < width; i++) {
+ f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg");
+ dump_sigspec(f, sig_clk);
+ f << stringf(", %sedge ", pol_set ? "pos" : "neg");
+ dump_sigspec(f, sig_set);
+ f << stringf(", %sedge ", pol_clr ? "pos" : "neg");
+ dump_sigspec(f, sig_clr);
+ f << stringf(")\n");
+
+ f << stringf("%s" " if (%s", indent.c_str(), pol_clr ? "" : "!");
+ dump_sigspec(f, sig_clr);
+ f << stringf(") %s[%d] <= 1'b0;\n", reg_name.c_str(), i);
+
+ f << stringf("%s" " else if (%s", indent.c_str(), pol_set ? "" : "!");
+ dump_sigspec(f, sig_set);
+ f << stringf(") %s[%d] <= 1'b1;\n", reg_name.c_str(), i);
+
+ f << stringf("%s" " else %s[%d] <= ", indent.c_str(), reg_name.c_str(), i);
+ dump_sigspec(f, sig_d[i]);
+ f << stringf(";\n");
+ }
+
+ if (!out_is_reg_wire) {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, sig_q);
+ f << stringf(" = %s;\n", reg_name.c_str());
+ }
+
+ return true;
+ }
+
+ if (cell->type == "$dff" || cell->type == "$adff" || cell->type == "$dffe")
+ {
+ RTLIL::SigSpec sig_clk, sig_arst, sig_en, val_arst;
+ bool pol_clk, pol_arst = false, pol_en = false;
+
+ sig_clk = cell->getPort("\\CLK");
+ pol_clk = cell->parameters["\\CLK_POLARITY"].as_bool();
+
+ if (cell->type == "$adff") {
+ sig_arst = cell->getPort("\\ARST");
+ pol_arst = cell->parameters["\\ARST_POLARITY"].as_bool();
+ val_arst = RTLIL::SigSpec(cell->parameters["\\ARST_VALUE"]);
+ }
+
+ if (cell->type == "$dffe") {
+ sig_en = cell->getPort("\\EN");
+ pol_en = cell->parameters["\\EN_POLARITY"].as_bool();
+ }
+
+ std::string reg_name = cellname(cell);
+ bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
+
+ if (!out_is_reg_wire)
+ f << stringf("%s" "reg [%d:0] %s;\n", indent.c_str(), cell->parameters["\\WIDTH"].as_int()-1, reg_name.c_str());
+
+ f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg");
+ dump_sigspec(f, sig_clk);
+ if (cell->type == "$adff") {
+ f << stringf(" or %sedge ", pol_arst ? "pos" : "neg");
+ dump_sigspec(f, sig_arst);
+ }
+ f << stringf(")\n");
+
+ if (cell->type == "$adff") {
+ f << stringf("%s" " if (%s", indent.c_str(), pol_arst ? "" : "!");
+ dump_sigspec(f, sig_arst);
+ f << stringf(")\n");
+ f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str());
+ dump_sigspec(f, val_arst);
+ f << stringf(";\n");
+ f << stringf("%s" " else\n", indent.c_str());
+ }
+
+ if (cell->type == "$dffe") {
+ f << stringf("%s" " if (%s", indent.c_str(), pol_en ? "" : "!");
+ dump_sigspec(f, sig_en);
+ f << stringf(")\n");
+ }
+
+ f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str());
+ dump_cell_expr_port(f, cell, "D", false);
+ f << stringf(";\n");
+
+ if (!out_is_reg_wire) {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Q"));
+ f << stringf(" = %s;\n", reg_name.c_str());
+ }
+
+ return true;
+ }
+
+ if (cell->type == "$mem")
+ {
+ RTLIL::IdString memid = cell->parameters["\\MEMID"].decode_string();
+ std::string mem_id = id(cell->parameters["\\MEMID"].decode_string());
+ int abits = cell->parameters["\\ABITS"].as_int();
+ int size = cell->parameters["\\SIZE"].as_int();
+ int width = cell->parameters["\\WIDTH"].as_int();
+ bool use_init = !(RTLIL::SigSpec(cell->parameters["\\INIT"]).is_fully_undef());
+
+ // for memory block make something like:
+ // reg [7:0] memid [3:0];
+ // initial begin
+ // memid[0] <= ...
+ // end
+ f << stringf("%s" "reg [%d:%d] %s [%d:%d];\n", indent.c_str(), width-1, 0, mem_id.c_str(), size-1, 0);
+ if (use_init)
+ {
+ f << stringf("%s" "initial begin\n", indent.c_str());
+ for (int i=0; i<size; i++)
+ {
+ f << stringf("%s" " %s[%d] <= ", indent.c_str(), mem_id.c_str(), i);
+ dump_const(f, cell->parameters["\\INIT"].extract(i*width, width));
+ f << stringf(";\n");
+ }
+ f << stringf("%s" "end\n", indent.c_str());
+ }
+
+ // create a map : "edge clk" -> expressions within that clock domain
+ dict<std::string, std::vector<std::string>> clk_to_lof_body;
+ clk_to_lof_body[""] = std::vector<std::string>();
+ std::string clk_domain_str;
+ // create a list of reg declarations
+ std::vector<std::string> lof_reg_declarations;
+
+ int nread_ports = cell->parameters["\\RD_PORTS"].as_int();
+ RTLIL::SigSpec sig_rd_clk, sig_rd_en, sig_rd_data, sig_rd_addr;
+ bool use_rd_clk, rd_clk_posedge, rd_transparent;
+ // read ports
+ for (int i=0; i < nread_ports; i++)
+ {
+ sig_rd_clk = cell->getPort("\\RD_CLK").extract(i);
+ sig_rd_en = cell->getPort("\\RD_EN").extract(i);
+ sig_rd_data = cell->getPort("\\RD_DATA").extract(i*width, width);
+ sig_rd_addr = cell->getPort("\\RD_ADDR").extract(i*abits, abits);
+ 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();
+ {
+ 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))
+ {
+ 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);
+ }
+ } else {
+ if (rd_transparent) {
+ // for rd-transparent read-ports make something like:
+ // reg [..] temp_id;
+ // always @(posedge clk)
+ // temp_id <= r_addr;
+ // assign r_data = array_reg[temp_id];
+ std::string temp_id = next_auto_id();
+ lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", sig_rd_addr.size() - 1, temp_id.c_str()) );
+ {
+ std::ostringstream os;
+ dump_sigspec(os, sig_rd_addr);
+ std::string line = stringf("%s <= %s;\n", temp_id.c_str(), os.str().c_str());
+ clk_to_lof_body[clk_domain_str].push_back(line);
+ }
+ {
+ std::ostringstream os;
+ dump_sigspec(os, sig_rd_data);
+ 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);
+ }
+ }
+ }
+
+ int nwrite_ports = cell->parameters["\\WR_PORTS"].as_int();
+ RTLIL::SigSpec sig_wr_clk, sig_wr_data, sig_wr_addr, sig_wr_en;
+ bool wr_clk_posedge;
+ SigMap sigmap(active_module);
+ // write ports
+ for (int i=0; i < nwrite_ports; i++)
+ {
+ sig_wr_clk = cell->getPort("\\WR_CLK").extract(i);
+ sig_wr_data = cell->getPort("\\WR_DATA").extract(i*width, width);
+ sig_wr_addr = cell->getPort("\\WR_ADDR").extract(i*abits, abits);
+ sig_wr_en = cell->getPort("\\WR_EN").extract(i*width, width);
+ wr_clk_posedge = cell->parameters["\\WR_CLK_POLARITY"].extract(i).as_bool();
+ {
+ std::ostringstream os;
+ dump_sigspec(os, sig_wr_clk);
+ clk_domain_str = stringf("%sedge %s", wr_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>();
+ }
+ // make something like:
+ // always @(posedge clk)
+ // if (wr_en_bit) memid[w_addr][??] <= w_data[??];
+ // ...
+ for (int i = 0; i < GetSize(sig_wr_en); i++)
+ {
+ int start_i = i, width = 1;
+ SigBit wen_bit = sig_wr_en[i];
+
+ while (i+1 < GetSize(sig_wr_en) && sigmap(sig_wr_en[i+1]) == sigmap(wen_bit))
+ i++, width++;
+
+ if (wen_bit == State::S0)
+ continue;
+
+ std::ostringstream os;
+ if (wen_bit != State::S1)
+ {
+ os << stringf("if (");
+ dump_sigspec(os, wen_bit);
+ os << stringf(") ");
+ }
+ os << stringf("%s[", mem_id.c_str());
+ dump_sigspec(os, sig_wr_addr);
+ if (width == GetSize(sig_wr_en))
+ os << stringf("] <= ");
+ else
+ os << stringf("][%d:%d] <= ", i, start_i);
+ dump_sigspec(os, sig_wr_data.extract(start_i, width));
+ os << stringf(";\n");
+ clk_to_lof_body[clk_domain_str].push_back(os.str());
+ }
+ }
+ // Output Verilog that looks something like this:
+ // reg [..] _3_;
+ // always @(posedge CLK2) begin
+ // _3_ <= memory[D1ADDR];
+ // if (A1EN)
+ // memory[A1ADDR] <= A1DATA;
+ // if (A2EN)
+ // memory[A2ADDR] <= A2DATA;
+ // ...
+ // end
+ // always @(negedge CLK1) begin
+ // if (C1EN)
+ // memory[C1ADDR] <= C1DATA;
+ // end
+ // ...
+ // assign D1DATA = _3_;
+ // assign D2DATA <= memory[D2ADDR];
+
+ // the reg ... definitions
+ for(auto &reg : lof_reg_declarations)
+ {
+ f << stringf("%s" "%s", indent.c_str(), reg.c_str());
+ }
+ // the block of expressions by clock domain
+ for(auto &pair : clk_to_lof_body)
+ {
+ std::string clk_domain = pair.first;
+ std::vector<std::string> lof_lines = pair.second;
+ if( clk_domain != "")
+ {
+ f << stringf("%s" "always @(%s) begin\n", indent.c_str(), clk_domain.c_str());
+ for(auto &line : lof_lines)
+ f << stringf("%s%s" "%s", indent.c_str(), indent.c_str(), line.c_str());
+ f << stringf("%s" "end\n", indent.c_str());
+ }
+ else
+ {
+ // the non-clocked assignments
+ for(auto &line : lof_lines)
+ f << stringf("%s" "%s", indent.c_str(), line.c_str());
+ }
+ }
+
+ return true;
+ }
+
+ // FIXME: $_SR_[PN][PN]_, $_DLATCH_[PN]_, $_DLATCHSR_[PN][PN][PN]_
+ // FIXME: $sr, $dlatch, $memrd, $memwr, $fsm
+
+ return false;
+}
+
+void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
+{
+ if (cell->type[0] == '$' && !noexpr) {
+ if (dump_cell_expr(f, indent, cell))
+ return;
+ }
+
+ dump_attributes(f, indent, cell->attributes);
+ f << stringf("%s" "%s", indent.c_str(), id(cell->type, false).c_str());
+
+ if (!defparam && cell->parameters.size() > 0) {
+ f << stringf(" #(");
+ for (auto it = cell->parameters.begin(); it != cell->parameters.end(); ++it) {
+ if (it != cell->parameters.begin())
+ f << stringf(",");
+ f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str());
+ bool is_signed = (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0;
+ dump_const(f, it->second, -1, 0, false, is_signed);
+ f << stringf(")");
+ }
+ f << stringf("\n%s" ")", indent.c_str());
+ }
+
+ std::string cell_name = cellname(cell);
+ if (cell_name != id(cell->name))
+ f << stringf(" %s /* %s */ (", cell_name.c_str(), id(cell->name).c_str());
+ else
+ f << stringf(" %s (", cell_name.c_str());
+
+ bool first_arg = true;
+ std::set<RTLIL::IdString> numbered_ports;
+ for (int i = 1; true; i++) {
+ char str[16];
+ snprintf(str, 16, "$%d", i);
+ for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) {
+ if (it->first != str)
+ continue;
+ if (!first_arg)
+ f << stringf(",");
+ first_arg = false;
+ f << stringf("\n%s ", indent.c_str());
+ dump_sigspec(f, it->second);
+ numbered_ports.insert(it->first);
+ goto found_numbered_port;
+ }
+ break;
+ found_numbered_port:;
+ }
+ for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) {
+ if (numbered_ports.count(it->first))
+ continue;
+ if (!first_arg)
+ f << stringf(",");
+ first_arg = false;
+ f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str());
+ if (it->second.size() > 0)
+ dump_sigspec(f, it->second);
+ f << stringf(")");
+ }
+ f << stringf("\n%s" ");\n", indent.c_str());
+
+ if (defparam && cell->parameters.size() > 0) {
+ for (auto it = cell->parameters.begin(); it != cell->parameters.end(); ++it) {
+ f << stringf("%sdefparam %s.%s = ", indent.c_str(), cell_name.c_str(), id(it->first).c_str());
+ bool is_signed = (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0;
+ dump_const(f, it->second, -1, 0, false, is_signed);
+ f << stringf(";\n");
+ }
+ }
+
+}
+
+void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
+{
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, left);
+ f << stringf(" = ");
+ dump_sigspec(f, right);
+ f << stringf(";\n");
+}
+
+void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw);
+
+void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false)
+{
+ int number_of_stmts = cs->switches.size() + cs->actions.size();
+
+ if (!omit_trailing_begin && number_of_stmts >= 2)
+ f << stringf("%s" "begin\n", indent.c_str());
+
+ for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) {
+ if (it->first.size() == 0)
+ continue;
+ f << stringf("%s ", indent.c_str());
+ dump_sigspec(f, it->first);
+ f << stringf(" = ");
+ dump_sigspec(f, it->second);
+ f << stringf(";\n");
+ }
+
+ for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
+ dump_proc_switch(f, indent + " ", *it);
+
+ if (!omit_trailing_begin && number_of_stmts == 0)
+ f << stringf("%s /* empty */;\n", indent.c_str());
+
+ if (omit_trailing_begin || number_of_stmts >= 2)
+ f << stringf("%s" "end\n", indent.c_str());
+}
+
+void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw)
+{
+ if (sw->signal.size() == 0) {
+ f << stringf("%s" "begin\n", indent.c_str());
+ for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) {
+ if ((*it)->compare.size() == 0)
+ dump_case_body(f, indent + " ", *it);
+ }
+ f << stringf("%s" "end\n", indent.c_str());
+ return;
+ }
+
+ f << stringf("%s" "casez (", indent.c_str());
+ dump_sigspec(f, sw->signal);
+ f << stringf(")\n");
+
+ bool got_default = false;
+ for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) {
+ if ((*it)->compare.size() == 0) {
+ if (got_default)
+ continue;
+ f << stringf("%s default", indent.c_str());
+ got_default = true;
+ } else {
+ f << stringf("%s ", indent.c_str());
+ for (size_t i = 0; i < (*it)->compare.size(); i++) {
+ if (i > 0)
+ f << stringf(", ");
+ dump_sigspec(f, (*it)->compare[i]);
+ }
+ }
+ f << stringf(":\n");
+ dump_case_body(f, indent + " ", *it);
+ }
+
+ f << stringf("%s" "endcase\n", indent.c_str());
+}
+
+void case_body_find_regs(RTLIL::CaseRule *cs)
+{
+ for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
+ for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++)
+ case_body_find_regs(*it2);
+
+ for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) {
+ for (auto &c : it->first.chunks())
+ if (c.wire != NULL)
+ reg_wires.insert(c.wire->name);
+ }
+}
+
+void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, bool find_regs = false)
+{
+ if (find_regs) {
+ case_body_find_regs(&proc->root_case);
+ for (auto it = proc->syncs.begin(); it != proc->syncs.end(); ++it)
+ for (auto it2 = (*it)->actions.begin(); it2 != (*it)->actions.end(); it2++) {
+ for (auto &c : it2->first.chunks())
+ if (c.wire != NULL)
+ reg_wires.insert(c.wire->name);
+ }
+ return;
+ }
+
+ f << stringf("%s" "always @* begin\n", indent.c_str());
+ dump_case_body(f, indent, &proc->root_case, true);
+
+ std::string backup_indent = indent;
+
+ for (size_t i = 0; i < proc->syncs.size(); i++)
+ {
+ RTLIL::SyncRule *sync = proc->syncs[i];
+ indent = backup_indent;
+
+ if (sync->type == RTLIL::STa) {
+ f << stringf("%s" "always @* begin\n", indent.c_str());
+ } else {
+ f << stringf("%s" "always @(", indent.c_str());
+ if (sync->type == RTLIL::STp || sync->type == RTLIL::ST1)
+ f << stringf("posedge ");
+ if (sync->type == RTLIL::STn || sync->type == RTLIL::ST0)
+ f << stringf("negedge ");
+ dump_sigspec(f, sync->signal);
+ f << stringf(") begin\n");
+ }
+ std::string ends = indent + "end\n";
+ indent += " ";
+
+ if (sync->type == RTLIL::ST0 || sync->type == RTLIL::ST1) {
+ f << stringf("%s" "if (%s", indent.c_str(), sync->type == RTLIL::ST0 ? "!" : "");
+ dump_sigspec(f, sync->signal);
+ f << stringf(") begin\n");
+ ends = indent + "end\n" + ends;
+ indent += " ";
+ }
+
+ if (sync->type == RTLIL::STp || sync->type == RTLIL::STn) {
+ for (size_t j = 0; j < proc->syncs.size(); j++) {
+ RTLIL::SyncRule *sync2 = proc->syncs[j];
+ if (sync2->type == RTLIL::ST0 || sync2->type == RTLIL::ST1) {
+ f << stringf("%s" "if (%s", indent.c_str(), sync2->type == RTLIL::ST1 ? "!" : "");
+ dump_sigspec(f, sync2->signal);
+ f << stringf(") begin\n");
+ ends = indent + "end\n" + ends;
+ indent += " ";
+ }
+ }
+ }
+
+ for (auto it = sync->actions.begin(); it != sync->actions.end(); ++it) {
+ if (it->first.size() == 0)
+ continue;
+ f << stringf("%s ", indent.c_str());
+ dump_sigspec(f, it->first);
+ f << stringf(" <= ");
+ dump_sigspec(f, it->second);
+ f << stringf(";\n");
+ }
+
+ f << stringf("%s", ends.c_str());
+ }
+}
+
+void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
+{
+ reg_wires.clear();
+ reset_auto_counter(module);
+ active_module = module;
+
+ if (!module->processes.empty())
+ log_warning("Module %s contains unmapped RTLIL proccesses. 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));
+
+ f << stringf("\n");
+ for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
+ dump_process(f, indent + " ", it->second, true);
+
+ if (!noexpr)
+ {
+ std::set<std::pair<RTLIL::Wire*,int>> reg_bits;
+ for (auto &it : module->cells_)
+ {
+ RTLIL::Cell *cell = it.second;
+ if (!reg_ct.count(cell->type) || !cell->hasPort("\\Q"))
+ continue;
+
+ RTLIL::SigSpec sig = cell->getPort("\\Q");
+
+ if (sig.is_chunk()) {
+ RTLIL::SigChunk chunk = sig.as_chunk();
+ if (chunk.wire != NULL)
+ for (int i = 0; i < chunk.width; i++)
+ reg_bits.insert(std::pair<RTLIL::Wire*,int>(chunk.wire, chunk.offset+i));
+ }
+ }
+ for (auto &it : module->wires_)
+ {
+ RTLIL::Wire *wire = it.second;
+ for (int i = 0; i < wire->width; i++)
+ if (reg_bits.count(std::pair<RTLIL::Wire*,int>(wire, i)) == 0)
+ goto this_wire_aint_reg;
+ if (wire->width)
+ reg_wires.insert(wire->name);
+ this_wire_aint_reg:;
+ }
+ }
+
+ dump_attributes(f, indent, module->attributes, '\n', true);
+ f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str());
+ bool keep_running = true;
+ for (int port_id = 1; keep_running; port_id++) {
+ keep_running = false;
+ for (auto it = module->wires_.begin(); it != module->wires_.end(); ++it) {
+ RTLIL::Wire *wire = it->second;
+ if (wire->port_id == port_id) {
+ if (port_id != 1)
+ f << stringf(", ");
+ f << stringf("%s", id(wire->name).c_str());
+ keep_running = true;
+ continue;
+ }
+ }
+ }
+ f << stringf(");\n");
+
+ for (auto it = module->wires_.begin(); it != module->wires_.end(); ++it)
+ dump_wire(f, indent + " ", it->second);
+
+ for (auto it = module->memories.begin(); it != module->memories.end(); ++it)
+ dump_memory(f, indent + " ", it->second);
+
+ for (auto it = module->cells_.begin(); it != module->cells_.end(); ++it)
+ dump_cell(f, indent + " ", it->second);
+
+ for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
+ dump_process(f, indent + " ", it->second);
+
+ for (auto it = module->connections().begin(); it != module->connections().end(); ++it)
+ dump_conn(f, indent + " ", it->first, it->second);
+
+ f << stringf("%s" "endmodule\n", indent.c_str());
+ active_module = NULL;
+}
+
+struct VerilogBackend : public Backend {
+ VerilogBackend() : Backend("verilog", "write design to Verilog file") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" write_verilog [options] [filename]\n");
+ log("\n");
+ log("Write the current design to a Verilog file.\n");
+ log("\n");
+ log(" -norename\n");
+ log(" without this option all internal object names (the ones with a dollar\n");
+ log(" instead of a backslash prefix) are changed to short names in the\n");
+ log(" format '_<number>_'.\n");
+ log("\n");
+ log(" -renameprefix <prefix>\n");
+ log(" insert this prefix in front of auto-generated instance names\n");
+ log("\n");
+ log(" -noattr\n");
+ log(" with this option no attributes are included in the output\n");
+ log("\n");
+ log(" -attr2comment\n");
+ log(" with this option attributes are included as comments in the output\n");
+ log("\n");
+ log(" -noexpr\n");
+ log(" without this option all internal cells are converted to Verilog\n");
+ log(" expressions.\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 decativates this feature and instead\n");
+ log(" will write out all constants in binary.\n");
+ log("\n");
+ log(" -nostr\n");
+ log(" Parameters and attributes that are specified as strings in the\n");
+ log(" original input will be output as strings by this back-end. This\n");
+ log(" decativates this feature and instead will write string constants\n");
+ log(" as binary numbers.\n");
+ log("\n");
+ log(" -defparam\n");
+ log(" Use 'defparam' statements instead of the Verilog-2001 syntax for\n");
+ log(" cell parameters.\n");
+ log("\n");
+ log(" -blackboxes\n");
+ log(" usually modules with the 'blackbox' attribute are ignored. with\n");
+ log(" this option set only the modules with the 'blackbox' attribute\n");
+ log(" are written to the output file.\n");
+ log("\n");
+ log(" -selected\n");
+ log(" only write selected modules. modules must be selected entirely or\n");
+ log(" not at all.\n");
+ log("\n");
+ log(" -v\n");
+ log(" verbose output (print new names of all renamed wires and cells)\n");
+ log("\n");
+ log("Note that RTLIL processes can't always be mapped directly to Verilog\n");
+ log("always blocks. This frontend should only be used to export an RTLIL\n");
+ log("netlist, i.e. after the \"proc\" pass has been used to convert all\n");
+ log("processes to logic networks and registers. A warning is generated when\n");
+ log("this command is called on a design with RTLIL processes.\n");
+ log("\n");
+ }
+ virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing Verilog backend.\n");
+
+ verbose = false;
+ norename = false;
+ noattr = false;
+ attr2comment = false;
+ noexpr = false;
+ nodec = false;
+ nostr = false;
+ defparam = false;
+ auto_prefix = "";
+
+ bool blackboxes = false;
+ bool selected = false;
+
+ reg_ct.clear();
+
+ reg_ct.insert("$dff");
+ reg_ct.insert("$adff");
+
+ reg_ct.insert("$_DFF_N_");
+ reg_ct.insert("$_DFF_P_");
+
+ reg_ct.insert("$_DFF_NN0_");
+ reg_ct.insert("$_DFF_NN1_");
+ reg_ct.insert("$_DFF_NP0_");
+ reg_ct.insert("$_DFF_NP1_");
+ reg_ct.insert("$_DFF_PN0_");
+ reg_ct.insert("$_DFF_PN1_");
+ reg_ct.insert("$_DFF_PP0_");
+ reg_ct.insert("$_DFF_PP1_");
+
+ reg_ct.insert("$_DFFSR_NNN_");
+ reg_ct.insert("$_DFFSR_NNP_");
+ reg_ct.insert("$_DFFSR_NPN_");
+ reg_ct.insert("$_DFFSR_NPP_");
+ reg_ct.insert("$_DFFSR_PNN_");
+ reg_ct.insert("$_DFFSR_PNP_");
+ reg_ct.insert("$_DFFSR_PPN_");
+ reg_ct.insert("$_DFFSR_PPP_");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-norename") {
+ norename = true;
+ continue;
+ }
+ if (arg == "-renameprefix" && argidx+1 < args.size()) {
+ auto_prefix = args[++argidx];
+ continue;
+ }
+ if (arg == "-noattr") {
+ noattr = true;
+ continue;
+ }
+ if (arg == "-attr2comment") {
+ attr2comment = true;
+ continue;
+ }
+ if (arg == "-noexpr") {
+ noexpr = true;
+ continue;
+ }
+ if (arg == "-nodec") {
+ nodec = true;
+ continue;
+ }
+ if (arg == "-nostr") {
+ nostr = true;
+ continue;
+ }
+ if (arg == "-defparam") {
+ defparam = true;
+ continue;
+ }
+ if (arg == "-blackboxes") {
+ blackboxes = true;
+ continue;
+ }
+ if (arg == "-selected") {
+ selected = true;
+ continue;
+ }
+ if (arg == "-v") {
+ verbose = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ design->sort();
+
+ *f << stringf("/* Generated by %s */\n", yosys_version_str);
+ for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it) {
+ if (it->second->get_bool_attribute("\\blackbox") != blackboxes)
+ continue;
+ if (selected && !design->selected_whole_module(it->first)) {
+ if (design->selected_module(it->first))
+ log_cmd_error("Can't handle partially selected module %s!\n", RTLIL::id2cstr(it->first));
+ continue;
+ }
+ log("Dumping module `%s'.\n", it->first.c_str());
+ dump_module(*f, "", it->second);
+ }
+
+ reg_ct.clear();
+ }
+} VerilogBackend;
+
+PRIVATE_NAMESPACE_END
diff --git a/examples/basys3/README b/examples/basys3/README
new file mode 100644
index 00000000..0ce71729
--- /dev/null
+++ b/examples/basys3/README
@@ -0,0 +1,19 @@
+
+A simple example design, based on the Digilent BASYS3 board
+===========================================================
+
+This example uses Yosys for synthesis and Xilinx Vivado
+for place&route and bit-stream creation.
+
+Running Yosys:
+ yosys run_yosys.ys
+
+Running Vivado:
+ vivado -nolog -nojournal -mode batch -source run_vivado.tcl
+
+Programming board:
+ vivado -nolog -nojournal -mode batch -source run_prog.tcl
+
+All of the above:
+ bash run.sh
+
diff --git a/examples/basys3/example.v b/examples/basys3/example.v
new file mode 100644
index 00000000..2b01a22a
--- /dev/null
+++ b/examples/basys3/example.v
@@ -0,0 +1,21 @@
+module example(CLK, LD);
+ input CLK;
+ output [15:0] LD;
+
+ wire clock;
+ reg [15:0] leds;
+
+ BUFG CLK_BUF (.I(CLK), .O(clock));
+ OBUF LD_BUF[15:0] (.I(leds), .O(LD));
+
+ parameter COUNTBITS = 26;
+ reg [COUNTBITS-1:0] counter;
+
+ always @(posedge CLK) begin
+ counter <= counter + 1;
+ if (counter[COUNTBITS-1])
+ leds <= 16'h8000 >> counter[COUNTBITS-2:COUNTBITS-5];
+ else
+ leds <= 16'h0001 << counter[COUNTBITS-2:COUNTBITS-5];
+ end
+endmodule
diff --git a/examples/basys3/example.xdc b/examples/basys3/example.xdc
new file mode 100644
index 00000000..c1fd0e92
--- /dev/null
+++ b/examples/basys3/example.xdc
@@ -0,0 +1,21 @@
+
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN W5 } [get_ports CLK]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN U16 } [get_ports {LD[0]}]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN E19 } [get_ports {LD[1]}]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN U19 } [get_ports {LD[2]}]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN V19 } [get_ports {LD[3]}]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN W18 } [get_ports {LD[4]}]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN U15 } [get_ports {LD[5]}]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN U14 } [get_ports {LD[6]}]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN V14 } [get_ports {LD[7]}]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN V13 } [get_ports {LD[8]}]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN V3 } [get_ports {LD[9]}]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN W3 } [get_ports {LD[10]}]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN U3 } [get_ports {LD[11]}]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN P3 } [get_ports {LD[12]}]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN N3 } [get_ports {LD[13]}]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN P1 } [get_ports {LD[14]}]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN L1 } [get_ports {LD[15]}]
+
+create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports CLK]
+
diff --git a/examples/basys3/run.sh b/examples/basys3/run.sh
new file mode 100644
index 00000000..10f05910
--- /dev/null
+++ b/examples/basys3/run.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+yosys run_yosys.ys
+vivado -nolog -nojournal -mode batch -source run_vivado.tcl
+vivado -nolog -nojournal -mode batch -source run_prog.tcl
diff --git a/examples/basys3/run_prog.tcl b/examples/basys3/run_prog.tcl
new file mode 100644
index 00000000..d711af84
--- /dev/null
+++ b/examples/basys3/run_prog.tcl
@@ -0,0 +1,4 @@
+connect_hw_server
+open_hw_target [lindex [get_hw_targets] 0]
+set_property PROGRAM.FILE example.bit [lindex [get_hw_devices] 0]
+program_hw_devices [lindex [get_hw_devices] 0]
diff --git a/examples/basys3/run_vivado.tcl b/examples/basys3/run_vivado.tcl
new file mode 100644
index 00000000..c3b6a610
--- /dev/null
+++ b/examples/basys3/run_vivado.tcl
@@ -0,0 +1,9 @@
+read_xdc example.xdc
+read_edif example.edif
+link_design -part xc7a35tcpg236-1 -top example
+opt_design
+place_design
+route_design
+report_utilization
+report_timing
+write_bitstream -force example.bit
diff --git a/examples/basys3/run_yosys.ys b/examples/basys3/run_yosys.ys
new file mode 100644
index 00000000..4541826d
--- /dev/null
+++ b/examples/basys3/run_yosys.ys
@@ -0,0 +1,2 @@
+read_verilog example.v
+synth_xilinx -edif example.edif -top example
diff --git a/examples/cmos/.gitignore b/examples/cmos/.gitignore
new file mode 100644
index 00000000..f58d9501
--- /dev/null
+++ b/examples/cmos/.gitignore
@@ -0,0 +1,4 @@
+counter_tb
+counter_tb.vcd
+synth.sp
+synth.v
diff --git a/examples/cmos/README b/examples/cmos/README
new file mode 100644
index 00000000..c459b4b5
--- /dev/null
+++ b/examples/cmos/README
@@ -0,0 +1,13 @@
+
+In this directory contains an example for generating a spice output using two
+different spice modes, normal analog transient simulation and event-driven
+digital simulation as supported by ngspice xspice sub-module.
+
+Each test bench can be run separately by either running:
+
+- testbench.sh, to start analog simulation or
+- testbench_digital.sh for mixed-signal digital simulation.
+
+The later case also includes pure verilog simulation using the iverilog
+and gtkwave for comparison.
+
diff --git a/examples/cmos/cmos_cells.lib b/examples/cmos/cmos_cells.lib
new file mode 100644
index 00000000..1b0bf845
--- /dev/null
+++ b/examples/cmos/cmos_cells.lib
@@ -0,0 +1,55 @@
+// test comment
+/* test comment */
+library(demo) {
+ cell(BUF) {
+ area: 6;
+ pin(A) { direction: input; }
+ pin(Y) { direction: output;
+ function: "A"; }
+ }
+ cell(NOT) {
+ area: 3;
+ pin(A) { direction: input; }
+ pin(Y) { direction: output;
+ function: "A'"; }
+ }
+ cell(NAND) {
+ area: 4;
+ pin(A) { direction: input; }
+ pin(B) { direction: input; }
+ pin(Y) { direction: output;
+ function: "(A*B)'"; }
+ }
+ cell(NOR) {
+ area: 4;
+ pin(A) { direction: input; }
+ pin(B) { direction: input; }
+ pin(Y) { direction: output;
+ function: "(A+B)'"; }
+ }
+ cell(DFF) {
+ area: 18;
+ ff(IQ, IQN) { clocked_on: C;
+ next_state: D; }
+ pin(C) { direction: input;
+ clock: true; }
+ pin(D) { direction: input; }
+ pin(Q) { direction: output;
+ function: "IQ"; }
+ }
+ cell(DFFSR) {
+ area: 18;
+ ff("IQ", "IQN") { clocked_on: C;
+ next_state: D;
+ preset: S;
+ clear: R; }
+ pin(C) { direction: input;
+ clock: true; }
+ pin(D) { direction: input; }
+ pin(Q) { direction: output;
+ function: "IQ"; }
+ pin(S) { direction: input; }
+ pin(R) { direction: input; }
+ ; // empty statement
+ }
+}
diff --git a/examples/cmos/cmos_cells.sp b/examples/cmos/cmos_cells.sp
new file mode 100644
index 00000000..673b20d0
--- /dev/null
+++ b/examples/cmos/cmos_cells.sp
@@ -0,0 +1,39 @@
+
+.SUBCKT BUF A Y
+X1 A B NOT
+X2 B Y NOT
+.ENDS NOT
+
+.SUBCKT NOT A Y
+M1 Y A Vdd Vdd cmosp L=1u W=10u
+M2 Y A Vss Vss cmosn L=1u W=10u
+.ENDS NOT
+
+.SUBCKT NAND A B Y
+M1 Y A Vdd Vdd cmosp L=1u W=10u
+M2 Y B Vdd Vdd cmosp L=1u W=10u
+M3 Y A M34 Vss cmosn L=1u W=10u
+M4 M34 B Vss Vss cmosn L=1u W=10u
+.ENDS NAND
+
+.SUBCKT NOR A B Y
+M1 Y A M12 Vdd cmosp L=1u W=10u
+M2 M12 B Vdd Vdd cmosp L=1u W=10u
+M3 Y A Vss Vss cmosn L=1u W=10u
+M4 Y B Vss Vss cmosn L=1u W=10u
+.ENDS NOR
+
+.SUBCKT DLATCH E D Q
+X1 D E S NAND
+X2 nD E R NAND
+X3 S nQ Q NAND
+X4 Q R nQ NAND
+X5 D nD NOT
+.ENDS DLATCH
+
+.SUBCKT DFF C D Q
+X1 nC D t DLATCH
+X2 C t Q DLATCH
+X3 C nC NOT
+.ENDS DFF
+
diff --git a/examples/cmos/cmos_cells.v b/examples/cmos/cmos_cells.v
new file mode 100644
index 00000000..27278fac
--- /dev/null
+++ b/examples/cmos/cmos_cells.v
@@ -0,0 +1,44 @@
+
+module BUF(A, Y);
+input A;
+output Y;
+assign Y = A;
+endmodule
+
+module NOT(A, Y);
+input A;
+output Y;
+assign Y = ~A;
+endmodule
+
+module NAND(A, B, Y);
+input A, B;
+output Y;
+assign Y = ~(A & B);
+endmodule
+
+module NOR(A, B, Y);
+input A, B;
+output Y;
+assign Y = ~(A | B);
+endmodule
+
+module DFF(C, D, Q);
+input C, D;
+output reg Q;
+always @(posedge C)
+ Q <= D;
+endmodule
+
+module DFFSR(C, D, Q, S, R);
+input C, D, S, R;
+output reg Q;
+always @(posedge C, posedge S, posedge R)
+ if (S)
+ Q <= 1'b1;
+ else if (R)
+ Q <= 1'b0;
+ else
+ Q <= D;
+endmodule
+
diff --git a/examples/cmos/cmos_cells_digital.sp b/examples/cmos/cmos_cells_digital.sp
new file mode 100644
index 00000000..e1cb82a2
--- /dev/null
+++ b/examples/cmos/cmos_cells_digital.sp
@@ -0,0 +1,31 @@
+
+.SUBCKT BUF A Y
+.model buffer1 d_buffer
+Abuf A Y buffer1
+.ENDS NOT
+
+.SUBCKT NOT A Y
+.model not1 d_inverter
+Anot A Y not1
+.ENDS NOT
+
+.SUBCKT NAND A B Y
+.model nand1 d_nand
+Anand [A B] Y nand1
+.ENDS NAND
+
+.SUBCKT NOR A B Y
+.model nor1 d_nor
+Anand [A B] Y nor1
+.ENDS NOR
+
+.SUBCKT DLATCH E D Q
+.model latch1 d_latch
+Alatch D E null null Q nQ latch1
+.ENDS DLATCH
+
+.SUBCKT DFF C D Q
+.model dff1 d_dff
+Adff D C null null Q nQ dff1
+.ENDS DFF
+
diff --git a/examples/cmos/counter.v b/examples/cmos/counter.v
new file mode 100644
index 00000000..f2165872
--- /dev/null
+++ b/examples/cmos/counter.v
@@ -0,0 +1,12 @@
+module counter (clk, rst, en, count);
+
+ input clk, rst, en;
+ output reg [2:0] count;
+
+ always @(posedge clk)
+ if (rst)
+ count <= 3'd0;
+ else if (en)
+ count <= count + 3'd1;
+
+endmodule
diff --git a/examples/cmos/counter.ys b/examples/cmos/counter.ys
new file mode 100644
index 00000000..a784f346
--- /dev/null
+++ b/examples/cmos/counter.ys
@@ -0,0 +1,16 @@
+
+read_verilog counter.v
+read_verilog -lib cmos_cells.v
+
+proc;; memory;; techmap;;
+
+dfflibmap -liberty cmos_cells.lib
+abc -liberty cmos_cells.lib;;
+
+# http://vlsiarch.ecen.okstate.edu/flows/MOSIS_SCMOS/latest/cadence/lib/tsmc025/signalstorm/osu025_stdcells.lib
+# dfflibmap -liberty osu025_stdcells.lib
+# abc -liberty osu025_stdcells.lib;;
+
+write_verilog synth.v
+write_spice synth.sp
+
diff --git a/examples/cmos/counter_digital.ys b/examples/cmos/counter_digital.ys
new file mode 100644
index 00000000..a5e728e0
--- /dev/null
+++ b/examples/cmos/counter_digital.ys
@@ -0,0 +1,16 @@
+
+read_verilog counter.v
+read_verilog -lib cmos_cells.v
+
+proc;; memory;; techmap;;
+
+dfflibmap -liberty cmos_cells.lib
+abc -liberty cmos_cells.lib;;
+
+# http://vlsiarch.ecen.okstate.edu/flows/MOSIS_SCMOS/latest/cadence/lib/tsmc025/signalstorm/osu025_stdcells.lib
+# dfflibmap -liberty osu025_stdcells.lib
+# abc -liberty osu025_stdcells.lib;;
+
+write_verilog synth.v
+write_spice -neg 0s -pos 1s synth.sp
+
diff --git a/examples/cmos/counter_tb.gtkw b/examples/cmos/counter_tb.gtkw
new file mode 100644
index 00000000..4a2eac40
--- /dev/null
+++ b/examples/cmos/counter_tb.gtkw
@@ -0,0 +1,5 @@
+[dumpfile] "counter_tb.vcd"
+counter_tb.clk
+counter_tb.count[2:0]
+counter_tb.en
+counter_tb.reset
diff --git a/examples/cmos/counter_tb.v b/examples/cmos/counter_tb.v
new file mode 100644
index 00000000..bcd7d992
--- /dev/null
+++ b/examples/cmos/counter_tb.v
@@ -0,0 +1,33 @@
+module counter_tb;
+
+ /* Make a reset pulse and specify dump file */
+ reg reset = 0;
+ initial begin
+ $dumpfile("counter_tb.vcd");
+ $dumpvars(0,counter_tb);
+
+ # 0 reset = 1;
+ # 4 reset = 0;
+ # 36 reset = 1;
+ # 4 reset = 0;
+ # 6 $finish;
+ end
+
+ /* Make enable with period of 8 and 6,7 low */
+ reg en = 1;
+ always begin
+ en = 1;
+ #6;
+ en = 0;
+ #2;
+ end
+
+ /* Make a regular pulsing clock. */
+ reg clk = 0;
+ always #1 clk = !clk;
+
+ /* UUT */
+ wire [2:0] count;
+ counter c1 (clk, reset, en, count);
+
+endmodule
diff --git a/examples/cmos/testbench.sh b/examples/cmos/testbench.sh
new file mode 100644
index 00000000..061704b6
--- /dev/null
+++ b/examples/cmos/testbench.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -ex
+
+../../yosys counter.ys
+ngspice testbench.sp
+
diff --git a/examples/cmos/testbench.sp b/examples/cmos/testbench.sp
new file mode 100644
index 00000000..e571d281
--- /dev/null
+++ b/examples/cmos/testbench.sp
@@ -0,0 +1,29 @@
+
+* supply voltages
+.global Vss Vdd
+Vss Vss 0 DC 0
+Vdd Vdd 0 DC 3
+
+* simple transistor model
+.MODEL cmosn NMOS LEVEL=1 VT0=0.7 KP=110U GAMMA=0.4 LAMBDA=0.04 PHI=0.7
+.MODEL cmosp PMOS LEVEL=1 VT0=-0.7 KP=50U GAMMA=0.57 LAMBDA=0.05 PHI=0.8
+
+* load design and library
+.include cmos_cells.sp
+.include synth.sp
+
+* input signals
+Vclk clk 0 PULSE(0 3 1 0.1 0.1 0.8 2)
+Vrst rst 0 PULSE(0 3 0.5 0.1 0.1 2.9 40)
+Ven en 0 PULSE(0 3 0.5 0.1 0.1 5.9 8)
+
+Xuut clk rst en out0 out1 out2 COUNTER
+
+.tran 0.01 50
+
+.control
+run
+plot v(clk) v(rst)+5 v(en)+10 v(out0)+20 v(out1)+25 v(out2)+30
+.endc
+
+.end
diff --git a/examples/cmos/testbench_digital.sh b/examples/cmos/testbench_digital.sh
new file mode 100644
index 00000000..afaaf4d4
--- /dev/null
+++ b/examples/cmos/testbench_digital.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+set -ex
+
+# iverlog simulation
+echo "Doing Verilog simulation with iverilog"
+iverilog -o counter_tb counter.v counter_tb.v
+./counter_tb; gtkwave counter_tb.gtkw &
+
+# yosys synthesis
+../../yosys counter_digital.ys
+
+# requires ngspice with xspice support enabled:
+ngspice testbench_digital.sp
+
diff --git a/examples/cmos/testbench_digital.sp b/examples/cmos/testbench_digital.sp
new file mode 100644
index 00000000..c5f9d598
--- /dev/null
+++ b/examples/cmos/testbench_digital.sp
@@ -0,0 +1,26 @@
+
+* load design and library
+.include cmos_cells_digital.sp
+.include synth.sp
+
+* input signals
+Vclk clk 0 PULSE(0 3 1 0.1 0.1 0.8 2)
+Vrst rst 0 PULSE(0 3 0.5 0.1 0.1 2.9 40)
+Ven en 0 PULSE(0 3 0.5 0.1 0.1 5.9 8)
+
+Xuut dclk drst den dout0 dout1 dout2 counter
+* Bridge to digital
+.model adc_buff adc_bridge(in_low = 0.8 in_high=2)
+.model dac_buff dac_bridge(out_high = 3.5)
+Aad [clk rst en] [dclk drst den] adc_buff
+Ada [dout0 dout1 dout2] [out0 out1 out2] dac_buff
+
+
+.tran 0.01 50
+
+.control
+run
+plot v(clk) v(rst)+5 v(en)+10 v(out0)+20 v(out1)+25 v(out2)+30
+.endc
+
+.end
diff --git a/examples/cxx-api/demomain.cc b/examples/cxx-api/demomain.cc
new file mode 100644
index 00000000..a6459330
--- /dev/null
+++ b/examples/cxx-api/demomain.cc
@@ -0,0 +1,22 @@
+// Note: Set ENABLE_LIBYOSYS=1 in Makefile or Makefile.conf to build libyosys.so
+// yosys-config --exec --cxx -o demomain --cxxflags --ldflags demomain.cc -lyosys -lstdc++
+
+#include <kernel/yosys.h>
+
+int main()
+{
+ Yosys::log_streams.push_back(&std::cout);
+ Yosys::log_error_stderr = true;
+
+ Yosys::yosys_setup();
+ Yosys::yosys_banner();
+
+ Yosys::run_pass("read_verilog example.v");
+ Yosys::run_pass("synth -noabc");
+ Yosys::run_pass("clean -purge");
+ Yosys::run_pass("write_blif example.blif");
+
+ Yosys::yosys_shutdown();
+ return 0;
+}
+
diff --git a/examples/cxx-api/evaldemo.cc b/examples/cxx-api/evaldemo.cc
new file mode 100644
index 00000000..e5cc8d8e
--- /dev/null
+++ b/examples/cxx-api/evaldemo.cc
@@ -0,0 +1,55 @@
+/* A simple Yosys plugin. (Copy&paste from http://stackoverflow.com/questions/32093541/how-does-the-yosys-consteval-api-work)
+
+Usage example:
+
+$ cat > evaldemo.v <<EOT
+module main(input [1:0] A, input [7:0] B, C, D, output [7:0] Y);
+ assign Y = A == 0 ? B : A == 1 ? C : A == 2 ? D : 42;
+endmodule
+EOT
+
+$ yosys-config --build evaldemo.so evaldemo.cc
+$ yosys -m evaldemo.so -p evaldemo evaldemo.v
+*/
+
+#include "kernel/yosys.h"
+#include "kernel/consteval.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct EvalDemoPass : public Pass
+{
+ EvalDemoPass() : Pass("evaldemo") { }
+
+ virtual void execute(vector<string>, Design *design)
+ {
+ Module *module = design->top_module();
+
+ if (module == nullptr)
+ log_error("No top module found!\n");
+
+ Wire *wire_a = module->wire("\\A");
+ Wire *wire_y = module->wire("\\Y");
+
+ if (wire_a == nullptr)
+ log_error("No wire A found!\n");
+
+ if (wire_y == nullptr)
+ log_error("No wire Y found!\n");
+
+ ConstEval ce(module);
+ for (int v = 0; v < 4; v++) {
+ ce.push();
+ ce.set(wire_a, Const(v, GetSize(wire_a)));
+ SigSpec sig_y = wire_y, sig_undef;
+ if (ce.eval(sig_y, sig_undef))
+ log("Eval results for A=%d: Y=%s\n", v, log_signal(sig_y));
+ else
+ log("Eval failed for A=%d: Missing value for %s\n", v, log_signal(sig_undef));
+ ce.pop();
+ }
+ }
+} EvalDemoPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/examples/smtbmc/.gitignore b/examples/smtbmc/.gitignore
new file mode 100644
index 00000000..a3f4f0f2
--- /dev/null
+++ b/examples/smtbmc/.gitignore
@@ -0,0 +1,22 @@
+demo1.smt2
+demo1.yslog
+demo2.smt2
+demo2.smtc
+demo2.vcd
+demo2.yslog
+demo2_tb
+demo2_tb.v
+demo2_tb.vcd
+demo3.smt2
+demo3.vcd
+demo3.yslog
+demo4.smt2
+demo4.vcd
+demo4.yslog
+demo5.smt2
+demo5.vcd
+demo5.yslog
+demo6.smt2
+demo6.yslog
+demo7.smt2
+demo7.yslog
diff --git a/examples/smtbmc/Makefile b/examples/smtbmc/Makefile
new file mode 100644
index 00000000..2f7060bd
--- /dev/null
+++ b/examples/smtbmc/Makefile
@@ -0,0 +1,59 @@
+
+all: demo1 demo2 demo3 demo4 demo5 demo6 demo7
+
+demo1: demo1.smt2
+ yosys-smtbmc --dump-vcd demo1.vcd demo1.smt2
+ yosys-smtbmc -i --dump-vcd demo1.vcd demo1.smt2
+
+demo2: demo2.smt2
+ yosys-smtbmc -g --dump-vcd demo2.vcd --dump-smtc demo2.smtc --dump-vlogtb demo2_tb.v demo2.smt2
+ iverilog -g2012 -o demo2_tb demo2_tb.v demo2.v
+ vvp demo2_tb +vcd=demo2_tb.vcd
+
+demo3: demo3.smt2
+ yosys-smtbmc --dump-vcd demo3.vcd --smtc demo3.smtc demo3.smt2
+
+demo4: demo4.smt2
+ yosys-smtbmc -s yices --dump-vcd demo4.vcd --smtc demo4.smtc demo4.smt2
+
+demo5: demo5.smt2
+ yosys-smtbmc -g -t 50 --dump-vcd demo5.vcd demo5.smt2
+
+demo6: demo6.smt2
+ yosys-smtbmc -t 1 demo6.smt2
+
+demo7: demo7.smt2
+ yosys-smtbmc -t 10 demo7.smt2
+
+demo1.smt2: demo1.v
+ yosys -ql demo1.yslog -p 'read_verilog -formal demo1.v; prep -top demo1 -nordff; write_smt2 -wires demo1.smt2'
+
+demo2.smt2: demo2.v
+ yosys -ql demo2.yslog -p 'read_verilog -formal demo2.v; prep -top demo2 -nordff; write_smt2 -wires demo2.smt2'
+
+demo3.smt2: demo3.v
+ yosys -ql demo3.yslog -p 'read_verilog -formal demo3.v; prep -top demo3 -nordff; write_smt2 -wires demo3.smt2'
+
+demo4.smt2: demo4.v
+ yosys -ql demo4.yslog -p 'read_verilog -formal demo4.v; prep -top demo4 -nordff; write_smt2 -wires demo4.smt2'
+
+demo5.smt2: demo5.v
+ yosys -ql demo5.yslog -p 'read_verilog -formal demo5.v; prep -top demo5 -nordff; write_smt2 -wires demo5.smt2'
+
+demo6.smt2: demo6.v
+ yosys -ql demo6.yslog -p 'read_verilog demo6.v; prep -top demo6 -nordff; assertpmux; opt -keepdc -fast; write_smt2 -wires demo6.smt2'
+
+demo7.smt2: demo7.v
+ yosys -ql demo7.yslog -p 'read_verilog -formal demo7.v; prep -top demo7 -nordff; write_smt2 -wires demo7.smt2'
+
+clean:
+ rm -f demo1.yslog demo1.smt2 demo1.vcd
+ rm -f demo2.yslog demo2.smt2 demo2.vcd demo2.smtc demo2_tb.v demo2_tb demo2_tb.vcd
+ rm -f demo3.yslog demo3.smt2 demo3.vcd
+ rm -f demo4.yslog demo4.smt2 demo4.vcd
+ rm -f demo5.yslog demo5.smt2 demo5.vcd
+ rm -f demo6.yslog demo6.smt2
+ rm -f demo7.yslog demo7.smt2
+
+.PHONY: demo1 demo2 demo3 demo4 demo5 demo6 demo7 clean
+
diff --git a/examples/smtbmc/demo1.v b/examples/smtbmc/demo1.v
new file mode 100644
index 00000000..567dde14
--- /dev/null
+++ b/examples/smtbmc/demo1.v
@@ -0,0 +1,19 @@
+module demo1(input clk, input addtwo, output iseven);
+ reg [3:0] cnt;
+ wire [3:0] next_cnt;
+
+ inc inc_inst (addtwo, iseven, cnt, next_cnt);
+
+ always @(posedge clk)
+ cnt = (iseven ? cnt == 10 : cnt == 11) ? 0 : next_cnt;
+
+`ifdef FORMAL
+ assert property (cnt != 15);
+ initial assume (!cnt[2]);
+`endif
+endmodule
+
+module inc(input addtwo, output iseven, input [3:0] a, output [3:0] y);
+ assign iseven = !a[0];
+ assign y = a + (addtwo ? 2 : 1);
+endmodule
diff --git a/examples/smtbmc/demo2.v b/examples/smtbmc/demo2.v
new file mode 100644
index 00000000..34745e89
--- /dev/null
+++ b/examples/smtbmc/demo2.v
@@ -0,0 +1,29 @@
+// Nothing to prove in this demo.
+// Just an example for memories, vcd dumps and vlog testbench dumps.
+
+`ifdef FORMAL
+`define assume(_expr_) assume(_expr_)
+`else
+`define assume(_expr_)
+`endif
+
+module demo2(input clk, input [4:0] addr, output reg [31:0] data);
+ reg [31:0] mem [0:31];
+ always @(posedge clk)
+ data <= mem[addr];
+
+ reg [31:0] used_addr = 0;
+ reg [31:0] used_dbits = 0;
+ reg initstate = 1;
+
+ always @(posedge clk) begin
+ initstate <= 0;
+ `assume(!used_addr[addr]);
+ used_addr[addr] <= 1;
+ if (!initstate) begin
+ `assume(data != 0);
+ `assume((used_dbits & data) == 0);
+ used_dbits <= used_dbits | data;
+ end
+ end
+endmodule
diff --git a/examples/smtbmc/demo3.smtc b/examples/smtbmc/demo3.smtc
new file mode 100644
index 00000000..f5e017cf
--- /dev/null
+++ b/examples/smtbmc/demo3.smtc
@@ -0,0 +1,5 @@
+initial
+assume [rst]
+
+always -1
+assert (= [-1:mem] [mem])
diff --git a/examples/smtbmc/demo3.v b/examples/smtbmc/demo3.v
new file mode 100644
index 00000000..13b3a197
--- /dev/null
+++ b/examples/smtbmc/demo3.v
@@ -0,0 +1,18 @@
+// Whatever the initial content of this memory is at reset, it will never change
+// see demo3.smtc for assumptions and assertions
+
+module demo3(input clk, rst, input [15:0] addr, output reg [31:0] data);
+ reg [31:0] mem [0:2**16-1];
+ reg [15:0] addr_q;
+
+ always @(posedge clk) begin
+ if (rst) begin
+ data <= mem[0] ^ 123456789;
+ addr_q <= 0;
+ end else begin
+ mem[addr_q] <= data ^ 123456789;
+ data <= mem[addr] ^ 123456789;
+ addr_q <= addr;
+ end
+ end
+endmodule
diff --git a/examples/smtbmc/demo4.smtc b/examples/smtbmc/demo4.smtc
new file mode 100644
index 00000000..2f91f816
--- /dev/null
+++ b/examples/smtbmc/demo4.smtc
@@ -0,0 +1,11 @@
+initial
+assume [rst]
+
+always -1
+assume (not [rst])
+assume (=> [-1:inv2] [inv2])
+
+final -2
+assume [-1:inv2]
+assume (not [-2:inv2])
+assert (= [r1] [r2])
diff --git a/examples/smtbmc/demo4.v b/examples/smtbmc/demo4.v
new file mode 100644
index 00000000..3f1b4727
--- /dev/null
+++ b/examples/smtbmc/demo4.v
@@ -0,0 +1,13 @@
+// Demo for "final" smtc constraints
+
+module demo4(input clk, rst, inv2, input [15:0] in, output reg [15:0] r1, r2);
+ always @(posedge clk) begin
+ if (rst) begin
+ r1 <= in;
+ r2 <= -in;
+ end else begin
+ r1 <= r1 + in;
+ r2 <= inv2 ? -(r2 - in) : (r2 - in);
+ end
+ end
+endmodule
diff --git a/examples/smtbmc/demo5.v b/examples/smtbmc/demo5.v
new file mode 100644
index 00000000..63ace307
--- /dev/null
+++ b/examples/smtbmc/demo5.v
@@ -0,0 +1,18 @@
+// Demo for $anyconst
+
+module demo5 (input clk);
+ wire [7:0] step_size = $anyconst;
+ reg [7:0] state = 0, count = 0;
+ reg [31:0] hash = 0;
+
+ always @(posedge clk) begin
+ count <= count + 1;
+ hash <= ((hash << 5) + hash) ^ state;
+ state <= state + step_size;
+ end
+
+ always @* begin
+ if (count == 42)
+ assert(hash == 32'h A18FAC0A);
+ end
+endmodule
diff --git a/examples/smtbmc/demo6.v b/examples/smtbmc/demo6.v
new file mode 100644
index 00000000..62a72e2a
--- /dev/null
+++ b/examples/smtbmc/demo6.v
@@ -0,0 +1,14 @@
+// Demo for assertpmux
+
+module demo6 (input A, B, C, D, E, output reg Y);
+ always @* begin
+ Y = 0;
+ if (A != B) begin
+ (* parallel_case *)
+ case (C)
+ A: Y = D;
+ B: Y = E;
+ endcase
+ end
+ end
+endmodule
diff --git a/examples/smtbmc/demo7.v b/examples/smtbmc/demo7.v
new file mode 100644
index 00000000..63f6272f
--- /dev/null
+++ b/examples/smtbmc/demo7.v
@@ -0,0 +1,19 @@
+// Demo for memory initialization
+
+module demo7;
+ wire [2:0] addr = $anyseq;
+ reg [15:0] memory [0:7];
+
+ initial begin
+ memory[0] = 1331;
+ memory[1] = 1331 + 1;
+ memory[2] = 1331 + 2;
+ memory[3] = 1331 + 4;
+ memory[4] = 1331 + 8;
+ memory[5] = 1331 + 16;
+ memory[6] = 1331 + 32;
+ memory[7] = 1331 + 64;
+ end
+
+ assert property (1000 < memory[addr] && memory[addr] < 2000);
+endmodule
diff --git a/frontends/ast/Makefile.inc b/frontends/ast/Makefile.inc
new file mode 100644
index 00000000..91d917c9
--- /dev/null
+++ b/frontends/ast/Makefile.inc
@@ -0,0 +1,6 @@
+
+OBJS += frontends/ast/ast.o
+OBJS += frontends/ast/simplify.o
+OBJS += frontends/ast/genrtlil.o
+OBJS += frontends/ast/dpicall.o
+
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
new file mode 100644
index 00000000..92513a24
--- /dev/null
+++ b/frontends/ast/ast.cc
@@ -0,0 +1,1180 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * ---
+ *
+ * This is the AST frontend library.
+ *
+ * The AST frontend library is not a frontend on it's own but provides a
+ * generic abstract syntax tree (AST) abstraction for HDL code and can be
+ * used by HDL frontends. See "ast.h" for an overview of the API and the
+ * Verilog frontend for an usage example.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "libs/sha1/sha1.h"
+#include "ast.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+using namespace AST;
+using namespace AST_INTERNAL;
+
+// instanciate 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)
+namespace AST_INTERNAL {
+ bool flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
+ bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire;
+ AstNode *current_ast, *current_ast_mod;
+ std::map<std::string, AstNode*> current_scope;
+ const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr = NULL;
+ RTLIL::SigSpec ignoreThisSignalsInInitial;
+ AstNode *current_always, *current_top_block, *current_block, *current_block_child;
+ AstModule *current_module;
+ bool current_always_clocked;
+}
+
+// convert node types to string
+std::string AST::type2str(AstNodeType type)
+{
+ switch (type)
+ {
+#define X(_item) case _item: return #_item;
+ X(AST_NONE)
+ X(AST_DESIGN)
+ X(AST_MODULE)
+ X(AST_TASK)
+ X(AST_FUNCTION)
+ X(AST_DPI_FUNCTION)
+ X(AST_WIRE)
+ X(AST_MEMORY)
+ X(AST_AUTOWIRE)
+ X(AST_PARAMETER)
+ X(AST_LOCALPARAM)
+ X(AST_DEFPARAM)
+ X(AST_PARASET)
+ X(AST_ARGUMENT)
+ X(AST_RANGE)
+ X(AST_MULTIRANGE)
+ X(AST_CONSTANT)
+ X(AST_REALVALUE)
+ X(AST_CELLTYPE)
+ X(AST_IDENTIFIER)
+ X(AST_PREFIX)
+ X(AST_ASSERT)
+ X(AST_ASSUME)
+ X(AST_FCALL)
+ X(AST_TO_BITS)
+ X(AST_TO_SIGNED)
+ X(AST_TO_UNSIGNED)
+ X(AST_CONCAT)
+ X(AST_REPLICATE)
+ X(AST_BIT_NOT)
+ X(AST_BIT_AND)
+ X(AST_BIT_OR)
+ X(AST_BIT_XOR)
+ X(AST_BIT_XNOR)
+ X(AST_REDUCE_AND)
+ X(AST_REDUCE_OR)
+ X(AST_REDUCE_XOR)
+ X(AST_REDUCE_XNOR)
+ X(AST_REDUCE_BOOL)
+ X(AST_SHIFT_LEFT)
+ X(AST_SHIFT_RIGHT)
+ X(AST_SHIFT_SLEFT)
+ X(AST_SHIFT_SRIGHT)
+ X(AST_LT)
+ X(AST_LE)
+ X(AST_EQ)
+ X(AST_NE)
+ X(AST_EQX)
+ X(AST_NEX)
+ X(AST_GE)
+ X(AST_GT)
+ X(AST_ADD)
+ X(AST_SUB)
+ X(AST_MUL)
+ X(AST_DIV)
+ X(AST_MOD)
+ X(AST_POW)
+ X(AST_POS)
+ X(AST_NEG)
+ X(AST_LOGIC_AND)
+ X(AST_LOGIC_OR)
+ X(AST_LOGIC_NOT)
+ X(AST_TERNARY)
+ X(AST_MEMRD)
+ X(AST_MEMWR)
+ X(AST_MEMINIT)
+ X(AST_TCALL)
+ X(AST_ASSIGN)
+ X(AST_CELL)
+ X(AST_PRIMITIVE)
+ X(AST_CELLARRAY)
+ X(AST_ALWAYS)
+ X(AST_INITIAL)
+ X(AST_BLOCK)
+ X(AST_ASSIGN_EQ)
+ X(AST_ASSIGN_LE)
+ X(AST_CASE)
+ X(AST_COND)
+ X(AST_CONDX)
+ X(AST_CONDZ)
+ X(AST_DEFAULT)
+ X(AST_FOR)
+ X(AST_WHILE)
+ X(AST_REPEAT)
+ X(AST_GENVAR)
+ X(AST_GENFOR)
+ X(AST_GENIF)
+ X(AST_GENCASE)
+ X(AST_GENBLOCK)
+ X(AST_POSEDGE)
+ X(AST_NEGEDGE)
+ X(AST_EDGE)
+ X(AST_PACKAGE)
+#undef X
+ default:
+ log_abort();
+ }
+}
+
+// check if attribute exists and has non-zero value
+bool AstNode::get_bool_attribute(RTLIL::IdString id)
+{
+ if (attributes.count(id) == 0)
+ return false;
+
+ AstNode *attr = attributes.at(id);
+ if (attr->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ id.c_str(), attr->filename.c_str(), attr->linenum);
+
+ return attr->integer != 0;
+}
+
+// create new node (AstNode constructor)
+// (the optional child arguments make it easier to create AST trees)
+AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *child3)
+{
+ static unsigned int hashidx_count = 123456789;
+ hashidx_count = mkhash_xorshift(hashidx_count);
+ hashidx_ = hashidx_count;
+
+ this->type = type;
+ filename = current_filename;
+ linenum = get_line_num();
+ is_input = false;
+ is_output = false;
+ is_reg = false;
+ is_signed = false;
+ is_string = false;
+ range_valid = false;
+ range_swapped = false;
+ port_id = 0;
+ range_left = -1;
+ range_right = 0;
+ integer = 0;
+ realvalue = 0;
+ id2ast = NULL;
+ basic_prep = false;
+
+ if (child1)
+ children.push_back(child1);
+ if (child2)
+ children.push_back(child2);
+ if (child3)
+ children.push_back(child3);
+}
+
+// create a (deep recursive) copy of a node
+AstNode *AstNode::clone()
+{
+ AstNode *that = new AstNode;
+ *that = *this;
+ for (auto &it : that->children)
+ it = it->clone();
+ for (auto &it : that->attributes)
+ it.second = it.second->clone();
+ return that;
+}
+
+// create a (deep recursive) copy of a node use 'other' as target root node
+void AstNode::cloneInto(AstNode *other)
+{
+ AstNode *tmp = clone();
+ other->delete_children();
+ *other = *tmp;
+ tmp->children.clear();
+ tmp->attributes.clear();
+ delete tmp;
+}
+
+// delete all children in this node
+void AstNode::delete_children()
+{
+ for (auto &it : children)
+ delete it;
+ children.clear();
+
+ for (auto &it : attributes)
+ delete it.second;
+ attributes.clear();
+}
+
+// AstNode destructor
+AstNode::~AstNode()
+{
+ delete_children();
+}
+
+// create a nice text representation of the node
+// (traverse tree by recursion, use 'other' pointer for diffing two AST trees)
+void AstNode::dumpAst(FILE *f, std::string indent)
+{
+ if (f == NULL) {
+ for (auto f : log_files)
+ dumpAst(f, indent);
+ return;
+ }
+
+ std::string type_name = type2str(type);
+ fprintf(f, "%s%s <%s:%d>", indent.c_str(), type_name.c_str(), filename.c_str(), linenum);
+
+ if (id2ast)
+ fprintf(f, " [%p -> %p]", this, id2ast);
+ else
+ fprintf(f, " [%p]", this);
+
+ if (!str.empty())
+ fprintf(f, " str='%s'", str.c_str());
+ if (!bits.empty()) {
+ fprintf(f, " bits='");
+ for (size_t i = bits.size(); i > 0; i--)
+ fprintf(f, "%c", bits[i-1] == RTLIL::S0 ? '0' :
+ bits[i-1] == RTLIL::S1 ? '1' :
+ bits[i-1] == RTLIL::Sx ? 'x' :
+ bits[i-1] == RTLIL::Sz ? 'z' : '?');
+ fprintf(f, "'(%d)", GetSize(bits));
+ }
+ if (is_input)
+ fprintf(f, " input");
+ if (is_output)
+ fprintf(f, " output");
+ if (is_reg)
+ fprintf(f, " reg");
+ if (is_signed)
+ fprintf(f, " signed");
+ if (port_id > 0)
+ fprintf(f, " port=%d", port_id);
+ if (range_valid || range_left != -1 || range_right != 0)
+ fprintf(f, " %srange=[%d:%d]%s", range_swapped ? "swapped_" : "", range_left, range_right, range_valid ? "" : "!");
+ if (integer != 0)
+ fprintf(f, " int=%u", (int)integer);
+ if (realvalue != 0)
+ fprintf(f, " real=%e", realvalue);
+ if (!multirange_dimensions.empty()) {
+ fprintf(f, " multirange=[");
+ for (int v : multirange_dimensions)
+ fprintf(f, " %d", v);
+ fprintf(f, " ]");
+ }
+ fprintf(f, "\n");
+
+ for (auto &it : attributes) {
+ fprintf(f, "%s ATTR %s:\n", indent.c_str(), it.first.c_str());
+ it.second->dumpAst(f, indent + " ");
+ }
+
+ for (size_t i = 0; i < children.size(); i++)
+ children[i]->dumpAst(f, indent + " ");
+
+ fflush(f);
+}
+
+// helper function for AstNode::dumpVlog()
+static std::string id2vl(std::string txt)
+{
+ if (txt.size() > 1 && txt[0] == '\\')
+ txt = txt.substr(1);
+ for (size_t i = 0; i < txt.size(); i++) {
+ if ('A' <= txt[i] && txt[i] <= 'Z') continue;
+ if ('a' <= txt[i] && txt[i] <= 'z') continue;
+ if ('0' <= txt[i] && txt[i] <= '9') continue;
+ if (txt[i] == '_') continue;
+ txt = "\\" + txt + " ";
+ break;
+ }
+ return txt;
+}
+
+// dump AST node as Verilog pseudo-code
+void AstNode::dumpVlog(FILE *f, std::string indent)
+{
+ bool first = true;
+ std::string txt;
+ std::vector<AstNode*> rem_children1, rem_children2;
+
+ if (f == NULL) {
+ for (auto f : log_files)
+ dumpVlog(f, indent);
+ return;
+ }
+
+ for (auto &it : attributes) {
+ fprintf(f, "%s" "(* %s = ", indent.c_str(), id2vl(it.first.str()).c_str());
+ it.second->dumpVlog(f, "");
+ fprintf(f, " *)%s", indent.empty() ? "" : "\n");
+ }
+
+ switch (type)
+ {
+ case AST_MODULE:
+ fprintf(f, "%s" "module %s(", indent.c_str(), id2vl(str).c_str());
+ for (auto child : children)
+ if (child->type == AST_WIRE && (child->is_input || child->is_output)) {
+ fprintf(f, "%s%s", first ? "" : ", ", id2vl(child->str).c_str());
+ first = false;
+ }
+ fprintf(f, ");\n");
+
+ for (auto child : children)
+ if (child->type == AST_PARAMETER || child->type == AST_LOCALPARAM || child->type == AST_DEFPARAM)
+ child->dumpVlog(f, indent + " ");
+ else
+ rem_children1.push_back(child);
+
+ for (auto child : rem_children1)
+ if (child->type == AST_WIRE || child->type == AST_AUTOWIRE || child->type == AST_MEMORY)
+ child->dumpVlog(f, indent + " ");
+ else
+ rem_children2.push_back(child);
+ rem_children1.clear();
+
+ for (auto child : rem_children2)
+ if (child->type == AST_TASK || child->type == AST_FUNCTION)
+ child->dumpVlog(f, indent + " ");
+ else
+ rem_children1.push_back(child);
+ rem_children2.clear();
+
+ for (auto child : rem_children1)
+ child->dumpVlog(f, indent + " ");
+ rem_children1.clear();
+
+ fprintf(f, "%s" "endmodule\n", indent.c_str());
+ break;
+
+ case AST_WIRE:
+ if (is_input && is_output)
+ fprintf(f, "%s" "inout", indent.c_str());
+ else if (is_input)
+ fprintf(f, "%s" "input", indent.c_str());
+ else if (is_output)
+ fprintf(f, "%s" "output", indent.c_str());
+ else if (!is_reg)
+ fprintf(f, "%s" "wire", indent.c_str());
+ if (is_reg)
+ fprintf(f, "%s" "reg", (is_input || is_output) ? " " : indent.c_str());
+ if (is_signed)
+ fprintf(f, " signed");
+ for (auto child : children) {
+ fprintf(f, " ");
+ child->dumpVlog(f, "");
+ }
+ fprintf(f, " %s", id2vl(str).c_str());
+ fprintf(f, ";\n");
+ break;
+
+ case AST_MEMORY:
+ fprintf(f, "%s" "memory", indent.c_str());
+ if (is_signed)
+ fprintf(f, " signed");
+ for (auto child : children) {
+ fprintf(f, " ");
+ child->dumpVlog(f, "");
+ if (first)
+ fprintf(f, " %s", id2vl(str).c_str());
+ first = false;
+ }
+ fprintf(f, ";\n");
+ break;
+
+ case AST_RANGE:
+ if (range_valid)
+ fprintf(f, "[%d:%d]", range_left, range_right);
+ else {
+ for (auto child : children) {
+ fprintf(f, "%c", first ? '[' : ':');
+ child->dumpVlog(f, "");
+ first = false;
+ }
+ fprintf(f, "]");
+ }
+ break;
+
+ case AST_ALWAYS:
+ fprintf(f, "%s" "always @", indent.c_str());
+ for (auto child : children) {
+ if (child->type != AST_POSEDGE && child->type != AST_NEGEDGE && child->type != AST_EDGE)
+ continue;
+ fprintf(f, first ? "(" : ", ");
+ child->dumpVlog(f, "");
+ first = false;
+ }
+ fprintf(f, first ? "*\n" : ")\n");
+ for (auto child : children) {
+ if (child->type != AST_POSEDGE && child->type != AST_NEGEDGE && child->type != AST_EDGE)
+ child->dumpVlog(f, indent + " ");
+ }
+ break;
+
+ case AST_INITIAL:
+ fprintf(f, "%s" "initial\n", indent.c_str());
+ for (auto child : children) {
+ if (child->type != AST_POSEDGE && child->type != AST_NEGEDGE && child->type != AST_EDGE)
+ child->dumpVlog(f, indent + " ");
+ }
+ break;
+
+ case AST_POSEDGE:
+ case AST_NEGEDGE:
+ case AST_EDGE:
+ if (type == AST_POSEDGE)
+ fprintf(f, "posedge ");
+ if (type == AST_NEGEDGE)
+ fprintf(f, "negedge ");
+ for (auto child : children)
+ child->dumpVlog(f, "");
+ break;
+
+ case AST_IDENTIFIER:
+ fprintf(f, "%s", id2vl(str).c_str());
+ for (auto child : children)
+ child->dumpVlog(f, "");
+ break;
+
+ case AST_CONSTANT:
+ if (!str.empty())
+ fprintf(f, "\"%s\"", str.c_str());
+ else if (bits.size() == 32)
+ fprintf(f, "%d", RTLIL::Const(bits).as_int());
+ else
+ fprintf(f, "%d'b %s", GetSize(bits), RTLIL::Const(bits).as_string().c_str());
+ break;
+
+ case AST_REALVALUE:
+ fprintf(f, "%e", realvalue);
+ break;
+
+ case AST_BLOCK:
+ if (children.size() == 1) {
+ children[0]->dumpVlog(f, indent);
+ } else {
+ fprintf(f, "%s" "begin\n", indent.c_str());
+ for (auto child : children)
+ child->dumpVlog(f, indent + " ");
+ fprintf(f, "%s" "end\n", indent.c_str());
+ }
+ break;
+
+ case AST_CASE:
+ if (!children.empty() && children[0]->type == AST_CONDX)
+ fprintf(f, "%s" "casex (", indent.c_str());
+ else if (!children.empty() && children[0]->type == AST_CONDZ)
+ fprintf(f, "%s" "casez (", indent.c_str());
+ else
+ fprintf(f, "%s" "case (", indent.c_str());
+ children[0]->dumpVlog(f, "");
+ fprintf(f, ")\n");
+ for (size_t i = 1; i < children.size(); i++) {
+ AstNode *child = children[i];
+ child->dumpVlog(f, indent + " ");
+ }
+ fprintf(f, "%s" "endcase\n", indent.c_str());
+ break;
+
+ case AST_COND:
+ case AST_CONDX:
+ case AST_CONDZ:
+ for (auto child : children) {
+ if (child->type == AST_BLOCK) {
+ fprintf(f, ":\n");
+ child->dumpVlog(f, indent + " ");
+ first = true;
+ } else {
+ fprintf(f, "%s", first ? indent.c_str() : ", ");
+ if (child->type == AST_DEFAULT)
+ fprintf(f, "default");
+ else
+ child->dumpVlog(f, "");
+ first = false;
+ }
+ }
+ break;
+
+ case AST_ASSIGN:
+ fprintf(f, "%sassign ", indent.c_str());
+ children[0]->dumpVlog(f, "");
+ fprintf(f, " = ");
+ children[1]->dumpVlog(f, "");
+ fprintf(f, ";\n");
+ break;
+
+ case AST_ASSIGN_EQ:
+ case AST_ASSIGN_LE:
+ fprintf(f, "%s", indent.c_str());
+ children[0]->dumpVlog(f, "");
+ fprintf(f, " %s ", type == AST_ASSIGN_EQ ? "=" : "<=");
+ children[1]->dumpVlog(f, "");
+ fprintf(f, ";\n");
+ break;
+
+ case AST_CONCAT:
+ fprintf(f, "{");
+ for (auto child : children) {
+ if (!first)
+ fprintf(f, ", ");
+ child->dumpVlog(f, "");
+ first = false;
+ }
+ fprintf(f, "}");
+ break;
+
+ case AST_REPLICATE:
+ fprintf(f, "{");
+ children[0]->dumpVlog(f, "");
+ fprintf(f, "{");
+ children[1]->dumpVlog(f, "");
+ fprintf(f, "}}");
+ break;
+
+ if (0) { case AST_BIT_NOT: txt = "~"; }
+ if (0) { case AST_REDUCE_AND: txt = "&"; }
+ if (0) { case AST_REDUCE_OR: txt = "|"; }
+ if (0) { case AST_REDUCE_XOR: txt = "^"; }
+ if (0) { case AST_REDUCE_XNOR: txt = "~^"; }
+ if (0) { case AST_REDUCE_BOOL: txt = "|"; }
+ if (0) { case AST_POS: txt = "+"; }
+ if (0) { case AST_NEG: txt = "-"; }
+ if (0) { case AST_LOGIC_NOT: txt = "!"; }
+ fprintf(f, "%s(", txt.c_str());
+ children[0]->dumpVlog(f, "");
+ fprintf(f, ")");
+ break;
+
+ if (0) { case AST_BIT_AND: txt = "&"; }
+ if (0) { case AST_BIT_OR: txt = "|"; }
+ if (0) { case AST_BIT_XOR: txt = "^"; }
+ if (0) { case AST_BIT_XNOR: txt = "~^"; }
+ if (0) { case AST_SHIFT_LEFT: txt = "<<"; }
+ if (0) { case AST_SHIFT_RIGHT: txt = ">>"; }
+ if (0) { case AST_SHIFT_SLEFT: txt = "<<<"; }
+ if (0) { case AST_SHIFT_SRIGHT: txt = ">>>"; }
+ if (0) { case AST_LT: txt = "<"; }
+ if (0) { case AST_LE: txt = "<="; }
+ if (0) { case AST_EQ: txt = "=="; }
+ if (0) { case AST_NE: txt = "!="; }
+ if (0) { case AST_EQX: txt = "==="; }
+ if (0) { case AST_NEX: txt = "!=="; }
+ if (0) { case AST_GE: txt = ">="; }
+ if (0) { case AST_GT: txt = ">"; }
+ if (0) { case AST_ADD: txt = "+"; }
+ if (0) { case AST_SUB: txt = "-"; }
+ if (0) { case AST_MUL: txt = "*"; }
+ if (0) { case AST_DIV: txt = "/"; }
+ if (0) { case AST_MOD: txt = "%"; }
+ if (0) { case AST_POW: txt = "**"; }
+ if (0) { case AST_LOGIC_AND: txt = "&&"; }
+ if (0) { case AST_LOGIC_OR: txt = "||"; }
+ fprintf(f, "(");
+ children[0]->dumpVlog(f, "");
+ fprintf(f, ")%s(", txt.c_str());
+ children[1]->dumpVlog(f, "");
+ fprintf(f, ")");
+ break;
+
+ case AST_TERNARY:
+ fprintf(f, "(");
+ children[0]->dumpVlog(f, "");
+ fprintf(f, ") ? (");
+ children[1]->dumpVlog(f, "");
+ fprintf(f, ") : (");
+ children[2]->dumpVlog(f, "");
+ fprintf(f, ")");
+ break;
+
+ default:
+ std::string type_name = type2str(type);
+ fprintf(f, "%s" "/** %s **/%s", indent.c_str(), type_name.c_str(), indent.empty() ? "" : "\n");
+ // dumpAst(f, indent, NULL);
+ }
+
+ fflush(f);
+}
+
+// check if two AST nodes are identical
+bool AstNode::operator==(const AstNode &other) const
+{
+ if (type != other.type)
+ return false;
+ if (children.size() != other.children.size())
+ return false;
+ if (str != other.str)
+ return false;
+ if (bits != other.bits)
+ return false;
+ if (is_input != other.is_input)
+ return false;
+ if (is_output != other.is_output)
+ return false;
+ if (is_reg != other.is_reg)
+ return false;
+ if (is_signed != other.is_signed)
+ return false;
+ if (is_string != other.is_string)
+ return false;
+ if (range_valid != other.range_valid)
+ return false;
+ if (range_swapped != other.range_swapped)
+ return false;
+ if (port_id != other.port_id)
+ return false;
+ if (range_left != other.range_left)
+ return false;
+ if (range_right != other.range_right)
+ return false;
+ if (integer != other.integer)
+ return false;
+ for (size_t i = 0; i < children.size(); i++)
+ if (*children[i] != *other.children[i])
+ return false;
+ return true;
+}
+
+// check if two AST nodes are not identical
+bool AstNode::operator!=(const AstNode &other) const
+{
+ return !(*this == other);
+}
+
+// check if this AST contains the given node
+bool AstNode::contains(const AstNode *other) const
+{
+ if (this == other)
+ return true;
+ for (auto child : children)
+ if (child->contains(other))
+ return true;
+ return false;
+}
+
+// create an AST node for a constant (using a 32 bit int as value)
+AstNode *AstNode::mkconst_int(uint32_t v, bool is_signed, int width)
+{
+ AstNode *node = new AstNode(AST_CONSTANT);
+ node->integer = v;
+ node->is_signed = is_signed;
+ for (int i = 0; i < width; i++) {
+ node->bits.push_back((v & 1) ? RTLIL::S1 : RTLIL::S0);
+ v = v >> 1;
+ }
+ node->range_valid = true;
+ node->range_left = width-1;
+ node->range_right = 0;
+ return node;
+}
+
+// create an AST node for a constant (using a bit vector as value)
+AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed)
+{
+ AstNode *node = new AstNode(AST_CONSTANT);
+ node->is_signed = is_signed;
+ node->bits = v;
+ for (size_t i = 0; i < 32; i++) {
+ if (i < node->bits.size())
+ node->integer |= (node->bits[i] == RTLIL::S1) << i;
+ else if (is_signed && !node->bits.empty())
+ node->integer |= (node->bits.back() == RTLIL::S1) << i;
+ }
+ node->range_valid = true;
+ node->range_left = node->bits.size()-1;
+ node->range_right = 0;
+ return node;
+}
+
+// create an AST node for a constant (using a string in bit vector form as value)
+AstNode *AstNode::mkconst_str(const std::vector<RTLIL::State> &v)
+{
+ AstNode *node = mkconst_str(RTLIL::Const(v).decode_string());
+ while (GetSize(node->bits) < GetSize(v))
+ node->bits.push_back(RTLIL::State::S0);
+ log_assert(node->bits == v);
+ return node;
+}
+
+// create an AST node for a constant (using a string as value)
+AstNode *AstNode::mkconst_str(const std::string &str)
+{
+ std::vector<RTLIL::State> data;
+ data.reserve(str.size() * 8);
+ for (size_t i = 0; i < str.size(); i++) {
+ unsigned char ch = str[str.size() - i - 1];
+ for (int j = 0; j < 8; j++) {
+ data.push_back((ch & 1) ? RTLIL::S1 : RTLIL::S0);
+ ch = ch >> 1;
+ }
+ }
+ AstNode *node = AstNode::mkconst_bits(data, false);
+ node->is_string = true;
+ node->str = str;
+ return node;
+}
+
+bool AstNode::bits_only_01()
+{
+ for (auto bit : bits)
+ if (bit != RTLIL::S0 && bit != RTLIL::S1)
+ return false;
+ return true;
+}
+
+RTLIL::Const AstNode::bitsAsConst(int width, bool is_signed)
+{
+ std::vector<RTLIL::State> bits = this->bits;
+ if (width >= 0 && width < int(bits.size()))
+ bits.resize(width);
+ if (width >= 0 && width > int(bits.size())) {
+ RTLIL::State extbit = RTLIL::State::S0;
+ if (is_signed && !bits.empty())
+ extbit = bits.back();
+ while (width > int(bits.size()))
+ bits.push_back(extbit);
+ }
+ return RTLIL::Const(bits);
+}
+
+RTLIL::Const AstNode::bitsAsConst(int width)
+{
+ return bitsAsConst(width, is_signed);
+}
+
+RTLIL::Const AstNode::asAttrConst()
+{
+ log_assert(type == AST_CONSTANT);
+
+ RTLIL::Const val;
+ val.bits = bits;
+
+ if (is_string) {
+ val.flags |= RTLIL::CONST_FLAG_STRING;
+ log_assert(val.decode_string() == str);
+ }
+
+ return val;
+}
+
+RTLIL::Const AstNode::asParaConst()
+{
+ RTLIL::Const val = asAttrConst();
+ if (is_signed)
+ val.flags |= RTLIL::CONST_FLAG_SIGNED;
+ return val;
+}
+
+bool AstNode::asBool()
+{
+ log_assert(type == AST_CONSTANT);
+ for (auto &bit : bits)
+ if (bit == RTLIL::State::S1)
+ return true;
+ return false;
+}
+
+int AstNode::isConst()
+{
+ if (type == AST_CONSTANT)
+ return 1;
+ if (type == AST_REALVALUE)
+ return 2;
+ return 0;
+}
+
+uint64_t AstNode::asInt(bool is_signed)
+{
+ if (type == AST_CONSTANT)
+ {
+ RTLIL::Const v = bitsAsConst(64, is_signed);
+ uint64_t ret = 0;
+
+ for (int i = 0; i < 64; i++)
+ if (v.bits.at(i) == RTLIL::State::S1)
+ ret |= uint64_t(1) << i;
+
+ return ret;
+ }
+
+ if (type == AST_REALVALUE)
+ return uint64_t(realvalue);
+
+ log_abort();
+}
+
+double AstNode::asReal(bool is_signed)
+{
+ if (type == AST_CONSTANT)
+ {
+ RTLIL::Const val(bits);
+
+ bool is_negative = is_signed && !val.bits.empty() && val.bits.back() == RTLIL::State::S1;
+ if (is_negative)
+ val = const_neg(val, val, false, false, val.bits.size());
+
+ double v = 0;
+ for (size_t i = 0; i < val.bits.size(); i++)
+ // IEEE Std 1800-2012 Par 6.12.2: Individual bits that are x or z in
+ // the net or the variable shall be treated as zero upon conversion.
+ if (val.bits.at(i) == RTLIL::State::S1)
+ v += exp2(i);
+ if (is_negative)
+ v *= -1;
+
+ return v;
+ }
+
+ if (type == AST_REALVALUE)
+ return realvalue;
+
+ log_abort();
+}
+
+RTLIL::Const AstNode::realAsConst(int width)
+{
+ double v = round(realvalue);
+ RTLIL::Const result;
+#ifdef EMSCRIPTEN
+ if (!isfinite(v)) {
+#else
+ if (!std::isfinite(v)) {
+#endif
+ result.bits = std::vector<RTLIL::State>(width, RTLIL::State::Sx);
+ } else {
+ bool is_negative = v < 0;
+ if (is_negative)
+ v *= -1;
+ for (int i = 0; i < width; i++, v /= 2)
+ result.bits.push_back((fmod(floor(v), 2) != 0) ? RTLIL::State::S1 : RTLIL::State::S0);
+ if (is_negative)
+ result = const_neg(result, result, false, false, result.bits.size());
+ }
+ return result;
+}
+
+// create a new AstModule from an AST_MODULE AST node
+static AstModule* process_module(AstNode *ast, bool defer)
+{
+ log_assert(ast->type == AST_MODULE);
+
+ if (defer)
+ log("Storing AST representation for module `%s'.\n", ast->str.c_str());
+ else
+ log("Generating RTLIL representation for module `%s'.\n", ast->str.c_str());
+
+ current_module = new AstModule;
+ current_module->ast = NULL;
+ current_module->name = ast->str;
+ current_module->attributes["\\src"] = stringf("%s:%d", ast->filename.c_str(), ast->linenum);
+
+ current_ast_mod = ast;
+ AstNode *ast_before_simplify = ast->clone();
+
+ if (flag_dump_ast1) {
+ log("Dumping Verilog AST before simplification:\n");
+ ast->dumpAst(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");
+ ast->dumpAst(NULL, " ");
+ log("--- END OF AST DUMP ---\n");
+ }
+
+ if (flag_dump_vlog) {
+ log("Dumping Verilog AST (as requested by dump_vlog option):\n");
+ ast->dumpVlog(NULL, " ");
+ log("--- END OF AST DUMP ---\n");
+ }
+
+ if (flag_lib) {
+ std::vector<AstNode*> new_children;
+ for (auto child : ast->children) {
+ if (child->type == AST_WIRE && (child->is_input || child->is_output)) {
+ new_children.push_back(child);
+ } else if (child->type == AST_PARAMETER) {
+ child->delete_children();
+ child->children.push_back(AstNode::mkconst_int(0, false, 0));
+ new_children.push_back(child);
+ } else {
+ delete child;
+ }
+ }
+ ast->children.swap(new_children);
+ ast->attributes["\\blackbox"] = AstNode::mkconst_int(1, false);
+ }
+
+ ignoreThisSignalsInInitial = RTLIL::SigSpec();
+
+ for (auto &attr : ast->attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), ast->filename.c_str(), ast->linenum);
+ current_module->attributes[attr.first] = attr.second->asAttrConst();
+ }
+ for (size_t i = 0; i < ast->children.size(); i++) {
+ AstNode *node = ast->children[i];
+ if (node->type == AST_WIRE || node->type == AST_MEMORY)
+ node->genRTLIL();
+ }
+ for (size_t i = 0; i < ast->children.size(); i++) {
+ AstNode *node = ast->children[i];
+ if (node->type != AST_WIRE && node->type != AST_MEMORY && node->type != AST_INITIAL)
+ node->genRTLIL();
+ }
+
+ ignoreThisSignalsInInitial.sort_and_unify();
+
+ for (size_t i = 0; i < ast->children.size(); i++) {
+ AstNode *node = ast->children[i];
+ if (node->type == AST_INITIAL)
+ node->genRTLIL();
+ }
+
+ ignoreThisSignalsInInitial = RTLIL::SigSpec();
+ }
+
+ current_module->ast = ast_before_simplify;
+ current_module->nolatches = flag_nolatches;
+ current_module->nomeminit = flag_nomeminit;
+ current_module->nomem2reg = flag_nomem2reg;
+ current_module->mem2reg = flag_mem2reg;
+ current_module->lib = flag_lib;
+ current_module->noopt = flag_noopt;
+ current_module->icells = flag_icells;
+ current_module->autowire = flag_autowire;
+ current_module->fixup_ports();
+
+ if (flag_dump_rtlil) {
+ log("Dumping generated RTLIL:\n");
+ log_module(current_module);
+ log("--- END OF RTLIL DUMP ---\n");
+ }
+
+ return current_module;
+}
+
+// create AstModule instances for all modules in the AST tree and add them to 'design'
+void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool dump_rtlil,
+ bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire)
+{
+ current_ast = ast;
+ flag_dump_ast1 = dump_ast1;
+ flag_dump_ast2 = dump_ast2;
+ flag_dump_vlog = dump_vlog;
+ flag_dump_rtlil = dump_rtlil;
+ flag_nolatches = nolatches;
+ flag_nomeminit = nomeminit;
+ flag_nomem2reg = nomem2reg;
+ flag_mem2reg = mem2reg;
+ flag_lib = lib;
+ flag_noopt = noopt;
+ flag_icells = icells;
+ flag_autowire = autowire;
+
+ std::vector<AstNode*> global_decls;
+
+ log_assert(current_ast->type == AST_DESIGN);
+ for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++)
+ {
+ if ((*it)->type == AST_MODULE)
+ {
+ for (auto n : global_decls)
+ (*it)->children.push_back(n->clone());
+
+ for (auto n : design->verilog_packages){
+ for (auto o : n->children) {
+ AstNode *cloned_node = o->clone();
+ cloned_node->str = n->str + std::string("::") + cloned_node->str.substr(1);
+ (*it)->children.push_back(cloned_node);
+ }
+ }
+
+ if (flag_icells && (*it)->str.substr(0, 2) == "\\$")
+ (*it)->str = (*it)->str.substr(1);
+
+ if (defer)
+ (*it)->str = "$abstract" + (*it)->str;
+
+ if (design->has((*it)->str)) {
+ if (!ignore_redef)
+ log_error("Re-definition of module `%s' at %s:%d!\n",
+ (*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum);
+ log("Ignoring re-definition of module `%s' at %s:%d!\n",
+ (*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum);
+ continue;
+ }
+
+ design->add(process_module(*it, defer));
+ }
+ else if ((*it)->type == AST_PACKAGE)
+ design->verilog_packages.push_back((*it)->clone());
+ else
+ global_decls.push_back(*it);
+ }
+}
+
+// AstModule destructor
+AstModule::~AstModule()
+{
+ if (ast != NULL)
+ delete ast;
+}
+
+// create a new parametric module (when needed) and return the name of the generated module
+RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters)
+{
+ std::string stripped_name = name.str();
+
+ if (stripped_name.substr(0, 9) == "$abstract")
+ stripped_name = stripped_name.substr(9);
+
+ log_header(design, "Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", stripped_name.c_str());
+
+ current_ast = NULL;
+ flag_dump_ast1 = false;
+ flag_dump_ast2 = false;
+ flag_dump_vlog = false;
+ flag_nolatches = nolatches;
+ flag_nomeminit = nomeminit;
+ flag_nomem2reg = nomem2reg;
+ flag_mem2reg = mem2reg;
+ flag_lib = lib;
+ flag_noopt = noopt;
+ flag_icells = icells;
+ flag_autowire = autowire;
+ use_internal_line_num();
+
+ std::string para_info;
+ AstNode *new_ast = ast->clone();
+
+ int para_counter = 0;
+ int orig_parameters_n = parameters.size();
+ for (auto it = new_ast->children.begin(); it != new_ast->children.end(); it++) {
+ AstNode *child = *it;
+ if (child->type != AST_PARAMETER)
+ continue;
+ para_counter++;
+ std::string para_id = child->str;
+ if (parameters.count(para_id) > 0) {
+ log("Parameter %s = %s\n", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[child->str])));
+ rewrite_parameter:
+ para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
+ delete child->children.at(0);
+ child->children[0] = AstNode::mkconst_bits(parameters[para_id].bits, (parameters[para_id].flags & RTLIL::CONST_FLAG_SIGNED) != 0);
+ parameters.erase(para_id);
+ continue;
+ }
+ para_id = stringf("$%d", para_counter);
+ if (parameters.count(para_id) > 0) {
+ log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
+ goto rewrite_parameter;
+ }
+ }
+ if (parameters.size() > 0)
+ log_error("Requested parameter `%s' does not exist in module `%s'!\n", parameters.begin()->first.c_str(), stripped_name.c_str());
+
+ std::string modname;
+
+ if (orig_parameters_n == 0)
+ modname = stripped_name;
+ else if (para_info.size() > 60)
+ modname = "$paramod$" + sha1(para_info) + stripped_name;
+ 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;
+ return modname;
+}
+
+RTLIL::Module *AstModule::clone() const
+{
+ AstModule *new_mod = new AstModule;
+ new_mod->name = name;
+ cloneInto(new_mod);
+
+ new_mod->ast = ast->clone();
+ new_mod->nolatches = nolatches;
+ new_mod->nomeminit = nomeminit;
+ new_mod->nomem2reg = nomem2reg;
+ new_mod->mem2reg = mem2reg;
+ new_mod->lib = lib;
+ new_mod->noopt = noopt;
+ new_mod->icells = icells;
+ new_mod->autowire = autowire;
+
+ return new_mod;
+}
+
+// internal dummy line number callbacks
+namespace {
+ int internal_line_num;
+ void internal_set_line_num(int n) {
+ internal_line_num = n;
+ }
+ int internal_get_line_num() {
+ return internal_line_num;
+ }
+}
+
+// use internal dummy line number callbacks
+void AST::use_internal_line_num()
+{
+ set_line_num = &internal_set_line_num;
+ get_line_num = &internal_get_line_num;
+}
+
+YOSYS_NAMESPACE_END
+
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
new file mode 100644
index 00000000..cd6e264e
--- /dev/null
+++ b/frontends/ast/ast.h
@@ -0,0 +1,319 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * ---
+ *
+ * This is the AST frontend library.
+ *
+ * The AST frontend library is not a frontend on it's own but provides a
+ * generic abstract syntax tree (AST) abstraction for HDL code and can be
+ * used by HDL frontends. See "ast.h" for an overview of the API and the
+ * Verilog frontend for an usage example.
+ *
+ */
+
+#ifndef AST_H
+#define AST_H
+
+#include "kernel/rtlil.h"
+#include <stdint.h>
+#include <set>
+
+YOSYS_NAMESPACE_BEGIN
+
+namespace AST
+{
+ // all node types, type2str() must be extended
+ // whenever a new node type is added here
+ enum AstNodeType
+ {
+ AST_NONE,
+ AST_DESIGN,
+ AST_MODULE,
+ AST_TASK,
+ AST_FUNCTION,
+ AST_DPI_FUNCTION,
+
+ AST_WIRE,
+ AST_MEMORY,
+ AST_AUTOWIRE,
+ AST_PARAMETER,
+ AST_LOCALPARAM,
+ AST_DEFPARAM,
+ AST_PARASET,
+ AST_ARGUMENT,
+ AST_RANGE,
+ AST_MULTIRANGE,
+ AST_CONSTANT,
+ AST_REALVALUE,
+ AST_CELLTYPE,
+ AST_IDENTIFIER,
+ AST_PREFIX,
+ AST_ASSERT,
+ AST_ASSUME,
+
+ AST_FCALL,
+ AST_TO_BITS,
+ AST_TO_SIGNED,
+ AST_TO_UNSIGNED,
+ AST_CONCAT,
+ AST_REPLICATE,
+ AST_BIT_NOT,
+ AST_BIT_AND,
+ AST_BIT_OR,
+ AST_BIT_XOR,
+ AST_BIT_XNOR,
+ AST_REDUCE_AND,
+ AST_REDUCE_OR,
+ AST_REDUCE_XOR,
+ AST_REDUCE_XNOR,
+ AST_REDUCE_BOOL,
+ AST_SHIFT_LEFT,
+ AST_SHIFT_RIGHT,
+ AST_SHIFT_SLEFT,
+ AST_SHIFT_SRIGHT,
+ AST_LT,
+ AST_LE,
+ AST_EQ,
+ AST_NE,
+ AST_EQX,
+ AST_NEX,
+ AST_GE,
+ AST_GT,
+ AST_ADD,
+ AST_SUB,
+ AST_MUL,
+ AST_DIV,
+ AST_MOD,
+ AST_POW,
+ AST_POS,
+ AST_NEG,
+ AST_LOGIC_AND,
+ AST_LOGIC_OR,
+ AST_LOGIC_NOT,
+ AST_TERNARY,
+ AST_MEMRD,
+ AST_MEMWR,
+ AST_MEMINIT,
+
+ AST_TCALL,
+ AST_ASSIGN,
+ AST_CELL,
+ AST_PRIMITIVE,
+ AST_CELLARRAY,
+ AST_ALWAYS,
+ AST_INITIAL,
+ AST_BLOCK,
+ AST_ASSIGN_EQ,
+ AST_ASSIGN_LE,
+ AST_CASE,
+ AST_COND,
+ AST_CONDX,
+ AST_CONDZ,
+ AST_DEFAULT,
+ AST_FOR,
+ AST_WHILE,
+ AST_REPEAT,
+
+ AST_GENVAR,
+ AST_GENFOR,
+ AST_GENIF,
+ AST_GENCASE,
+ AST_GENBLOCK,
+
+ AST_POSEDGE,
+ AST_NEGEDGE,
+ AST_EDGE,
+
+ AST_PACKAGE
+ };
+
+ // convert an node type to a string (e.g. for debug output)
+ std::string type2str(AstNodeType type);
+
+ // The AST is built using instances of this struct
+ struct AstNode
+ {
+ // for dict<> and pool<>
+ unsigned int hashidx_;
+ unsigned int hash() const { return hashidx_; }
+
+ // this nodes type
+ AstNodeType type;
+
+ // the list of child nodes for this node
+ std::vector<AstNode*> children;
+
+ // the list of attributes assigned to this node
+ std::map<RTLIL::IdString, AstNode*> attributes;
+ bool get_bool_attribute(RTLIL::IdString id);
+
+ // node content - most of it is unused in most node types
+ std::string str;
+ std::vector<RTLIL::State> bits;
+ bool is_input, is_output, is_reg, is_signed, is_string, range_valid, range_swapped;
+ int port_id, range_left, range_right;
+ uint32_t integer;
+ double realvalue;
+
+ // if this is a multirange memory then this vector contains offset and length of each dimension
+ std::vector<int> multirange_dimensions;
+
+ // this is set by simplify and used during RTLIL generation
+ AstNode *id2ast;
+
+ // this is used by simplify to detect if basic analysis has been performed already on the node
+ bool basic_prep;
+
+ // this is the original sourcecode location that resulted in this AST node
+ // it is automatically set by the constructor using AST::current_filename and
+ // the AST::get_line_num() callback function.
+ std::string filename;
+ int linenum;
+
+ // creating and deleting nodes
+ AstNode(AstNodeType type = AST_NONE, AstNode *child1 = NULL, AstNode *child2 = NULL, AstNode *child3 = NULL);
+ AstNode *clone();
+ void cloneInto(AstNode *other);
+ void delete_children();
+ ~AstNode();
+
+ enum mem2reg_flags
+ {
+ /* status flags */
+ MEM2REG_FL_ALL = 0x00000001,
+ MEM2REG_FL_ASYNC = 0x00000002,
+ MEM2REG_FL_INIT = 0x00000004,
+
+ /* candidate flags */
+ MEM2REG_FL_FORCED = 0x00000100,
+ MEM2REG_FL_SET_INIT = 0x00000200,
+ MEM2REG_FL_SET_ELSE = 0x00000400,
+ MEM2REG_FL_SET_ASYNC = 0x00000800,
+ MEM2REG_FL_EQ2 = 0x00001000,
+ MEM2REG_FL_CMPLX_LHS = 0x00002000,
+
+ /* proc flags */
+ MEM2REG_FL_EQ1 = 0x01000000,
+ };
+
+ // simplify() creates a simpler AST by unrolling for-loops, expanding generate blocks, etc.
+ // it also sets the id2ast pointers so that identifier lookups are fast in genRTLIL()
+ bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param);
+ AstNode *readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init);
+ void expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map);
+ void replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules);
+ void mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg_places,
+ dict<AstNode*, uint32_t> &mem2reg_flags, dict<AstNode*, uint32_t> &proc_flags, uint32_t &status_flags);
+ bool mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block, AstNode *&async_block);
+ bool mem2reg_check(pool<AstNode*> &mem2reg_set);
+ void mem2reg_remove(pool<AstNode*> &mem2reg_set, vector<AstNode*> &delnodes);
+ void meminfo(int &mem_width, int &mem_size, int &addr_bits);
+
+ // additional functionality for evaluating constant functions
+ struct varinfo_t { RTLIL::Const val; int offset; bool is_signed; };
+ 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);
+
+ // create a human-readable text representation of the AST (for debugging)
+ void dumpAst(FILE *f, std::string indent);
+ void dumpVlog(FILE *f, std::string indent);
+
+ // used by genRTLIL() for detecting expression width and sign
+ void detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real = NULL);
+ void detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real = NULL);
+
+ // create RTLIL code for this AST node
+ // for expressions the resulting signal vector is returned
+ // all generated cell instances, etc. are written to the RTLIL::Module pointed to by AST_INTERNAL::current_module
+ RTLIL::SigSpec genRTLIL(int width_hint = -1, bool sign_hint = false);
+ RTLIL::SigSpec genWidthRTLIL(int width, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr = NULL);
+
+ // compare AST nodes
+ bool operator==(const AstNode &other) const;
+ bool operator!=(const AstNode &other) const;
+ bool contains(const AstNode *other) const;
+
+ // helper functions for creating AST nodes for constants
+ static AstNode *mkconst_int(uint32_t v, bool is_signed, int width = 32);
+ static AstNode *mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed);
+ static AstNode *mkconst_str(const std::vector<RTLIL::State> &v);
+ static AstNode *mkconst_str(const std::string &str);
+
+ // helper function for creating sign-extended const objects
+ RTLIL::Const bitsAsConst(int width, bool is_signed);
+ RTLIL::Const bitsAsConst(int width = -1);
+ RTLIL::Const asAttrConst();
+ RTLIL::Const asParaConst();
+ uint64_t asInt(bool is_signed);
+ bool bits_only_01();
+ bool asBool();
+
+ // helper functions for real valued const eval
+ int isConst(); // return '1' for AST_CONSTANT and '2' for AST_REALVALUE
+ double asReal(bool is_signed);
+ RTLIL::Const realAsConst(int width);
+ };
+
+ // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
+ void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool dump_rtlil, bool nolatches, bool nomeminit,
+ bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire);
+
+ // parametric modules are supported directly by the AST library
+ // therefore we need our own derivate of RTLIL::Module with overloaded virtual functions
+ struct AstModule : RTLIL::Module {
+ AstNode *ast;
+ bool nolatches, nomeminit, nomem2reg, mem2reg, lib, noopt, icells, autowire;
+ virtual ~AstModule();
+ virtual RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters);
+ virtual RTLIL::Module *clone() const;
+ };
+
+ // this must be set by the language frontend before parsing the sources
+ // the AstNode constructor then uses current_filename and get_line_num()
+ // to initialize the filename and linenum properties of new nodes
+ extern std::string current_filename;
+ extern void (*set_line_num)(int);
+ extern int (*get_line_num)();
+
+ // set set_line_num and get_line_num to internal dummy functions (done by simplify() and AstModule::derive
+ // to control the filename and linenum properties of new nodes not generated by a frontend parser)
+ void use_internal_line_num();
+
+ // 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);
+}
+
+namespace AST_INTERNAL
+{
+ // internal state variables
+ extern bool flag_dump_ast1, flag_dump_ast2, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
+ extern bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire;
+ extern AST::AstNode *current_ast, *current_ast_mod;
+ extern std::map<std::string, AST::AstNode*> current_scope;
+ extern const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr;
+ extern RTLIL::SigSpec ignoreThisSignalsInInitial;
+ extern AST::AstNode *current_always, *current_top_block, *current_block, *current_block_child;
+ extern AST::AstModule *current_module;
+ extern bool current_always_clocked;
+ struct ProcessGenerator;
+}
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/frontends/ast/dpicall.cc b/frontends/ast/dpicall.cc
new file mode 100644
index 00000000..e241142d
--- /dev/null
+++ b/frontends/ast/dpicall.cc
@@ -0,0 +1,148 @@
+/*
+ * 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 "ast.h"
+
+#ifdef YOSYS_ENABLE_PLUGINS
+
+#include <dlfcn.h>
+#include <ffi.h>
+
+YOSYS_NAMESPACE_BEGIN
+
+typedef void (*ffi_fptr) ();
+
+static ffi_fptr resolve_fn (std::string symbol_name)
+{
+ if (symbol_name.find(':') != std::string::npos)
+ {
+ int pos = symbol_name.find(':');
+ std::string plugin_name = symbol_name.substr(0, pos);
+ std::string real_symbol_name = symbol_name.substr(pos+1);
+
+ while (loaded_plugin_aliases.count(plugin_name))
+ plugin_name = loaded_plugin_aliases.at(plugin_name);
+
+ if (loaded_plugins.count(plugin_name) == 0)
+ log_error("unable to resolve '%s': can't find plugin `%s'\n", symbol_name.c_str(), plugin_name.c_str());
+
+ void *symbol = dlsym(loaded_plugins.at(plugin_name), real_symbol_name.c_str());
+
+ if (symbol == nullptr)
+ log_error("unable to resolve '%s': can't find symbol `%s' in plugin `%s'\n",
+ symbol_name.c_str(), real_symbol_name.c_str(), plugin_name.c_str());
+
+ return (ffi_fptr) symbol;
+ }
+
+ for (auto &it : loaded_plugins) {
+ void *symbol = dlsym(it.second, symbol_name.c_str());
+ if (symbol != nullptr)
+ return (ffi_fptr) symbol;
+ }
+
+ void *symbol = dlsym(RTLD_DEFAULT, symbol_name.c_str());
+ if (symbol != nullptr)
+ return (ffi_fptr) symbol;
+
+ log_error("unable to resolve '%s'.\n", symbol_name.c_str());
+}
+
+AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, const std::vector<std::string> &argtypes, const std::vector<AstNode*> &args)
+{
+ AST::AstNode *newNode = nullptr;
+ union { double f64; float f32; int32_t i32; } value_store [args.size() + 1];
+ ffi_type *types [args.size() + 1];
+ void *values [args.size() + 1];
+ ffi_cif cif;
+ int status;
+
+ log("Calling DPI function `%s' and returning `%s':\n", fname.c_str(), rtype.c_str());
+
+ log_assert(GetSize(args) == GetSize(argtypes));
+ for (int i = 0; i < GetSize(args); i++) {
+ if (argtypes[i] == "real") {
+ log(" arg %d (%s): %f\n", i, argtypes[i].c_str(), args[i]->asReal(args[i]->is_signed));
+ value_store[i].f64 = args[i]->asReal(args[i]->is_signed);
+ values[i] = &value_store[i].f64;
+ types[i] = &ffi_type_double;
+ } else if (argtypes[i] == "shortreal") {
+ log(" arg %d (%s): %f\n", i, argtypes[i].c_str(), args[i]->asReal(args[i]->is_signed));
+ value_store[i].f32 = args[i]->asReal(args[i]->is_signed);
+ values[i] = &value_store[i].f32;
+ types[i] = &ffi_type_double;
+ } else if (argtypes[i] == "integer") {
+ log(" arg %d (%s): %lld\n", i, argtypes[i].c_str(), (long long)args[i]->asInt(args[i]->is_signed));
+ value_store[i].i32 = args[i]->asInt(args[i]->is_signed);
+ values[i] = &value_store[i].i32;
+ types[i] = &ffi_type_sint32;
+ } else {
+ log_error("invalid argtype '%s' for argument %d.\n", argtypes[i].c_str(), i);
+ }
+ }
+
+ if (rtype == "integer") {
+ types[args.size()] = &ffi_type_slong;
+ values[args.size()] = &value_store[args.size()].i32;
+ } else if (rtype == "shortreal") {
+ types[args.size()] = &ffi_type_float;
+ values[args.size()] = &value_store[args.size()].f32;
+ } else if (rtype == "real") {
+ types[args.size()] = &ffi_type_double;
+ values[args.size()] = &value_store[args.size()].f64;
+ } else {
+ log_error("invalid rtype '%s'.\n", rtype.c_str());
+ }
+
+ if ((status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, args.size(), types[args.size()], types)) != FFI_OK)
+ log_error("ffi_prep_cif failed: status %d.\n", status);
+
+ ffi_call(&cif, resolve_fn(fname.c_str()), values[args.size()], values);
+
+ if (rtype == "real") {
+ newNode = new AstNode(AST_REALVALUE);
+ newNode->realvalue = value_store[args.size()].f64;
+ log(" return realvalue: %g\n", newNode->asReal(true));
+ } else if (rtype == "shortreal") {
+ newNode = new AstNode(AST_REALVALUE);
+ newNode->realvalue = value_store[args.size()].f32;
+ log(" return realvalue: %g\n", newNode->asReal(true));
+ } else {
+ newNode = AstNode::mkconst_int(value_store[args.size()].i32, false);
+ log(" return integer: %lld\n", (long long)newNode->asInt(true));
+ }
+
+ return newNode;
+}
+
+YOSYS_NAMESPACE_END
+
+#else /* YOSYS_ENABLE_PLUGINS */
+
+YOSYS_NAMESPACE_BEGIN
+
+AST::AstNode *AST::dpi_call(const std::string&, const std::string &fname, const std::vector<std::string>&, const std::vector<AstNode*>&)
+{
+ log_error("Can't call DPI function `%s': this version of yosys is built without plugin support\n", fname.c_str());
+}
+
+YOSYS_NAMESPACE_END
+
+#endif /* YOSYS_ENABLE_PLUGINS */
+
diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc
new file mode 100644
index 00000000..db8d7409
--- /dev/null
+++ b/frontends/ast/genrtlil.cc
@@ -0,0 +1,1538 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * ---
+ *
+ * This is the AST frontend library.
+ *
+ * The AST frontend library is not a frontend on it's own but provides a
+ * generic abstract syntax tree (AST) abstraction for HDL code and can be
+ * used by HDL frontends. See "ast.h" for an overview of the API and the
+ * Verilog frontend for an usage example.
+ *
+ */
+
+#include "kernel/log.h"
+#include "kernel/utils.h"
+#include "libs/sha1/sha1.h"
+#include "ast.h"
+
+#include <sstream>
+#include <stdarg.h>
+#include <algorithm>
+
+YOSYS_NAMESPACE_BEGIN
+
+using namespace AST;
+using namespace AST_INTERNAL;
+
+// helper function for creating RTLIL code for unary operations
+static RTLIL::SigSpec uniop2rtlil(AstNode *that, std::string type, int result_width, const RTLIL::SigSpec &arg, bool gen_attributes = true)
+{
+ std::stringstream sstr;
+ sstr << type << "$" << that->filename << ":" << that->linenum << "$" << (autoidx++);
+
+ RTLIL::Cell *cell = current_module->addCell(sstr.str(), type);
+ cell->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum);
+
+ RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width);
+ wire->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum);
+
+ if (gen_attributes)
+ for (auto &attr : that->attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), that->filename.c_str(), that->linenum);
+ cell->attributes[attr.first] = attr.second->asAttrConst();
+ }
+
+ cell->parameters["\\A_SIGNED"] = RTLIL::Const(that->children[0]->is_signed);
+ cell->parameters["\\A_WIDTH"] = RTLIL::Const(arg.size());
+ cell->setPort("\\A", arg);
+
+ cell->parameters["\\Y_WIDTH"] = result_width;
+ cell->setPort("\\Y", wire);
+ return wire;
+}
+
+// helper function for extending bit width (preferred over SigSpec::extend() because of correct undef propagation in ConstEval)
+static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_signed)
+{
+ if (width <= sig.size()) {
+ sig.extend_u0(width, is_signed);
+ return;
+ }
+
+ std::stringstream sstr;
+ sstr << "$extend" << "$" << that->filename << ":" << that->linenum << "$" << (autoidx++);
+
+ RTLIL::Cell *cell = current_module->addCell(sstr.str(), "$pos");
+ cell->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum);
+
+ RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", width);
+ wire->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum);
+
+ if (that != NULL)
+ for (auto &attr : that->attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), that->filename.c_str(), that->linenum);
+ cell->attributes[attr.first] = attr.second->asAttrConst();
+ }
+
+ cell->parameters["\\A_SIGNED"] = RTLIL::Const(is_signed);
+ cell->parameters["\\A_WIDTH"] = RTLIL::Const(sig.size());
+ cell->setPort("\\A", sig);
+
+ cell->parameters["\\Y_WIDTH"] = width;
+ cell->setPort("\\Y", wire);
+ sig = wire;
+}
+
+// helper function for creating RTLIL code for binary operations
+static RTLIL::SigSpec binop2rtlil(AstNode *that, std::string type, int result_width, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
+{
+ std::stringstream sstr;
+ sstr << type << "$" << that->filename << ":" << that->linenum << "$" << (autoidx++);
+
+ RTLIL::Cell *cell = current_module->addCell(sstr.str(), type);
+ cell->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum);
+
+ RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width);
+ wire->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum);
+
+ for (auto &attr : that->attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), that->filename.c_str(), that->linenum);
+ cell->attributes[attr.first] = attr.second->asAttrConst();
+ }
+
+ cell->parameters["\\A_SIGNED"] = RTLIL::Const(that->children[0]->is_signed);
+ cell->parameters["\\B_SIGNED"] = RTLIL::Const(that->children[1]->is_signed);
+
+ cell->parameters["\\A_WIDTH"] = RTLIL::Const(left.size());
+ cell->parameters["\\B_WIDTH"] = RTLIL::Const(right.size());
+
+ cell->setPort("\\A", left);
+ cell->setPort("\\B", right);
+
+ cell->parameters["\\Y_WIDTH"] = result_width;
+ cell->setPort("\\Y", wire);
+ return wire;
+}
+
+// helper function for creating RTLIL code for multiplexers
+static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
+{
+ log_assert(cond.size() == 1);
+
+ std::stringstream sstr;
+ sstr << "$ternary$" << that->filename << ":" << that->linenum << "$" << (autoidx++);
+
+ RTLIL::Cell *cell = current_module->addCell(sstr.str(), "$mux");
+ cell->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum);
+
+ RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", left.size());
+ wire->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum);
+
+ for (auto &attr : that->attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), that->filename.c_str(), that->linenum);
+ cell->attributes[attr.first] = attr.second->asAttrConst();
+ }
+
+ cell->parameters["\\WIDTH"] = RTLIL::Const(left.size());
+
+ cell->setPort("\\A", right);
+ cell->setPort("\\B", left);
+ cell->setPort("\\S", cond);
+ cell->setPort("\\Y", wire);
+
+ return wire;
+}
+
+// helper class for converting AST always nodes to RTLIL processes
+struct AST_INTERNAL::ProcessGenerator
+{
+ // input and output structures
+ AstNode *always;
+ RTLIL::SigSpec initSyncSignals;
+ RTLIL::Process *proc;
+ RTLIL::SigSpec outputSignals;
+
+ // This always points to the RTLIL::CaseRule being filled at the moment
+ RTLIL::CaseRule *current_case;
+
+ // This map contains the replacement pattern to be used in the right hand side
+ // of an assignment. E.g. in the code "foo = bar; foo = func(foo);" the foo in the right
+ // hand side of the 2nd assignment needs to be replace with the temporary signal holding
+ // the value assigned in the first assignment. So when the first assignment is processed
+ // the according information is appended to subst_rvalue_from and subst_rvalue_to.
+ stackmap<RTLIL::SigBit, RTLIL::SigBit> subst_rvalue_map;
+
+ // This map contains the replacement pattern to be used in the left hand side
+ // of an assignment. E.g. in the code "always @(posedge clk) foo <= bar" the signal bar
+ // should not be connected to the signal foo. Instead it must be connected to the temporary
+ // signal that is used as input for the register that drives the signal foo.
+ stackmap<RTLIL::SigBit, RTLIL::SigBit> subst_lvalue_map;
+
+ // The code here generates a number of temporary signal for each output register. This
+ // map helps generating nice numbered names for all this temporary signals.
+ std::map<RTLIL::Wire*, int> new_temp_count;
+
+ // Buffer for generating the init action
+ RTLIL::SigSpec init_lvalue, init_rvalue;
+
+ ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg)
+ {
+ // generate process and simple root case
+ proc = new RTLIL::Process;
+ proc->attributes["\\src"] = stringf("%s:%d", always->filename.c_str(), always->linenum);
+ proc->name = stringf("$proc$%s:%d$%d", always->filename.c_str(), always->linenum, autoidx++);
+ for (auto &attr : always->attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), always->filename.c_str(), always->linenum);
+ proc->attributes[attr.first] = attr.second->asAttrConst();
+ }
+ current_module->processes[proc->name] = proc;
+ current_case = &proc->root_case;
+
+ // create initial temporary signal for all output registers
+ RTLIL::SigSpec subst_lvalue_from, subst_lvalue_to;
+ collect_lvalues(subst_lvalue_from, always, true, true);
+ subst_lvalue_to = new_temp_signal(subst_lvalue_from);
+ subst_lvalue_map = subst_lvalue_from.to_sigbit_map(subst_lvalue_to);
+
+ bool found_global_syncs = false;
+ bool found_anyedge_syncs = false;
+ for (auto child : always->children)
+ if (child->type == AST_EDGE) {
+ if (GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER && child->children.at(0)->str == "\\$global_clock")
+ found_global_syncs = true;
+ else
+ found_anyedge_syncs = true;
+ }
+
+ if (found_anyedge_syncs) {
+ if (found_global_syncs)
+ log_error("Found non-synthesizable event list at %s:%d!\n", always->filename.c_str(), always->linenum);
+ log("Note: Assuming pure combinatorial block at %s:%d in\n", always->filename.c_str(), always->linenum);
+ log("compliance with IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002. Recommending\n");
+ log("use of @* instead of @(...) for better match of synthesis and simulation.\n");
+ }
+
+ // create syncs for the process
+ bool found_clocked_sync = false;
+ for (auto child : always->children)
+ if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE) {
+ found_clocked_sync = true;
+ if (found_global_syncs || found_anyedge_syncs)
+ log_error("Found non-synthesizable event list at %s:%d!\n", always->filename.c_str(), always->linenum);
+ RTLIL::SyncRule *syncrule = new RTLIL::SyncRule;
+ syncrule->type = child->type == AST_POSEDGE ? RTLIL::STp : RTLIL::STn;
+ syncrule->signal = child->children[0]->genRTLIL();
+ if (GetSize(syncrule->signal) != 1)
+ log_error("Found posedge/negedge event on a signal that is not 1 bit wide at %s:%d!\n", always->filename.c_str(), always->linenum);
+ addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to, true);
+ proc->syncs.push_back(syncrule);
+ }
+ if (proc->syncs.empty()) {
+ RTLIL::SyncRule *syncrule = new RTLIL::SyncRule;
+ syncrule->type = found_global_syncs ? RTLIL::STg : RTLIL::STa;
+ syncrule->signal = RTLIL::SigSpec();
+ addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to, true);
+ proc->syncs.push_back(syncrule);
+ }
+
+ // create initial assignments for the temporary signals
+ if ((flag_nolatches || always->get_bool_attribute("\\nolatches") || current_module->get_bool_attribute("\\nolatches")) && !found_clocked_sync) {
+ subst_rvalue_map = subst_lvalue_from.to_sigbit_dict(RTLIL::SigSpec(RTLIL::State::Sx, GetSize(subst_lvalue_from)));
+ } else {
+ addChunkActions(current_case->actions, subst_lvalue_to, subst_lvalue_from);
+ }
+
+ // process the AST
+ for (auto child : always->children)
+ if (child->type == AST_BLOCK)
+ processAst(child);
+
+ if (initSyncSignals.size() > 0)
+ {
+ RTLIL::SyncRule *sync = new RTLIL::SyncRule;
+ sync->type = RTLIL::SyncType::STi;
+ proc->syncs.push_back(sync);
+
+ log_assert(init_lvalue.size() == init_rvalue.size());
+
+ int offset = 0;
+ for (auto &init_lvalue_c : init_lvalue.chunks()) {
+ RTLIL::SigSpec lhs = init_lvalue_c;
+ RTLIL::SigSpec rhs = init_rvalue.extract(offset, init_lvalue_c.width);
+ remove_unwanted_lvalue_bits(lhs, rhs);
+ sync->actions.push_back(RTLIL::SigSig(lhs, rhs));
+ offset += lhs.size();
+ }
+ }
+
+ outputSignals = RTLIL::SigSpec(subst_lvalue_from);
+ }
+
+ void remove_unwanted_lvalue_bits(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs)
+ {
+ RTLIL::SigSpec new_lhs, new_rhs;
+
+ log_assert(GetSize(lhs) == GetSize(rhs));
+ for (int i = 0; i < GetSize(lhs); i++) {
+ if (lhs[i].wire == nullptr)
+ continue;
+ new_lhs.append(lhs[i]);
+ new_rhs.append(rhs[i]);
+ }
+
+ lhs = new_lhs;
+ rhs = new_rhs;
+ }
+
+ // create new temporary signals
+ RTLIL::SigSpec new_temp_signal(RTLIL::SigSpec sig)
+ {
+ std::vector<RTLIL::SigChunk> chunks = sig.chunks();
+
+ for (int i = 0; i < GetSize(chunks); i++)
+ {
+ RTLIL::SigChunk &chunk = chunks[i];
+ if (chunk.wire == NULL)
+ continue;
+
+ std::string wire_name;
+ do {
+ wire_name = stringf("$%d%s[%d:%d]", new_temp_count[chunk.wire]++,
+ chunk.wire->name.c_str(), chunk.width+chunk.offset-1, chunk.offset);;
+ if (chunk.wire->name.str().find('$') != std::string::npos)
+ wire_name += stringf("$%d", autoidx++);
+ } while (current_module->wires_.count(wire_name) > 0);
+
+ RTLIL::Wire *wire = current_module->addWire(wire_name, chunk.width);
+ wire->attributes["\\src"] = stringf("%s:%d", always->filename.c_str(), always->linenum);
+
+ chunk.wire = wire;
+ chunk.offset = 0;
+ }
+
+ return chunks;
+ }
+
+ // recursively traverse the AST an collect all assigned signals
+ void collect_lvalues(RTLIL::SigSpec &reg, AstNode *ast, bool type_eq, bool type_le, bool run_sort_and_unify = true)
+ {
+ switch (ast->type)
+ {
+ case AST_CASE:
+ for (auto child : ast->children)
+ if (child != ast->children[0]) {
+ log_assert(child->type == AST_COND || child->type == AST_CONDX || child->type == AST_CONDZ);
+ collect_lvalues(reg, child, type_eq, type_le, false);
+ }
+ break;
+
+ case AST_COND:
+ case AST_CONDX:
+ case AST_CONDZ:
+ case AST_ALWAYS:
+ case AST_INITIAL:
+ for (auto child : ast->children)
+ if (child->type == AST_BLOCK)
+ collect_lvalues(reg, child, type_eq, type_le, false);
+ break;
+
+ case AST_BLOCK:
+ for (auto child : ast->children) {
+ if (child->type == AST_ASSIGN_EQ && type_eq)
+ reg.append(child->children[0]->genRTLIL());
+ if (child->type == AST_ASSIGN_LE && type_le)
+ reg.append(child->children[0]->genRTLIL());
+ if (child->type == AST_CASE || child->type == AST_BLOCK)
+ collect_lvalues(reg, child, type_eq, type_le, false);
+ }
+ break;
+
+ default:
+ log_abort();
+ }
+
+ if (run_sort_and_unify) {
+ std::set<RTLIL::SigBit> sorted_reg;
+ for (auto bit : reg)
+ if (bit.wire)
+ sorted_reg.insert(bit);
+ reg = RTLIL::SigSpec(sorted_reg);
+ }
+ }
+
+ // remove all assignments to the given signal pattern in a case and all its children.
+ // e.g. when the last statement in the code "a = 23; if (b) a = 42; a = 0;" is processed this
+ // function is called to clean up the first two assignments as they are overwritten by
+ // the third assignment.
+ void removeSignalFromCaseTree(const RTLIL::SigSpec &pattern, RTLIL::CaseRule *cs)
+ {
+ for (auto it = cs->actions.begin(); it != cs->actions.end(); it++)
+ it->first.remove2(pattern, &it->second);
+
+ for (auto it = cs->switches.begin(); it != cs->switches.end(); it++)
+ for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++)
+ removeSignalFromCaseTree(pattern, *it2);
+ }
+
+ // add an assignment (aka "action") but split it up in chunks. this way huge assignments
+ // are avoided and the generated $mux cells have a more "natural" size.
+ void addChunkActions(std::vector<RTLIL::SigSig> &actions, RTLIL::SigSpec lvalue, RTLIL::SigSpec rvalue, bool inSyncRule = false)
+ {
+ if (inSyncRule && initSyncSignals.size() > 0) {
+ init_lvalue.append(lvalue.extract(initSyncSignals));
+ init_rvalue.append(lvalue.extract(initSyncSignals, &rvalue));
+ lvalue.remove2(initSyncSignals, &rvalue);
+ }
+ log_assert(lvalue.size() == rvalue.size());
+
+ int offset = 0;
+ for (auto &lvalue_c : lvalue.chunks()) {
+ RTLIL::SigSpec lhs = lvalue_c;
+ RTLIL::SigSpec rhs = rvalue.extract(offset, lvalue_c.width);
+ if (inSyncRule && lvalue_c.wire && lvalue_c.wire->get_bool_attribute("\\nosync"))
+ rhs = RTLIL::SigSpec(RTLIL::State::Sx, rhs.size());
+ remove_unwanted_lvalue_bits(lhs, rhs);
+ actions.push_back(RTLIL::SigSig(lhs, rhs));
+ offset += lhs.size();
+ }
+ }
+
+ // recursively process the AST and fill the RTLIL::Process
+ void processAst(AstNode *ast)
+ {
+ switch (ast->type)
+ {
+ case AST_BLOCK:
+ for (auto child : ast->children)
+ processAst(child);
+ break;
+
+ case AST_ASSIGN_EQ:
+ case AST_ASSIGN_LE:
+ {
+ RTLIL::SigSpec unmapped_lvalue = ast->children[0]->genRTLIL(), lvalue = unmapped_lvalue;
+ RTLIL::SigSpec rvalue = ast->children[1]->genWidthRTLIL(lvalue.size(), &subst_rvalue_map.stdmap());
+
+ pool<SigBit> lvalue_sigbits;
+ for (int i = 0; i < GetSize(lvalue); i++) {
+ if (lvalue_sigbits.count(lvalue[i]) > 0) {
+ unmapped_lvalue.remove(i);
+ lvalue.remove(i);
+ rvalue.remove(i--);
+ } else
+ lvalue_sigbits.insert(lvalue[i]);
+ }
+
+ lvalue.replace(subst_lvalue_map.stdmap());
+
+ if (ast->type == AST_ASSIGN_EQ) {
+ for (int i = 0; i < GetSize(unmapped_lvalue); i++)
+ subst_rvalue_map.set(unmapped_lvalue[i], rvalue[i]);
+ }
+
+ removeSignalFromCaseTree(lvalue, current_case);
+ remove_unwanted_lvalue_bits(lvalue, rvalue);
+ current_case->actions.push_back(RTLIL::SigSig(lvalue, rvalue));
+ }
+ break;
+
+ case AST_CASE:
+ {
+ RTLIL::SwitchRule *sw = new RTLIL::SwitchRule;
+ sw->attributes["\\src"] = stringf("%s:%d", ast->filename.c_str(), ast->linenum);
+ sw->signal = ast->children[0]->genWidthRTLIL(-1, &subst_rvalue_map.stdmap());
+ current_case->switches.push_back(sw);
+
+ for (auto &attr : ast->attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), ast->filename.c_str(), ast->linenum);
+ sw->attributes[attr.first] = attr.second->asAttrConst();
+ }
+
+ RTLIL::SigSpec this_case_eq_lvalue;
+ collect_lvalues(this_case_eq_lvalue, ast, true, false);
+
+ RTLIL::SigSpec this_case_eq_ltemp = new_temp_signal(this_case_eq_lvalue);
+
+ RTLIL::SigSpec this_case_eq_rvalue = this_case_eq_lvalue;
+ this_case_eq_rvalue.replace(subst_rvalue_map.stdmap());
+
+ RTLIL::CaseRule *default_case = NULL;
+ RTLIL::CaseRule *last_generated_case = NULL;
+ for (auto child : ast->children)
+ {
+ if (child == ast->children[0])
+ continue;
+ log_assert(child->type == AST_COND || child->type == AST_CONDX || child->type == AST_CONDZ);
+
+ subst_lvalue_map.save();
+ subst_rvalue_map.save();
+
+ for (int i = 0; i < GetSize(this_case_eq_lvalue); i++)
+ subst_lvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]);
+
+ RTLIL::CaseRule *backup_case = current_case;
+ current_case = new RTLIL::CaseRule;
+ last_generated_case = current_case;
+ addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue);
+ for (auto node : child->children) {
+ if (node->type == AST_DEFAULT)
+ default_case = current_case;
+ else if (node->type == AST_BLOCK)
+ processAst(node);
+ else
+ current_case->compare.push_back(node->genWidthRTLIL(sw->signal.size(), &subst_rvalue_map.stdmap()));
+ }
+ if (default_case != current_case)
+ sw->cases.push_back(current_case);
+ else
+ log_assert(current_case->compare.size() == 0);
+ current_case = backup_case;
+
+ subst_lvalue_map.restore();
+ subst_rvalue_map.restore();
+ }
+
+ if (last_generated_case != NULL && ast->get_bool_attribute("\\full_case") && default_case == NULL) {
+ last_generated_case->compare.clear();
+ } else {
+ if (default_case == NULL) {
+ default_case = new RTLIL::CaseRule;
+ addChunkActions(default_case->actions, this_case_eq_ltemp, this_case_eq_rvalue);
+ }
+ sw->cases.push_back(default_case);
+ }
+
+ for (int i = 0; i < GetSize(this_case_eq_lvalue); i++)
+ subst_rvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]);
+
+ this_case_eq_lvalue.replace(subst_lvalue_map.stdmap());
+ removeSignalFromCaseTree(this_case_eq_lvalue, current_case);
+ addChunkActions(current_case->actions, this_case_eq_lvalue, this_case_eq_ltemp);
+ }
+ break;
+
+ case AST_WIRE:
+ log_error("Found wire declaration in block without label at at %s:%d!\n", ast->filename.c_str(), ast->linenum);
+ break;
+
+ case AST_PARAMETER:
+ case AST_LOCALPARAM:
+ log_error("Found parameter declaration in block without label at at %s:%d!\n", ast->filename.c_str(), ast->linenum);
+ break;
+
+ case AST_NONE:
+ case AST_TCALL:
+ case AST_FOR:
+ break;
+
+ default:
+ log_abort();
+ }
+ }
+};
+
+// detect sign and width of an expression
+void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real)
+{
+ std::string type_name;
+ bool sub_sign_hint = true;
+ int sub_width_hint = -1;
+ int this_width = 0;
+ AstNode *range = NULL;
+ AstNode *id_ast = NULL;
+
+ bool local_found_real = false;
+ if (found_real == NULL)
+ found_real = &local_found_real;
+
+ switch (type)
+ {
+ case AST_CONSTANT:
+ width_hint = max(width_hint, int(bits.size()));
+ if (!is_signed)
+ sign_hint = false;
+ break;
+
+ case AST_REALVALUE:
+ *found_real = true;
+ width_hint = max(width_hint, 32);
+ break;
+
+ case AST_IDENTIFIER:
+ id_ast = id2ast;
+ if (id_ast == NULL && current_scope.count(str))
+ id_ast = current_scope.at(str);
+ if (!id_ast)
+ log_error("Failed to resolve identifier %s for width detection at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
+ if (id_ast->type == AST_PARAMETER || id_ast->type == AST_LOCALPARAM) {
+ if (id_ast->children.size() > 1 && id_ast->children[1]->range_valid) {
+ this_width = id_ast->children[1]->range_left - id_ast->children[1]->range_right + 1;
+ } else
+ if (id_ast->children[0]->type != AST_CONSTANT)
+ while (id_ast->simplify(true, false, false, 1, -1, false, true)) { }
+ if (id_ast->children[0]->type == AST_CONSTANT)
+ this_width = id_ast->children[0]->bits.size();
+ else
+ log_error("Failed to detect width for parameter %s at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
+ if (children.size() != 0)
+ range = children[0];
+ } else if (id_ast->type == AST_WIRE || id_ast->type == AST_AUTOWIRE) {
+ if (!id_ast->range_valid) {
+ if (id_ast->type == AST_AUTOWIRE)
+ this_width = 1;
+ else {
+ // current_ast_mod->dumpAst(NULL, "mod> ");
+ // log("---\n");
+ // id_ast->dumpAst(NULL, "decl> ");
+ // dumpAst(NULL, "ref> ");
+ log_error("Failed to detect width of signal access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
+ }
+ } else {
+ this_width = id_ast->range_left - id_ast->range_right + 1;
+ if (children.size() != 0)
+ range = children[0];
+ }
+ } else if (id_ast->type == AST_GENVAR) {
+ this_width = 32;
+ } else if (id_ast->type == AST_MEMORY) {
+ if (!id_ast->children[0]->range_valid)
+ log_error("Failed to detect width of memory access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
+ this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1;
+ } else
+ log_error("Failed to detect width for identifier %s at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
+ if (range) {
+ if (range->children.size() == 1)
+ this_width = 1;
+ else if (!range->range_valid) {
+ AstNode *left_at_zero_ast = children[0]->children[0]->clone();
+ AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : left_at_zero_ast->clone();
+ while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
+ while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
+ if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
+ log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+ this_width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
+ delete left_at_zero_ast;
+ delete right_at_zero_ast;
+ } else
+ this_width = range->range_left - range->range_right + 1;
+ sign_hint = false;
+ }
+ width_hint = max(width_hint, this_width);
+ if (!id_ast->is_signed)
+ sign_hint = false;
+ break;
+
+ case AST_TO_BITS:
+ while (children[0]->simplify(true, false, false, 1, -1, false, false) == true) { }
+ if (children[0]->type != AST_CONSTANT)
+ log_error("Left operand of tobits expression is not constant at %s:%d!\n", filename.c_str(), linenum);
+ children[1]->detectSignWidthWorker(sub_width_hint, sign_hint);
+ width_hint = max(width_hint, children[0]->bitsAsConst().as_int());
+ break;
+
+ case AST_TO_SIGNED:
+ children.at(0)->detectSignWidthWorker(width_hint, sub_sign_hint);
+ break;
+
+ case AST_TO_UNSIGNED:
+ children.at(0)->detectSignWidthWorker(width_hint, sub_sign_hint);
+ sign_hint = false;
+ break;
+
+ case AST_CONCAT:
+ for (auto child : children) {
+ sub_width_hint = 0;
+ sub_sign_hint = true;
+ child->detectSignWidthWorker(sub_width_hint, sub_sign_hint);
+ this_width += sub_width_hint;
+ }
+ width_hint = max(width_hint, this_width);
+ sign_hint = false;
+ break;
+
+ case AST_REPLICATE:
+ while (children[0]->simplify(true, false, false, 1, -1, false, true) == true) { }
+ if (children[0]->type != AST_CONSTANT)
+ log_error("Left operand of replicate expression is not constant at %s:%d!\n", filename.c_str(), linenum);
+ children[1]->detectSignWidthWorker(sub_width_hint, sub_sign_hint);
+ width_hint = max(width_hint, children[0]->bitsAsConst().as_int() * sub_width_hint);
+ sign_hint = false;
+ break;
+
+ case AST_NEG:
+ case AST_BIT_NOT:
+ case AST_POS:
+ children[0]->detectSignWidthWorker(width_hint, sign_hint, found_real);
+ break;
+
+ case AST_BIT_AND:
+ case AST_BIT_OR:
+ case AST_BIT_XOR:
+ case AST_BIT_XNOR:
+ for (auto child : children)
+ child->detectSignWidthWorker(width_hint, sign_hint, found_real);
+ break;
+
+ case AST_REDUCE_AND:
+ case AST_REDUCE_OR:
+ case AST_REDUCE_XOR:
+ case AST_REDUCE_XNOR:
+ case AST_REDUCE_BOOL:
+ width_hint = max(width_hint, 1);
+ sign_hint = false;
+ break;
+
+ case AST_SHIFT_LEFT:
+ case AST_SHIFT_RIGHT:
+ case AST_SHIFT_SLEFT:
+ case AST_SHIFT_SRIGHT:
+ case AST_POW:
+ children[0]->detectSignWidthWorker(width_hint, sign_hint, found_real);
+ break;
+
+ case AST_LT:
+ case AST_LE:
+ case AST_EQ:
+ case AST_NE:
+ case AST_EQX:
+ case AST_NEX:
+ case AST_GE:
+ case AST_GT:
+ width_hint = max(width_hint, 1);
+ sign_hint = false;
+ break;
+
+ case AST_ADD:
+ case AST_SUB:
+ case AST_MUL:
+ case AST_DIV:
+ case AST_MOD:
+ for (auto child : children)
+ child->detectSignWidthWorker(width_hint, sign_hint, found_real);
+ break;
+
+ case AST_LOGIC_AND:
+ case AST_LOGIC_OR:
+ case AST_LOGIC_NOT:
+ width_hint = max(width_hint, 1);
+ sign_hint = false;
+ break;
+
+ case AST_TERNARY:
+ children.at(1)->detectSignWidthWorker(width_hint, sign_hint, found_real);
+ children.at(2)->detectSignWidthWorker(width_hint, sign_hint, found_real);
+ break;
+
+ case AST_MEMRD:
+ if (!id2ast->is_signed)
+ sign_hint = false;
+ if (!id2ast->children[0]->range_valid)
+ log_error("Failed to detect width of memory access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
+ this_width = id2ast->children[0]->range_left - id2ast->children[0]->range_right + 1;
+ width_hint = max(width_hint, this_width);
+ break;
+
+ case AST_FCALL:
+ if (str == "\\$anyconst" || str == "\\$anyseq") {
+ if (GetSize(children) == 1) {
+ while (children[0]->simplify(true, false, false, 1, -1, false, true) == true) { }
+ if (children[0]->type != AST_CONSTANT)
+ log_error("System function %s called with non-const argument at %s:%d!\n",
+ RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+ width_hint = max(width_hint, int(children[0]->asInt(true)));
+ }
+ break;
+ }
+ if (str == "\\$past") {
+ if (GetSize(children) > 0) {
+ sub_width_hint = 0;
+ sub_sign_hint = true;
+ children.at(0)->detectSignWidthWorker(sub_width_hint, sub_sign_hint);
+ width_hint = max(width_hint, sub_width_hint);
+ sign_hint = false;
+ }
+ break;
+ }
+ /* fall through */
+
+ // everything should have been handled above -> print error if not.
+ default:
+ for (auto f : log_files)
+ current_ast->dumpAst(f, "verilog-ast> ");
+ log_error("Don't know how to detect sign and width for %s node at %s:%d!\n",
+ type2str(type).c_str(), filename.c_str(), linenum);
+ }
+
+ if (*found_real)
+ sign_hint = true;
+}
+
+// detect sign and width of an expression
+void AstNode::detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real)
+{
+ width_hint = -1;
+ sign_hint = true;
+ if (found_real)
+ *found_real = false;
+ detectSignWidthWorker(width_hint, sign_hint, found_real);
+}
+
+// create RTLIL from an AST node
+// all generated cells, wires and processes are added to the module pointed to by 'current_module'
+// when the AST node is an expression (AST_ADD, AST_BIT_XOR, etc.), the result signal is returned.
+//
+// note that this function is influenced by a number of global variables that might be set when
+// called from genWidthRTLIL(). also note that this function recursively calls itself to transform
+// larger expressions into a netlist of cells.
+RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
+{
+ // in the following big switch() statement there are some uses of
+ // Clifford's Device (http://www.clifford.at/cfun/cliffdev/). In this
+ // cases this variable is used to hold the type of the cell that should
+ // be instantiated for this type of AST node.
+ std::string type_name;
+
+ current_filename = filename;
+ set_line_num(linenum);
+
+ switch (type)
+ {
+ // simply ignore this nodes.
+ // they are either leftovers from simplify() or are referenced by other nodes
+ // and are only accessed here thru this references
+ case AST_NONE:
+ case AST_TASK:
+ case AST_FUNCTION:
+ case AST_DPI_FUNCTION:
+ case AST_AUTOWIRE:
+ case AST_LOCALPARAM:
+ case AST_DEFPARAM:
+ case AST_GENVAR:
+ case AST_GENFOR:
+ case AST_GENBLOCK:
+ case AST_GENIF:
+ case AST_GENCASE:
+ case AST_PACKAGE:
+ break;
+
+ // remember the parameter, needed for example in techmap
+ case AST_PARAMETER:
+ current_module->avail_parameters.insert(str);
+ break;
+
+ // create an RTLIL::Wire for an AST_WIRE node
+ case AST_WIRE: {
+ if (current_module->wires_.count(str) != 0)
+ log_error("Re-definition of signal `%s' at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+ if (!range_valid)
+ log_error("Signal `%s' with non-constant width at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+
+ log_assert(range_left >= range_right || (range_left == -1 && range_right == 0));
+
+ RTLIL::Wire *wire = current_module->addWire(str, range_left - range_right + 1);
+ wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+ wire->start_offset = range_right;
+ wire->port_id = port_id;
+ wire->port_input = is_input;
+ wire->port_output = is_output;
+ wire->upto = range_swapped;
+
+ for (auto &attr : attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), filename.c_str(), linenum);
+ wire->attributes[attr.first] = attr.second->asAttrConst();
+ }
+ }
+ break;
+
+ // create an RTLIL::Memory for an AST_MEMORY node
+ case AST_MEMORY: {
+ if (current_module->memories.count(str) != 0)
+ log_error("Re-definition of memory `%s' at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+
+ log_assert(children.size() >= 2);
+ log_assert(children[0]->type == AST_RANGE);
+ log_assert(children[1]->type == AST_RANGE);
+
+ if (!children[0]->range_valid || !children[1]->range_valid)
+ log_error("Memory `%s' with non-constant width or size at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+
+ RTLIL::Memory *memory = new RTLIL::Memory;
+ memory->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+ memory->name = str;
+ memory->width = children[0]->range_left - children[0]->range_right + 1;
+ if (children[1]->range_right < children[1]->range_left) {
+ memory->start_offset = children[1]->range_right;
+ memory->size = children[1]->range_left - children[1]->range_right + 1;
+ } else {
+ memory->start_offset = children[1]->range_left;
+ memory->size = children[1]->range_right - children[1]->range_left + 1;
+ }
+ current_module->memories[memory->name] = memory;
+
+ for (auto &attr : attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), filename.c_str(), linenum);
+ memory->attributes[attr.first] = attr.second->asAttrConst();
+ }
+ }
+ break;
+
+ // simply return the corresponding RTLIL::SigSpec for an AST_CONSTANT node
+ case AST_CONSTANT:
+ {
+ if (width_hint < 0)
+ detectSignWidth(width_hint, sign_hint);
+
+ is_signed = sign_hint;
+ return RTLIL::SigSpec(bitsAsConst());
+ }
+
+ case AST_REALVALUE:
+ {
+ RTLIL::SigSpec sig = realAsConst(width_hint);
+ log_warning("converting real value %e to binary %s at %s:%d.\n",
+ realvalue, log_signal(sig), filename.c_str(), linenum);
+ return sig;
+ }
+
+ // simply return the corresponding RTLIL::SigSpec for an AST_IDENTIFIER node
+ // for identifiers with dynamic bit ranges (e.g. "foo[bar]" or "foo[bar+3:bar]") a
+ // shifter cell is created and the output signal of this cell is returned
+ case AST_IDENTIFIER:
+ {
+ RTLIL::Wire *wire = NULL;
+ RTLIL::SigChunk chunk;
+
+ int add_undef_bits_msb = 0;
+ int add_undef_bits_lsb = 0;
+
+ if (id2ast && id2ast->type == AST_AUTOWIRE && current_module->wires_.count(str) == 0) {
+ RTLIL::Wire *wire = current_module->addWire(str);
+ wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+ wire->name = str;
+ if (flag_autowire)
+ log_warning("Identifier `%s' is implicitly declared at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ else
+ log_error("Identifier `%s' is implicitly declared at %s:%d and `default_nettype is set to none.\n", str.c_str(), filename.c_str(), linenum);
+ }
+ else if (id2ast->type == AST_PARAMETER || id2ast->type == AST_LOCALPARAM) {
+ if (id2ast->children[0]->type != AST_CONSTANT)
+ log_error("Parameter %s does not evaluate to constant value at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+ chunk = RTLIL::Const(id2ast->children[0]->bits);
+ goto use_const_chunk;
+ }
+ else if (!id2ast || (id2ast->type != AST_WIRE && id2ast->type != AST_AUTOWIRE &&
+ id2ast->type != AST_MEMORY) || current_module->wires_.count(str) == 0)
+ log_error("Identifier `%s' doesn't map to any signal at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+
+ if (id2ast->type == AST_MEMORY)
+ log_error("Identifier `%s' does map to an unexpanded memory at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+
+ wire = current_module->wires_[str];
+ chunk.wire = wire;
+ chunk.width = wire->width;
+ chunk.offset = 0;
+
+ use_const_chunk:
+ if (children.size() != 0) {
+ log_assert(children[0]->type == AST_RANGE);
+ int source_width = id2ast->range_left - id2ast->range_right + 1;
+ int source_offset = id2ast->range_right;
+ if (!children[0]->range_valid) {
+ AstNode *left_at_zero_ast = children[0]->children[0]->clone();
+ AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : left_at_zero_ast->clone();
+ while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
+ while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
+ if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
+ log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+ int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
+ AstNode *fake_ast = new AstNode(AST_NONE, clone(), children[0]->children.size() >= 2 ?
+ children[0]->children[1]->clone() : children[0]->children[0]->clone());
+ fake_ast->children[0]->delete_children();
+ RTLIL::SigSpec shift_val = fake_ast->children[1]->genRTLIL();
+ if (id2ast->range_right != 0) {
+ shift_val = current_module->Sub(NEW_ID, shift_val, id2ast->range_right, fake_ast->children[1]->is_signed);
+ fake_ast->children[1]->is_signed = true;
+ }
+ if (id2ast->range_swapped) {
+ shift_val = current_module->Sub(NEW_ID, RTLIL::SigSpec(source_width - width), shift_val, fake_ast->children[1]->is_signed);
+ fake_ast->children[1]->is_signed = true;
+ }
+ if (GetSize(shift_val) >= 32)
+ fake_ast->children[1]->is_signed = true;
+ RTLIL::SigSpec sig = binop2rtlil(fake_ast, "$shiftx", width, fake_ast->children[0]->genRTLIL(), shift_val);
+ delete left_at_zero_ast;
+ delete right_at_zero_ast;
+ delete fake_ast;
+ return sig;
+ } else {
+ chunk.width = children[0]->range_left - children[0]->range_right + 1;
+ chunk.offset = children[0]->range_right - source_offset;
+ if (id2ast->range_swapped)
+ chunk.offset = (id2ast->range_left - id2ast->range_right + 1) - (chunk.offset + chunk.width);
+ if (chunk.offset >= source_width || chunk.offset + chunk.width < 0) {
+ if (chunk.width == 1)
+ log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting result bit to undef.\n",
+ str.c_str(), filename.c_str(), linenum);
+ else
+ log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting all %d result bits to undef.\n",
+ str.c_str(), filename.c_str(), linenum, chunk.width);
+ chunk = RTLIL::SigChunk(RTLIL::State::Sx, chunk.width);
+ } else {
+ if (chunk.width + chunk.offset > source_width) {
+ add_undef_bits_msb = (chunk.width + chunk.offset) - source_width;
+ chunk.width -= add_undef_bits_msb;
+ }
+ if (chunk.offset < 0) {
+ add_undef_bits_lsb = -chunk.offset;
+ chunk.width -= add_undef_bits_lsb;
+ chunk.offset += add_undef_bits_lsb;
+ }
+ if (add_undef_bits_lsb)
+ log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting %d LSB bits to undef.\n",
+ str.c_str(), filename.c_str(), linenum, add_undef_bits_lsb);
+ if (add_undef_bits_msb)
+ log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting %d MSB bits to undef.\n",
+ str.c_str(), filename.c_str(), linenum, add_undef_bits_msb);
+ }
+ }
+ }
+
+ RTLIL::SigSpec sig = { RTLIL::SigSpec(RTLIL::State::Sx, add_undef_bits_msb), chunk, RTLIL::SigSpec(RTLIL::State::Sx, add_undef_bits_lsb) };
+
+ if (genRTLIL_subst_ptr)
+ sig.replace(*genRTLIL_subst_ptr);
+
+ is_signed = children.size() > 0 ? false : id2ast->is_signed && sign_hint;
+ return sig;
+ }
+
+ // just pass thru the signal. the parent will evaluate the is_signed property and interpret the SigSpec accordingly
+ case AST_TO_SIGNED:
+ case AST_TO_UNSIGNED: {
+ RTLIL::SigSpec sig = children[0]->genRTLIL();
+ if (sig.size() < width_hint)
+ sig.extend_u0(width_hint, sign_hint);
+ is_signed = sign_hint;
+ return sig;
+ }
+
+ // concatenation of signals can be done directly using RTLIL::SigSpec
+ case AST_CONCAT: {
+ RTLIL::SigSpec sig;
+ for (auto it = children.begin(); it != children.end(); it++)
+ sig.append((*it)->genRTLIL());
+ if (sig.size() < width_hint)
+ sig.extend_u0(width_hint, false);
+ return sig;
+ }
+
+ // replication of signals can be done directly using RTLIL::SigSpec
+ case AST_REPLICATE: {
+ RTLIL::SigSpec left = children[0]->genRTLIL();
+ RTLIL::SigSpec right = children[1]->genRTLIL();
+ if (!left.is_fully_const())
+ log_error("Left operand of replicate expression is not constant at %s:%d!\n", filename.c_str(), linenum);
+ int count = left.as_int();
+ RTLIL::SigSpec sig;
+ for (int i = 0; i < count; i++)
+ sig.append(right);
+ if (sig.size() < width_hint)
+ sig.extend_u0(width_hint, false);
+ is_signed = false;
+ return sig;
+ }
+
+ // generate cells for unary operations: $not, $pos, $neg
+ if (0) { case AST_BIT_NOT: type_name = "$not"; }
+ if (0) { case AST_POS: type_name = "$pos"; }
+ if (0) { case AST_NEG: type_name = "$neg"; }
+ {
+ RTLIL::SigSpec arg = children[0]->genRTLIL(width_hint, sign_hint);
+ is_signed = children[0]->is_signed;
+ int width = arg.size();
+ if (width_hint > 0) {
+ width = width_hint;
+ widthExtend(this, arg, width, is_signed);
+ }
+ return uniop2rtlil(this, type_name, width, arg);
+ }
+
+ // generate cells for binary operations: $and, $or, $xor, $xnor
+ if (0) { case AST_BIT_AND: type_name = "$and"; }
+ if (0) { case AST_BIT_OR: type_name = "$or"; }
+ if (0) { case AST_BIT_XOR: type_name = "$xor"; }
+ if (0) { case AST_BIT_XNOR: type_name = "$xnor"; }
+ {
+ if (width_hint < 0)
+ detectSignWidth(width_hint, sign_hint);
+ RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint);
+ RTLIL::SigSpec right = children[1]->genRTLIL(width_hint, sign_hint);
+ int width = max(left.size(), right.size());
+ if (width_hint > 0)
+ width = width_hint;
+ is_signed = children[0]->is_signed && children[1]->is_signed;
+ return binop2rtlil(this, type_name, width, left, right);
+ }
+
+ // generate cells for unary operations: $reduce_and, $reduce_or, $reduce_xor, $reduce_xnor
+ if (0) { case AST_REDUCE_AND: type_name = "$reduce_and"; }
+ if (0) { case AST_REDUCE_OR: type_name = "$reduce_or"; }
+ if (0) { case AST_REDUCE_XOR: type_name = "$reduce_xor"; }
+ if (0) { case AST_REDUCE_XNOR: type_name = "$reduce_xnor"; }
+ {
+ RTLIL::SigSpec arg = children[0]->genRTLIL();
+ RTLIL::SigSpec sig = uniop2rtlil(this, type_name, max(width_hint, 1), arg);
+ return sig;
+ }
+
+ // generate cells for unary operations: $reduce_bool
+ // (this is actually just an $reduce_or, but for clarity a different cell type is used)
+ if (0) { case AST_REDUCE_BOOL: type_name = "$reduce_bool"; }
+ {
+ RTLIL::SigSpec arg = children[0]->genRTLIL();
+ RTLIL::SigSpec sig = arg.size() > 1 ? uniop2rtlil(this, type_name, max(width_hint, 1), arg) : arg;
+ return sig;
+ }
+
+ // generate cells for binary operations: $shl, $shr, $sshl, $sshr
+ if (0) { case AST_SHIFT_LEFT: type_name = "$shl"; }
+ if (0) { case AST_SHIFT_RIGHT: type_name = "$shr"; }
+ if (0) { case AST_SHIFT_SLEFT: type_name = "$sshl"; }
+ if (0) { case AST_SHIFT_SRIGHT: type_name = "$sshr"; }
+ {
+ if (width_hint < 0)
+ detectSignWidth(width_hint, sign_hint);
+ RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint);
+ RTLIL::SigSpec right = children[1]->genRTLIL();
+ int width = width_hint > 0 ? width_hint : left.size();
+ is_signed = children[0]->is_signed;
+ return binop2rtlil(this, type_name, width, left, right);
+ }
+
+ // generate cells for binary operations: $pow
+ case AST_POW:
+ {
+ int right_width;
+ bool right_signed;
+ children[1]->detectSignWidth(right_width, right_signed);
+ if (width_hint < 0)
+ detectSignWidth(width_hint, sign_hint);
+ RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint);
+ RTLIL::SigSpec right = children[1]->genRTLIL(right_width, right_signed);
+ int width = width_hint > 0 ? width_hint : left.size();
+ is_signed = children[0]->is_signed;
+ if (!flag_noopt && left.is_fully_const() && left.as_int() == 2 && !right_signed)
+ return binop2rtlil(this, "$shl", width, RTLIL::SigSpec(1, left.size()), right);
+ return binop2rtlil(this, "$pow", width, left, right);
+ }
+
+ // generate cells for binary operations: $lt, $le, $eq, $ne, $ge, $gt
+ if (0) { case AST_LT: type_name = "$lt"; }
+ if (0) { case AST_LE: type_name = "$le"; }
+ if (0) { case AST_EQ: type_name = "$eq"; }
+ if (0) { case AST_NE: type_name = "$ne"; }
+ if (0) { case AST_EQX: type_name = "$eqx"; }
+ if (0) { case AST_NEX: type_name = "$nex"; }
+ if (0) { case AST_GE: type_name = "$ge"; }
+ if (0) { case AST_GT: type_name = "$gt"; }
+ {
+ int width = max(width_hint, 1);
+ width_hint = -1, sign_hint = true;
+ children[0]->detectSignWidthWorker(width_hint, sign_hint);
+ children[1]->detectSignWidthWorker(width_hint, sign_hint);
+ RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint);
+ RTLIL::SigSpec right = children[1]->genRTLIL(width_hint, sign_hint);
+ RTLIL::SigSpec sig = binop2rtlil(this, type_name, width, left, right);
+ return sig;
+ }
+
+ // generate cells for binary operations: $add, $sub, $mul, $div, $mod
+ if (0) { case AST_ADD: type_name = "$add"; }
+ if (0) { case AST_SUB: type_name = "$sub"; }
+ if (0) { case AST_MUL: type_name = "$mul"; }
+ if (0) { case AST_DIV: type_name = "$div"; }
+ if (0) { case AST_MOD: type_name = "$mod"; }
+ {
+ if (width_hint < 0)
+ detectSignWidth(width_hint, sign_hint);
+ RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint);
+ RTLIL::SigSpec right = children[1]->genRTLIL(width_hint, sign_hint);
+ #if 0
+ int width = max(left.size(), right.size());
+ if (width > width_hint && width_hint > 0)
+ width = width_hint;
+ if (width < width_hint) {
+ if (type == AST_ADD || type == AST_SUB || type == AST_DIV)
+ width++;
+ if (type == AST_SUB && (!children[0]->is_signed || !children[1]->is_signed))
+ width = width_hint;
+ if (type == AST_MUL)
+ width = min(left.size() + right.size(), width_hint);
+ }
+ #else
+ int width = max(max(left.size(), right.size()), width_hint);
+ #endif
+ is_signed = children[0]->is_signed && children[1]->is_signed;
+ return binop2rtlil(this, type_name, width, left, right);
+ }
+
+ // generate cells for binary operations: $logic_and, $logic_or
+ if (0) { case AST_LOGIC_AND: type_name = "$logic_and"; }
+ if (0) { case AST_LOGIC_OR: type_name = "$logic_or"; }
+ {
+ RTLIL::SigSpec left = children[0]->genRTLIL();
+ RTLIL::SigSpec right = children[1]->genRTLIL();
+ return binop2rtlil(this, type_name, max(width_hint, 1), left, right);
+ }
+
+ // generate cells for unary operations: $logic_not
+ case AST_LOGIC_NOT:
+ {
+ RTLIL::SigSpec arg = children[0]->genRTLIL();
+ return uniop2rtlil(this, "$logic_not", max(width_hint, 1), arg);
+ }
+
+ // generate multiplexer for ternary operator (aka ?:-operator)
+ case AST_TERNARY:
+ {
+ if (width_hint < 0)
+ detectSignWidth(width_hint, sign_hint);
+
+ RTLIL::SigSpec cond = children[0]->genRTLIL();
+ RTLIL::SigSpec val1 = children[1]->genRTLIL(width_hint, sign_hint);
+ RTLIL::SigSpec val2 = children[2]->genRTLIL(width_hint, sign_hint);
+
+ if (cond.size() > 1)
+ cond = uniop2rtlil(this, "$reduce_bool", 1, cond, false);
+
+ int width = max(val1.size(), val2.size());
+ is_signed = children[1]->is_signed && children[2]->is_signed;
+ widthExtend(this, val1, width, is_signed);
+ widthExtend(this, val2, width, is_signed);
+
+ RTLIL::SigSpec sig = mux2rtlil(this, cond, val1, val2);
+
+ if (sig.size() < width_hint)
+ sig.extend_u0(width_hint, sign_hint);
+ return sig;
+ }
+
+ // generate $memrd cells for memory read ports
+ case AST_MEMRD:
+ {
+ std::stringstream sstr;
+ sstr << "$memrd$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++);
+
+ RTLIL::Cell *cell = current_module->addCell(sstr.str(), "$memrd");
+ cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+
+ RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_DATA", current_module->memories[str]->width);
+ wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+
+ int mem_width, mem_size, addr_bits;
+ is_signed = id2ast->is_signed;
+ id2ast->meminfo(mem_width, mem_size, addr_bits);
+
+ RTLIL::SigSpec addr_sig = children[0]->genRTLIL();
+
+ cell->setPort("\\CLK", RTLIL::SigSpec(RTLIL::State::Sx, 1));
+ cell->setPort("\\EN", RTLIL::SigSpec(RTLIL::State::Sx, 1));
+ cell->setPort("\\ADDR", addr_sig);
+ cell->setPort("\\DATA", RTLIL::SigSpec(wire));
+
+ cell->parameters["\\MEMID"] = RTLIL::Const(str);
+ cell->parameters["\\ABITS"] = RTLIL::Const(GetSize(addr_sig));
+ cell->parameters["\\WIDTH"] = RTLIL::Const(wire->width);
+
+ cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(0);
+ cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(0);
+ cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
+
+ return RTLIL::SigSpec(wire);
+ }
+
+ // generate $memwr cells for memory write ports
+ case AST_MEMWR:
+ case AST_MEMINIT:
+ {
+ std::stringstream sstr;
+ sstr << (type == AST_MEMWR ? "$memwr$" : "$meminit$") << str << "$" << filename << ":" << linenum << "$" << (autoidx++);
+
+ RTLIL::Cell *cell = current_module->addCell(sstr.str(), type == AST_MEMWR ? "$memwr" : "$meminit");
+ cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+
+ int mem_width, mem_size, addr_bits;
+ id2ast->meminfo(mem_width, mem_size, addr_bits);
+
+ int num_words = 1;
+ if (type == AST_MEMINIT) {
+ if (children[2]->type != AST_CONSTANT)
+ log_error("Memory init with non-constant word count at %s:%d!\n", filename.c_str(), linenum);
+ num_words = int(children[2]->asInt(false));
+ cell->parameters["\\WORDS"] = RTLIL::Const(num_words);
+ }
+
+ SigSpec addr_sig = children[0]->genRTLIL();
+
+ cell->setPort("\\ADDR", addr_sig);
+ cell->setPort("\\DATA", children[1]->genWidthRTLIL(current_module->memories[str]->width * num_words));
+
+ cell->parameters["\\MEMID"] = RTLIL::Const(str);
+ cell->parameters["\\ABITS"] = RTLIL::Const(GetSize(addr_sig));
+ cell->parameters["\\WIDTH"] = RTLIL::Const(current_module->memories[str]->width);
+
+ if (type == AST_MEMWR) {
+ cell->setPort("\\CLK", RTLIL::SigSpec(RTLIL::State::Sx, 1));
+ cell->setPort("\\EN", children[2]->genRTLIL());
+ cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(0);
+ cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(0);
+ }
+
+ cell->parameters["\\PRIORITY"] = RTLIL::Const(autoidx-1);
+ }
+ break;
+
+ // generate $assert cells
+ case AST_ASSERT:
+ case AST_ASSUME:
+ {
+ const char *celltype = "$assert";
+ if (type == AST_ASSUME) celltype = "$assume";
+
+ log_assert(children.size() == 2);
+
+ RTLIL::SigSpec check = children[0]->genRTLIL();
+ if (GetSize(check) != 1)
+ check = current_module->ReduceBool(NEW_ID, check);
+
+ RTLIL::SigSpec en = children[1]->genRTLIL();
+ if (GetSize(en) != 1)
+ en = current_module->ReduceBool(NEW_ID, en);
+
+ std::stringstream sstr;
+ sstr << celltype << "$" << filename << ":" << linenum << "$" << (autoidx++);
+
+ RTLIL::Cell *cell = current_module->addCell(sstr.str(), celltype);
+ cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+
+ for (auto &attr : attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), filename.c_str(), linenum);
+ cell->attributes[attr.first] = attr.second->asAttrConst();
+ }
+
+ cell->setPort("\\A", check);
+ cell->setPort("\\EN", en);
+ }
+ break;
+
+ // add entries to current_module->connections for assignments (outside of always blocks)
+ case AST_ASSIGN:
+ {
+ RTLIL::SigSpec left = children[0]->genRTLIL();
+ RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.size());
+ if (left.has_const()) {
+ RTLIL::SigSpec new_left, new_right;
+ for (int i = 0; i < GetSize(left); i++)
+ if (left[i].wire) {
+ new_left.append(left[i]);
+ new_right.append(right[i]);
+ }
+ log_warning("Ignoring assignment to constant bits at %s:%d:\n"
+ " old assignment: %s = %s\n new assignment: %s = %s.\n",
+ filename.c_str(), linenum, log_signal(left), log_signal(right),
+ log_signal(new_left), log_signal(new_right));
+ left = new_left;
+ right = new_right;
+ }
+ current_module->connect(RTLIL::SigSig(left, right));
+ }
+ break;
+
+ // create an RTLIL::Cell for an AST_CELL
+ case AST_CELL:
+ {
+ int port_counter = 0, para_counter = 0;
+
+ if (current_module->count_id(str) != 0)
+ log_error("Re-definition of cell `%s' at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+
+ RTLIL::Cell *cell = current_module->addCell(str, "");
+ cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+
+ for (auto it = children.begin(); it != children.end(); it++) {
+ AstNode *child = *it;
+ if (child->type == AST_CELLTYPE) {
+ cell->type = child->str;
+ if (flag_icells && cell->type.substr(0, 2) == "\\$")
+ cell->type = cell->type.substr(1);
+ continue;
+ }
+ if (child->type == AST_PARASET) {
+ IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str;
+ if (child->children[0]->type == AST_REALVALUE) {
+ log_warning("Replacing floating point parameter %s.%s = %f with string at %s:%d.\n",
+ log_id(cell), log_id(paraname), child->children[0]->realvalue,
+ filename.c_str(), linenum);
+ auto strnode = AstNode::mkconst_str(stringf("%f", child->children[0]->realvalue));
+ strnode->cloneInto(child->children[0]);
+ delete strnode;
+ }
+ if (child->children[0]->type != AST_CONSTANT)
+ log_error("Parameter %s.%s with non-constant value at %s:%d!\n",
+ log_id(cell), log_id(paraname), filename.c_str(), linenum);
+ cell->parameters[paraname] = child->children[0]->asParaConst();
+ continue;
+ }
+ if (child->type == AST_ARGUMENT) {
+ RTLIL::SigSpec sig;
+ if (child->children.size() > 0)
+ sig = child->children[0]->genRTLIL();
+ if (child->str.size() == 0) {
+ char buf[100];
+ snprintf(buf, 100, "$%d", ++port_counter);
+ cell->setPort(buf, sig);
+ } else {
+ cell->setPort(child->str, sig);
+ }
+ continue;
+ }
+ log_abort();
+ }
+ for (auto &attr : attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_error("Attribute `%s' with non-constant value at %s:%d!\n",
+ attr.first.c_str(), filename.c_str(), linenum);
+ cell->attributes[attr.first] = attr.second->asAttrConst();
+ }
+ }
+ break;
+
+ // use ProcessGenerator for always blocks
+ case AST_ALWAYS: {
+ AstNode *always = this->clone();
+ ProcessGenerator generator(always);
+ ignoreThisSignalsInInitial.append(generator.outputSignals);
+ delete always;
+ } break;
+
+ case AST_INITIAL: {
+ AstNode *always = this->clone();
+ ProcessGenerator generator(always, ignoreThisSignalsInInitial);
+ delete always;
+ } break;
+
+ case AST_FCALL: {
+ if (str == "\\$anyconst" || str == "\\$anyseq")
+ {
+ string myid = stringf("%s$%d", str.c_str() + 1, autoidx++);
+ int width = width_hint;
+
+ if (GetSize(children) > 1)
+ log_error("System function %s got %d arguments, expected 1 or 0 at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), GetSize(children), filename.c_str(), linenum);
+
+ if (GetSize(children) == 1) {
+ if (children[0]->type != AST_CONSTANT)
+ log_error("System function %s called with non-const argument at %s:%d!\n",
+ RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+ width = children[0]->asInt(true);
+ }
+
+ if (width <= 0)
+ log_error("Failed to detect width of %s at %s:%d!\n",
+ RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+
+ Cell *cell = current_module->addCell(myid, str.substr(1));
+ cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+ cell->parameters["\\WIDTH"] = width;
+
+ Wire *wire = current_module->addWire(myid + "_wire", width);
+ wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+ cell->setPort("\\Y", wire);
+
+ is_signed = sign_hint;
+ return SigSpec(wire);
+ }
+ } /* fall through */
+
+ // everything should have been handled above -> print error if not.
+ default:
+ for (auto f : log_files)
+ current_ast->dumpAst(f, "verilog-ast> ");
+ type_name = type2str(type);
+ log_error("Don't know how to generate RTLIL code for %s node at %s:%d!\n",
+ type_name.c_str(), filename.c_str(), linenum);
+ }
+
+ return RTLIL::SigSpec();
+}
+
+// this is a wrapper for AstNode::genRTLIL() when a specific signal width is requested and/or
+// signals must be substituted before being used as input values (used by ProcessGenerator)
+// note that this is using some global variables to communicate this special settings to AstNode::genRTLIL().
+RTLIL::SigSpec AstNode::genWidthRTLIL(int width, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr)
+{
+ const dict<RTLIL::SigBit, RTLIL::SigBit> *backup_subst_ptr = genRTLIL_subst_ptr;
+
+ if (new_subst_ptr)
+ genRTLIL_subst_ptr = new_subst_ptr;
+
+ bool sign_hint = true;
+ int width_hint = width;
+ detectSignWidthWorker(width_hint, sign_hint);
+ RTLIL::SigSpec sig = genRTLIL(width_hint, sign_hint);
+
+ genRTLIL_subst_ptr = backup_subst_ptr;
+
+ if (width >= 0)
+ sig.extend_u0(width, is_signed);
+
+ return sig;
+}
+
+YOSYS_NAMESPACE_END
+
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
new file mode 100644
index 00000000..9d5c75fe
--- /dev/null
+++ b/frontends/ast/simplify.cc
@@ -0,0 +1,3341 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * ---
+ *
+ * This is the AST frontend library.
+ *
+ * The AST frontend library is not a frontend on it's own but provides a
+ * generic abstract syntax tree (AST) abstraction for HDL code and can be
+ * used by HDL frontends. See "ast.h" for an overview of the API and the
+ * Verilog frontend for an usage example.
+ *
+ */
+
+#include "kernel/log.h"
+#include "libs/sha1/sha1.h"
+#include "frontends/verilog/verilog_frontend.h"
+#include "ast.h"
+
+#include <sstream>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <math.h>
+
+YOSYS_NAMESPACE_BEGIN
+
+using namespace AST;
+using namespace AST_INTERNAL;
+
+// convert the AST into a simpler AST that has all parameters substituted by their
+// values, unrolled for-loops, expanded generate blocks, etc. when this function
+// is done with an AST it can be converted into RTLIL using genRTLIL().
+//
+// this function also does all name resolving and sets the id2ast member of all
+// nodes that link to a different node using names and lexical scoping.
+bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param)
+{
+ static int recursion_counter = 0;
+ static pair<string, int> last_blocking_assignment_warn;
+ static bool deep_recursion_warning = false;
+
+ if (recursion_counter++ == 1000 && deep_recursion_warning) {
+ log_warning("Deep recursion in AST simplifier.\nDoes this design contain insanely long expressions?\n");
+ deep_recursion_warning = false;
+ }
+
+ AstNode *newNode = NULL;
+ bool did_something = false;
+
+#if 0
+ log("-------------\n");
+ log("AST simplify[%d] depth %d at %s:%d on %s %p:\n", stage, recursion_counter, filename.c_str(), linenum, type2str(type).c_str(), this);
+ log("const_fold=%d, at_zero=%d, in_lvalue=%d, stage=%d, width_hint=%d, sign_hint=%d, in_param=%d\n",
+ int(const_fold), int(at_zero), int(in_lvalue), int(stage), int(width_hint), int(sign_hint), int(in_param));
+ // dumpAst(NULL, "> ");
+#endif
+
+ if (stage == 0)
+ {
+ log_assert(type == AST_MODULE);
+ last_blocking_assignment_warn = pair<string, int>();
+
+ deep_recursion_warning = true;
+ while (simplify(const_fold, at_zero, in_lvalue, 1, width_hint, sign_hint, in_param)) { }
+
+ if (!flag_nomem2reg && !get_bool_attribute("\\nomem2reg"))
+ {
+ dict<AstNode*, pool<std::string>> mem2reg_places;
+ dict<AstNode*, uint32_t> mem2reg_candidates, dummy_proc_flags;
+ uint32_t flags = flag_mem2reg ? AstNode::MEM2REG_FL_ALL : 0;
+ mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, dummy_proc_flags, flags);
+
+ pool<AstNode*> mem2reg_set;
+ for (auto &it : mem2reg_candidates)
+ {
+ AstNode *mem = it.first;
+ uint32_t memflags = it.second;
+ bool this_nomeminit = flag_nomeminit;
+ log_assert((memflags & ~0x00ffff00) == 0);
+
+ if (mem->get_bool_attribute("\\nomem2reg"))
+ continue;
+
+ if (mem->get_bool_attribute("\\nomeminit") || get_bool_attribute("\\nomeminit"))
+ this_nomeminit = true;
+
+ if (memflags & AstNode::MEM2REG_FL_FORCED)
+ goto silent_activate;
+
+ if (memflags & AstNode::MEM2REG_FL_EQ2)
+ goto verbose_activate;
+
+ if (memflags & AstNode::MEM2REG_FL_SET_ASYNC)
+ goto verbose_activate;
+
+ if ((memflags & AstNode::MEM2REG_FL_SET_INIT) && (memflags & AstNode::MEM2REG_FL_SET_ELSE) && this_nomeminit)
+ goto verbose_activate;
+
+ if (memflags & AstNode::MEM2REG_FL_CMPLX_LHS)
+ goto verbose_activate;
+
+ // log("Note: Not replacing memory %s with list of registers (flags=0x%08lx).\n", mem->str.c_str(), long(memflags));
+ continue;
+
+ verbose_activate:
+ if (mem2reg_set.count(mem) == 0) {
+ std::string message = stringf("Replacing memory %s with list of registers.", mem->str.c_str());
+ bool first_element = true;
+ for (auto &place : mem2reg_places[it.first]) {
+ message += stringf("%s%s", first_element ? " See " : ", ", place.c_str());
+ first_element = false;
+ }
+ log_warning("%s\n", message.c_str());
+ }
+
+ silent_activate:
+ // log("Note: Replacing memory %s with list of registers (flags=0x%08lx).\n", mem->str.c_str(), long(memflags));
+ mem2reg_set.insert(mem);
+ }
+
+ for (auto node : mem2reg_set)
+ {
+ int mem_width, mem_size, addr_bits;
+ node->meminfo(mem_width, mem_size, addr_bits);
+
+ 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)));
+ reg->str = stringf("%s[%d]", node->str.c_str(), i);
+ reg->is_reg = true;
+ reg->is_signed = node->is_signed;
+ children.push_back(reg);
+ while (reg->simplify(true, false, false, 1, -1, false, false)) { }
+ }
+ }
+
+ AstNode *async_block = NULL;
+ while (mem2reg_as_needed_pass2(mem2reg_set, this, NULL, async_block)) { }
+
+ vector<AstNode*> delnodes;
+ mem2reg_remove(mem2reg_set, delnodes);
+
+ for (auto node : delnodes)
+ delete node;
+ }
+
+ while (simplify(const_fold, at_zero, in_lvalue, 2, width_hint, sign_hint, in_param)) { }
+ recursion_counter--;
+ return false;
+ }
+
+ current_filename = filename;
+ set_line_num(linenum);
+
+ // we do not look inside a task or function
+ // (but as soon as a task or function is instantiated we process the generated AST as usual)
+ if (type == AST_FUNCTION || type == AST_TASK) {
+ recursion_counter--;
+ return false;
+ }
+
+ // deactivate all calls to non-synthesis system tasks
+ // note that $display, $finish, and $stop are used for synthesis-time DRC so they're not in this list
+ if ((type == AST_FCALL || type == AST_TCALL) && (str == "$strobe" || str == "$monitor" || str == "$time" ||
+ str == "$dumpfile" || str == "$dumpvars" || str == "$dumpon" || str == "$dumpoff" || str == "$dumpall")) {
+ log_warning("Ignoring call to system %s %s at %s:%d.\n", type == AST_FCALL ? "function" : "task", str.c_str(), filename.c_str(), linenum);
+ delete_children();
+ str = std::string();
+ }
+
+ if ((type == AST_TCALL) && (str == "$display" || str == "$write") && (!current_always || current_always->type != AST_INITIAL)) {
+ log_warning("System task `%s' outside initial block is unsupported at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ delete_children();
+ str = std::string();
+ }
+
+ // print messages if this a call to $display() or $write()
+ // This code implements only a small subset of Verilog-2005 $display() format specifiers,
+ // but should be good enough for most uses
+ if ((type == AST_TCALL) && ((str == "$display") || (str == "$write")))
+ {
+ int nargs = GetSize(children);
+ if (nargs < 1)
+ log_error("System task `%s' got %d arguments, expected >= 1 at %s:%d.\n",
+ str.c_str(), int(children.size()), filename.c_str(), linenum);
+
+ // First argument is the format string
+ AstNode *node_string = children[0];
+ while (node_string->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ if (node_string->type != AST_CONSTANT)
+ log_error("Failed to evaluate system task `%s' with non-constant 1st argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ std::string sformat = node_string->bitsAsConst().decode_string();
+
+ // Other arguments are placeholders. Process the string as we go through it
+ std::string sout;
+ int next_arg = 1;
+ for (size_t i = 0; i < sformat.length(); i++)
+ {
+ // format specifier
+ if (sformat[i] == '%')
+ {
+ // If there's no next character, that's a problem
+ if (i+1 >= sformat.length())
+ log_error("System task `%s' called with `%%' at end of string at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+
+ char cformat = sformat[++i];
+
+ // %% is special, does not need a matching argument
+ if (cformat == '%')
+ {
+ sout += '%';
+ continue;
+ }
+
+ // Simplify the argument
+ AstNode *node_arg = nullptr;
+
+ // Everything from here on depends on the format specifier
+ switch (cformat)
+ {
+ case 's':
+ case 'S':
+ case 'd':
+ case 'D':
+ case 'x':
+ case 'X':
+ if (next_arg >= GetSize(children))
+ log_error("Missing argument for %%%c format specifier in system task `%s' at %s:%d.\n",
+ cformat, str.c_str(), filename.c_str(), linenum);
+
+ node_arg = children[next_arg++];
+ while (node_arg->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ if (node_arg->type != AST_CONSTANT)
+ log_error("Failed to evaluate system task `%s' with non-constant argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ break;
+
+ case 'm':
+ case 'M':
+ break;
+
+ default:
+ log_error("System task `%s' called with invalid/unsupported format specifier at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ break;
+ }
+
+ switch (cformat)
+ {
+ case 's':
+ case 'S':
+ sout += node_arg->bitsAsConst().decode_string();
+ break;
+
+ case 'd':
+ case 'D':
+ {
+ char tmp[128];
+ snprintf(tmp, sizeof(tmp), "%d", node_arg->bitsAsConst().as_int());
+ sout += tmp;
+ }
+ break;
+
+ case 'x':
+ case 'X':
+ {
+ char tmp[128];
+ snprintf(tmp, sizeof(tmp), "%x", node_arg->bitsAsConst().as_int());
+ sout += tmp;
+ }
+ break;
+
+ case 'm':
+ case 'M':
+ sout += log_id(current_module->name);
+ break;
+
+ default:
+ log_abort();
+ }
+ }
+
+ // not a format specifier
+ else
+ sout += sformat[i];
+ }
+
+ // Finally, print the message (only include a \n for $display, not for $write)
+ log("%s", sout.c_str());
+ if (str == "$display")
+ log("\n");
+ delete_children();
+ str = std::string();
+ }
+
+ // activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.)
+ if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX)
+ const_fold = true;
+ if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM))
+ const_fold = true;
+
+ // in certain cases a function must be evaluated constant. this is what in_param controls.
+ if (type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_PREFIX)
+ in_param = true;
+
+ std::map<std::string, AstNode*> backup_scope;
+
+ // create name resolution entries for all objects with names
+ // also merge multiple declarations for the same wire (e.g. "output foobar; reg foobar;")
+ if (type == AST_MODULE) {
+ current_scope.clear();
+ std::map<std::string, AstNode*> this_wire_scope;
+ for (size_t i = 0; i < children.size(); i++) {
+ AstNode *node = children[i];
+ if (node->type == AST_WIRE) {
+ if (this_wire_scope.count(node->str) > 0) {
+ AstNode *first_node = this_wire_scope[node->str];
+ if (!node->is_input && !node->is_output && node->is_reg && node->children.size() == 0)
+ goto wires_are_compatible;
+ if (first_node->children.size() == 0 && node->children.size() == 1 && node->children[0]->type == AST_RANGE) {
+ AstNode *r = node->children[0];
+ if (r->range_valid && r->range_left == 0 && r->range_right == 0) {
+ delete r;
+ node->children.pop_back();
+ }
+ }
+ if (first_node->children.size() != node->children.size())
+ goto wires_are_incompatible;
+ for (size_t j = 0; j < node->children.size(); j++) {
+ AstNode *n1 = first_node->children[j], *n2 = node->children[j];
+ if (n1->type == AST_RANGE && n2->type == AST_RANGE && n1->range_valid && n2->range_valid) {
+ if (n1->range_left != n2->range_left)
+ goto wires_are_incompatible;
+ if (n1->range_right != n2->range_right)
+ goto wires_are_incompatible;
+ } else if (*n1 != *n2)
+ goto wires_are_incompatible;
+ }
+ if (first_node->range_left != node->range_left)
+ goto wires_are_incompatible;
+ if (first_node->range_right != node->range_right)
+ goto wires_are_incompatible;
+ if (first_node->port_id == 0 && (node->is_input || node->is_output))
+ goto wires_are_incompatible;
+ wires_are_compatible:
+ if (node->is_input)
+ first_node->is_input = true;
+ if (node->is_output)
+ first_node->is_output = true;
+ if (node->is_reg)
+ first_node->is_reg = true;
+ if (node->is_signed)
+ first_node->is_signed = true;
+ for (auto &it : node->attributes) {
+ if (first_node->attributes.count(it.first) > 0)
+ delete first_node->attributes[it.first];
+ first_node->attributes[it.first] = it.second->clone();
+ }
+ children.erase(children.begin()+(i--));
+ did_something = true;
+ delete node;
+ continue;
+ wires_are_incompatible:
+ if (stage > 1)
+ log_error("Incompatible re-declaration of wire %s at %s:%d.\n", node->str.c_str(), filename.c_str(), linenum);
+ continue;
+ }
+ this_wire_scope[node->str] = node;
+ }
+ if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR ||
+ node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION || node->type == AST_CELL) {
+ backup_scope[node->str] = current_scope[node->str];
+ current_scope[node->str] = node;
+ }
+ }
+ for (size_t i = 0; i < children.size(); i++) {
+ AstNode *node = children[i];
+ if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE)
+ while (node->simplify(true, false, false, 1, -1, false, node->type == AST_PARAMETER || node->type == AST_LOCALPARAM))
+ did_something = true;
+ }
+ }
+
+ auto backup_current_block = current_block;
+ auto backup_current_block_child = current_block_child;
+ auto backup_current_top_block = current_top_block;
+ auto backup_current_always = current_always;
+ auto backup_current_always_clocked = current_always_clocked;
+
+ if (type == AST_ALWAYS || type == AST_INITIAL)
+ {
+ current_always = this;
+ current_always_clocked = false;
+
+ if (type == AST_ALWAYS)
+ for (auto child : children)
+ if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE)
+ current_always_clocked = true;
+ }
+
+ int backup_width_hint = width_hint;
+ bool backup_sign_hint = sign_hint;
+
+ bool detect_width_simple = false;
+ bool child_0_is_self_determined = false;
+ bool child_1_is_self_determined = false;
+ bool child_2_is_self_determined = false;
+ bool children_are_self_determined = false;
+ bool reset_width_after_children = false;
+
+ switch (type)
+ {
+ case AST_ASSIGN_EQ:
+ case AST_ASSIGN_LE:
+ case AST_ASSIGN:
+ while (!children[0]->basic_prep && children[0]->simplify(false, false, true, stage, -1, false, in_param) == true)
+ did_something = true;
+ while (!children[1]->basic_prep && children[1]->simplify(false, false, false, stage, -1, false, in_param) == true)
+ did_something = true;
+ children[0]->detectSignWidth(backup_width_hint, backup_sign_hint);
+ children[1]->detectSignWidth(width_hint, sign_hint);
+ width_hint = max(width_hint, backup_width_hint);
+ child_0_is_self_determined = true;
+ break;
+
+ case AST_PARAMETER:
+ case AST_LOCALPARAM:
+ while (!children[0]->basic_prep && children[0]->simplify(false, false, false, stage, -1, false, true) == true)
+ did_something = true;
+ children[0]->detectSignWidth(width_hint, sign_hint);
+ if (children.size() > 1 && children[1]->type == AST_RANGE) {
+ while (!children[1]->basic_prep && children[1]->simplify(false, false, false, stage, -1, false, true) == true)
+ did_something = true;
+ if (!children[1]->range_valid)
+ log_error("Non-constant width range on parameter decl at %s:%d.\n", filename.c_str(), linenum);
+ width_hint = max(width_hint, children[1]->range_left - children[1]->range_right + 1);
+ }
+ break;
+
+ case AST_TO_BITS:
+ case AST_TO_SIGNED:
+ case AST_TO_UNSIGNED:
+ case AST_CONCAT:
+ case AST_REPLICATE:
+ case AST_REDUCE_AND:
+ case AST_REDUCE_OR:
+ case AST_REDUCE_XOR:
+ case AST_REDUCE_XNOR:
+ case AST_REDUCE_BOOL:
+ detect_width_simple = true;
+ children_are_self_determined = true;
+ break;
+
+ case AST_NEG:
+ case AST_BIT_NOT:
+ case AST_POS:
+ case AST_BIT_AND:
+ case AST_BIT_OR:
+ case AST_BIT_XOR:
+ case AST_BIT_XNOR:
+ case AST_ADD:
+ case AST_SUB:
+ case AST_MUL:
+ case AST_DIV:
+ case AST_MOD:
+ detect_width_simple = true;
+ break;
+
+ case AST_SHIFT_LEFT:
+ case AST_SHIFT_RIGHT:
+ case AST_SHIFT_SLEFT:
+ case AST_SHIFT_SRIGHT:
+ case AST_POW:
+ detect_width_simple = true;
+ child_1_is_self_determined = true;
+ break;
+
+ case AST_LT:
+ case AST_LE:
+ case AST_EQ:
+ case AST_NE:
+ case AST_EQX:
+ case AST_NEX:
+ case AST_GE:
+ case AST_GT:
+ width_hint = -1;
+ sign_hint = true;
+ for (auto child : children) {
+ while (!child->basic_prep && child->simplify(false, false, in_lvalue, stage, -1, false, in_param) == true)
+ did_something = true;
+ child->detectSignWidthWorker(width_hint, sign_hint);
+ }
+ reset_width_after_children = true;
+ break;
+
+ case AST_LOGIC_AND:
+ case AST_LOGIC_OR:
+ case AST_LOGIC_NOT:
+ detect_width_simple = true;
+ children_are_self_determined = true;
+ break;
+
+ case AST_TERNARY:
+ detect_width_simple = true;
+ child_0_is_self_determined = true;
+ break;
+
+ case AST_MEMRD:
+ detect_width_simple = true;
+ children_are_self_determined = true;
+ break;
+
+ case AST_FCALL:
+ case AST_TCALL:
+ children_are_self_determined = true;
+ break;
+
+ default:
+ width_hint = -1;
+ sign_hint = false;
+ }
+
+ if (detect_width_simple && width_hint < 0) {
+ if (type == AST_REPLICATE)
+ while (children[0]->simplify(true, false, in_lvalue, stage, -1, false, true) == true)
+ did_something = true;
+ for (auto child : children)
+ while (!child->basic_prep && child->simplify(false, false, in_lvalue, stage, -1, false, in_param) == true)
+ did_something = true;
+ detectSignWidth(width_hint, sign_hint);
+ }
+
+ if (type == AST_FCALL && str == "\\$past")
+ detectSignWidth(width_hint, sign_hint);
+
+ if (type == AST_TERNARY) {
+ int width_hint_left, width_hint_right;
+ bool sign_hint_left, sign_hint_right;
+ bool found_real_left, found_real_right;
+ children[1]->detectSignWidth(width_hint_left, sign_hint_left, &found_real_left);
+ children[2]->detectSignWidth(width_hint_right, sign_hint_right, &found_real_right);
+ if (found_real_left || found_real_right) {
+ child_1_is_self_determined = true;
+ child_2_is_self_determined = true;
+ }
+ }
+
+ if (type == AST_CONDX && children.size() > 0 && children.at(0)->type == AST_CONSTANT) {
+ for (auto &bit : children.at(0)->bits)
+ if (bit == State::Sz || bit == State::Sx)
+ bit = State::Sa;
+ }
+
+ if (type == AST_CONDZ && children.size() > 0 && children.at(0)->type == AST_CONSTANT) {
+ for (auto &bit : children.at(0)->bits)
+ if (bit == State::Sz)
+ bit = State::Sa;
+ }
+
+ if (const_fold && type == AST_CASE)
+ {
+ while (children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { }
+ if (children[0]->type == AST_CONSTANT && children[0]->bits_only_01()) {
+ std::vector<AstNode*> new_children;
+ new_children.push_back(children[0]);
+ for (int i = 1; i < GetSize(children); i++) {
+ AstNode *child = children[i];
+ log_assert(child->type == AST_COND || child->type == AST_CONDX || child->type == AST_CONDZ);
+ for (auto v : child->children) {
+ if (v->type == AST_DEFAULT)
+ goto keep_const_cond;
+ if (v->type == AST_BLOCK)
+ continue;
+ while (v->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { }
+ if (v->type == AST_CONSTANT && v->bits_only_01()) {
+ if (v->bits == children[0]->bits) {
+ while (i+1 < GetSize(children))
+ delete children[++i];
+ goto keep_const_cond;
+ }
+ continue;
+ }
+ goto keep_const_cond;
+ }
+ if (0)
+ keep_const_cond:
+ new_children.push_back(child);
+ else
+ delete child;
+ }
+ new_children.swap(children);
+ }
+ }
+
+ // simplify all children first
+ // (iterate by index as e.g. auto wires can add new children in the process)
+ for (size_t i = 0; i < children.size(); i++) {
+ bool did_something_here = true;
+ if ((type == AST_GENFOR || type == AST_FOR) && i >= 3)
+ break;
+ if ((type == AST_GENIF || type == AST_GENCASE) && i >= 1)
+ break;
+ if (type == AST_GENBLOCK)
+ break;
+ if (type == AST_BLOCK && !str.empty())
+ break;
+ if (type == AST_PREFIX && i >= 1)
+ break;
+ while (did_something_here && i < children.size()) {
+ bool const_fold_here = const_fold, in_lvalue_here = in_lvalue;
+ int width_hint_here = width_hint;
+ bool sign_hint_here = sign_hint;
+ bool in_param_here = in_param;
+ if (i == 0 && (type == AST_REPLICATE || type == AST_WIRE))
+ const_fold_here = true, in_param_here = true;
+ if (type == AST_PARAMETER || type == AST_LOCALPARAM)
+ const_fold_here = true;
+ if (i == 0 && (type == AST_ASSIGN || type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE))
+ in_lvalue_here = true;
+ if (type == AST_BLOCK) {
+ current_block = this;
+ current_block_child = children[i];
+ }
+ if ((type == AST_ALWAYS || type == AST_INITIAL) && children[i]->type == AST_BLOCK)
+ current_top_block = children[i];
+ if (i == 0 && child_0_is_self_determined)
+ width_hint_here = -1, sign_hint_here = false;
+ if (i == 1 && child_1_is_self_determined)
+ width_hint_here = -1, sign_hint_here = false;
+ if (i == 2 && child_2_is_self_determined)
+ width_hint_here = -1, sign_hint_here = false;
+ if (children_are_self_determined)
+ width_hint_here = -1, sign_hint_here = false;
+ did_something_here = children[i]->simplify(const_fold_here, at_zero, in_lvalue_here, stage, width_hint_here, sign_hint_here, in_param_here);
+ if (did_something_here)
+ did_something = true;
+ }
+ if (stage == 2 && children[i]->type == AST_INITIAL && current_ast_mod != this) {
+ current_ast_mod->children.push_back(children[i]);
+ children.erase(children.begin() + (i--));
+ did_something = true;
+ }
+ }
+ for (auto &attr : attributes) {
+ while (attr.second->simplify(true, false, false, stage, -1, false, true))
+ did_something = true;
+ }
+
+ if (reset_width_after_children) {
+ width_hint = backup_width_hint;
+ sign_hint = backup_sign_hint;
+ if (width_hint < 0)
+ detectSignWidth(width_hint, sign_hint);
+ }
+
+ current_block = backup_current_block;
+ current_block_child = backup_current_block_child;
+ current_top_block = backup_current_top_block;
+ current_always = backup_current_always;
+ current_always_clocked = backup_current_always_clocked;
+
+ for (auto it = backup_scope.begin(); it != backup_scope.end(); it++) {
+ if (it->second == NULL)
+ current_scope.erase(it->first);
+ else
+ current_scope[it->first] = it->second;
+ }
+
+ current_filename = filename;
+ set_line_num(linenum);
+
+ if (type == AST_MODULE)
+ current_scope.clear();
+
+ // convert defparam nodes to cell parameters
+ if (type == AST_DEFPARAM && !str.empty()) {
+ size_t pos = str.rfind('.');
+ if (pos == std::string::npos)
+ log_error("Defparam `%s' does not contain a dot (module/parameter separator) at %s:%d!\n",
+ RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+ std::string modname = str.substr(0, pos), paraname = "\\" + str.substr(pos+1);
+ if (current_scope.count(modname) == 0 || current_scope.at(modname)->type != AST_CELL)
+ log_error("Can't find cell for defparam `%s . %s` at %s:%d!\n", RTLIL::unescape_id(modname).c_str(), RTLIL::unescape_id(paraname).c_str(), filename.c_str(), linenum);
+ AstNode *cell = current_scope.at(modname), *paraset = clone();
+ cell->children.insert(cell->children.begin() + 1, paraset);
+ paraset->type = AST_PARASET;
+ paraset->str = paraname;
+ str.clear();
+ }
+
+ // resolve constant prefixes
+ if (type == AST_PREFIX) {
+ if (children[0]->type != AST_CONSTANT) {
+ // dumpAst(NULL, "> ");
+ log_error("Index in generate block prefix syntax at %s:%d is not constant!\n", filename.c_str(), linenum);
+ }
+ if (children[1]->type == AST_PREFIX)
+ children[1]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param);
+ log_assert(children[1]->type == AST_IDENTIFIER);
+ newNode = children[1]->clone();
+ const char *second_part = children[1]->str.c_str();
+ if (second_part[0] == '\\')
+ second_part++;
+ newNode->str = stringf("%s[%d].%s", str.c_str(), children[0]->integer, second_part);
+ goto apply_newNode;
+ }
+
+ // evaluate TO_BITS nodes
+ if (type == AST_TO_BITS) {
+ if (children[0]->type != AST_CONSTANT)
+ log_error("Left operand of to_bits expression is not constant at %s:%d!\n", filename.c_str(), linenum);
+ if (children[1]->type != AST_CONSTANT)
+ log_error("Right operand of to_bits expression is not constant at %s:%d!\n", filename.c_str(), linenum);
+ RTLIL::Const new_value = children[1]->bitsAsConst(children[0]->bitsAsConst().as_int(), children[1]->is_signed);
+ newNode = mkconst_bits(new_value.bits, children[1]->is_signed);
+ goto apply_newNode;
+ }
+
+ // annotate constant ranges
+ if (type == AST_RANGE) {
+ bool old_range_valid = range_valid;
+ range_valid = false;
+ range_swapped = false;
+ range_left = -1;
+ range_right = 0;
+ log_assert(children.size() >= 1);
+ if (children[0]->type == AST_CONSTANT) {
+ range_valid = true;
+ range_left = children[0]->integer;
+ if (children.size() == 1)
+ range_right = range_left;
+ }
+ if (children.size() >= 2) {
+ if (children[1]->type == AST_CONSTANT)
+ range_right = children[1]->integer;
+ else
+ range_valid = false;
+ }
+ if (old_range_valid != range_valid)
+ did_something = true;
+ if (range_valid && range_left >= 0 && range_right > range_left) {
+ int tmp = range_right;
+ range_right = range_left;
+ range_left = tmp;
+ range_swapped = true;
+ }
+ }
+
+ // annotate wires with their ranges
+ if (type == AST_WIRE) {
+ if (children.size() > 0) {
+ if (children[0]->range_valid) {
+ if (!range_valid)
+ did_something = true;
+ range_valid = true;
+ range_swapped = children[0]->range_swapped;
+ range_left = children[0]->range_left;
+ range_right = children[0]->range_right;
+ }
+ } else {
+ if (!range_valid)
+ did_something = true;
+ range_valid = true;
+ range_swapped = false;
+ range_left = 0;
+ range_right = 0;
+ }
+ }
+
+ // resolve multiranges on memory decl
+ if (type == AST_MEMORY && children.size() > 1 && children[1]->type == AST_MULTIRANGE)
+ {
+ int total_size = 1;
+ multirange_dimensions.clear();
+ for (auto range : children[1]->children) {
+ if (!range->range_valid)
+ log_error("Non-constant range on memory decl at %s:%d.\n", filename.c_str(), linenum);
+ multirange_dimensions.push_back(min(range->range_left, range->range_right));
+ multirange_dimensions.push_back(max(range->range_left, range->range_right) - min(range->range_left, range->range_right) + 1);
+ total_size *= multirange_dimensions.back();
+ }
+ delete children[1];
+ children[1] = new AstNode(AST_RANGE, AstNode::mkconst_int(0, true), AstNode::mkconst_int(total_size-1, true));
+ did_something = true;
+ }
+
+ // resolve multiranges on memory access
+ if (type == AST_IDENTIFIER && id2ast && id2ast->type == AST_MEMORY && children.size() > 0 && children[0]->type == AST_MULTIRANGE)
+ {
+ AstNode *index_expr = nullptr;
+
+ for (int i = 0; 2*i < GetSize(id2ast->multirange_dimensions); i++)
+ {
+ if (GetSize(children[0]->children) < i)
+ log_error("Insufficient number of array indices for %s at %s:%d.\n", log_id(str), filename.c_str(), linenum);
+
+ AstNode *new_index_expr = children[0]->children[i]->children.at(0)->clone();
+
+ if (id2ast->multirange_dimensions[2*i])
+ new_index_expr = new AstNode(AST_SUB, new_index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i], true));
+
+ if (i == 0)
+ index_expr = new_index_expr;
+ else
+ index_expr = new AstNode(AST_ADD, new AstNode(AST_MUL, index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i+1], true)), new_index_expr);
+ }
+
+ for (int i = GetSize(id2ast->multirange_dimensions)/2; i < GetSize(children[0]->children); i++)
+ children.push_back(children[0]->children[i]->clone());
+
+ delete children[0];
+ if (index_expr == nullptr)
+ children.erase(children.begin());
+ else
+ children[0] = new AstNode(AST_RANGE, index_expr);
+
+ did_something = true;
+ }
+
+ // trim/extend parameters
+ if (type == AST_PARAMETER || type == AST_LOCALPARAM) {
+ if (children.size() > 1 && children[1]->type == AST_RANGE) {
+ if (!children[1]->range_valid)
+ log_error("Non-constant width range on parameter decl at %s:%d.\n", filename.c_str(), linenum);
+ int width = std::abs(children[1]->range_left - children[1]->range_right) + 1;
+ if (children[0]->type == AST_REALVALUE) {
+ RTLIL::Const constvalue = children[0]->realAsConst(width);
+ log_warning("converting real value %e to binary %s at %s:%d.\n",
+ children[0]->realvalue, log_signal(constvalue), filename.c_str(), linenum);
+ delete children[0];
+ children[0] = mkconst_bits(constvalue.bits, sign_hint);
+ did_something = true;
+ }
+ if (children[0]->type == AST_CONSTANT) {
+ if (width != int(children[0]->bits.size())) {
+ RTLIL::SigSpec sig(children[0]->bits);
+ sig.extend_u0(width, children[0]->is_signed);
+ AstNode *old_child_0 = children[0];
+ children[0] = mkconst_bits(sig.as_const().bits, is_signed);
+ delete old_child_0;
+ }
+ children[0]->is_signed = is_signed;
+ }
+ range_valid = true;
+ range_swapped = children[1]->range_swapped;
+ range_left = children[1]->range_left;
+ range_right = children[1]->range_right;
+ } else
+ if (children.size() > 1 && children[1]->type == AST_REALVALUE && children[0]->type == AST_CONSTANT) {
+ double as_realvalue = children[0]->asReal(sign_hint);
+ delete children[0];
+ children[0] = new AstNode(AST_REALVALUE);
+ children[0]->realvalue = as_realvalue;
+ did_something = true;
+ }
+ }
+
+ // annotate identifiers using scope resolution and create auto-wires as needed
+ if (type == AST_IDENTIFIER) {
+ if (current_scope.count(str) == 0) {
+ for (auto node : current_ast_mod->children) {
+ if ((node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR ||
+ node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION) && str == node->str) {
+ current_scope[node->str] = node;
+ break;
+ }
+ }
+ }
+ 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 (id2ast != current_scope[str]) {
+ id2ast = current_scope[str];
+ did_something = true;
+ }
+ }
+
+ // split memory access with bit select to individual statements
+ if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE && !in_lvalue)
+ {
+ if (id2ast == NULL || id2ast->type != AST_MEMORY || children[0]->children.size() != 1)
+ log_error("Invalid bit-select on memory access at %s:%d!\n", filename.c_str(), linenum);
+
+ int mem_width, mem_size, addr_bits;
+ id2ast->meminfo(mem_width, mem_size, addr_bits);
+
+ int data_range_left = id2ast->children[0]->range_left;
+ int data_range_right = id2ast->children[0]->range_right;
+
+ std::stringstream sstr;
+ sstr << "$mem2bits$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++);
+ std::string wire_id = sstr.str();
+
+ AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(data_range_left, true), mkconst_int(data_range_right, true)));
+ wire->str = wire_id;
+ if (current_block)
+ wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
+ current_ast_mod->children.push_back(wire);
+ while (wire->simplify(true, false, false, 1, -1, false, false)) { }
+
+ AstNode *data = clone();
+ delete data->children[1];
+ data->children.pop_back();
+
+ AstNode *assign = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), data);
+ assign->children[0]->str = wire_id;
+
+ if (current_block)
+ {
+ size_t assign_idx = 0;
+ while (assign_idx < current_block->children.size() && current_block->children[assign_idx] != current_block_child)
+ assign_idx++;
+ log_assert(assign_idx < current_block->children.size());
+ current_block->children.insert(current_block->children.begin()+assign_idx, assign);
+ wire->is_reg = true;
+ }
+ else
+ {
+ AstNode *proc = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK));
+ proc->children[0]->children.push_back(assign);
+ current_ast_mod->children.push_back(proc);
+ }
+
+ newNode = new AstNode(AST_IDENTIFIER, children[1]->clone());
+ newNode->str = wire_id;
+ newNode->id2ast = wire;
+ goto apply_newNode;
+ }
+
+ if (type == AST_WHILE)
+ log_error("While loops are only allowed in constant functions at %s:%d!\n", filename.c_str(), linenum);
+
+ if (type == AST_REPEAT)
+ log_error("Repeat loops are only allowed in constant functions at %s:%d!\n", filename.c_str(), linenum);
+
+ // unroll for loops and generate-for blocks
+ if ((type == AST_GENFOR || type == AST_FOR) && children.size() != 0)
+ {
+ AstNode *init_ast = children[0];
+ AstNode *while_ast = children[1];
+ AstNode *next_ast = children[2];
+ AstNode *body_ast = children[3];
+
+ while (body_ast->type == AST_GENBLOCK && body_ast->str.empty() &&
+ body_ast->children.size() == 1 && body_ast->children.at(0)->type == AST_GENBLOCK)
+ body_ast = body_ast->children.at(0);
+
+ if (init_ast->type != AST_ASSIGN_EQ)
+ log_error("Unsupported 1st expression of generate for-loop at %s:%d!\n", filename.c_str(), linenum);
+ if (next_ast->type != AST_ASSIGN_EQ)
+ log_error("Unsupported 3rd expression of generate for-loop at %s:%d!\n", filename.c_str(), linenum);
+
+ if (type == AST_GENFOR) {
+ if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != AST_GENVAR)
+ log_error("Left hand side of 1st expression of generate for-loop at %s:%d is not a gen var!\n", filename.c_str(), linenum);
+ if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != AST_GENVAR)
+ log_error("Left hand side of 3rd expression of generate for-loop at %s:%d is not a gen var!\n", filename.c_str(), linenum);
+ } else {
+ if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != AST_WIRE)
+ log_error("Left hand side of 1st expression of generate for-loop at %s:%d is not a register!\n", filename.c_str(), linenum);
+ if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != AST_WIRE)
+ log_error("Left hand side of 3rd expression of generate for-loop at %s:%d is not a register!\n", filename.c_str(), linenum);
+ }
+
+ if (init_ast->children[0]->id2ast != next_ast->children[0]->id2ast)
+ log_error("Incompatible left-hand sides in 1st and 3rd expression of generate for-loop at %s:%d!\n", filename.c_str(), linenum);
+
+ // eval 1st expression
+ AstNode *varbuf = init_ast->children[1]->clone();
+ while (varbuf->simplify(true, false, false, stage, 32, true, false)) { }
+
+ if (varbuf->type != AST_CONSTANT)
+ log_error("Right hand side of 1st expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum);
+
+ varbuf = new AstNode(AST_LOCALPARAM, varbuf);
+ varbuf->str = init_ast->children[0]->str;
+
+ AstNode *backup_scope_varbuf = current_scope[varbuf->str];
+ current_scope[varbuf->str] = varbuf;
+
+ size_t current_block_idx = 0;
+ if (type == AST_FOR) {
+ while (current_block_idx < current_block->children.size() &&
+ current_block->children[current_block_idx] != current_block_child)
+ current_block_idx++;
+ }
+
+ while (1)
+ {
+ // eval 2nd expression
+ AstNode *buf = while_ast->clone();
+ while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+
+ if (buf->type != AST_CONSTANT)
+ log_error("2nd expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum);
+
+ if (buf->integer == 0) {
+ delete buf;
+ break;
+ }
+ delete buf;
+
+ // expand body
+ int index = varbuf->children[0]->integer;
+ if (body_ast->type == AST_GENBLOCK)
+ buf = body_ast->clone();
+ else
+ buf = new AstNode(AST_GENBLOCK, body_ast->clone());
+ if (buf->str.empty()) {
+ std::stringstream sstr;
+ sstr << "$genblock$" << filename << ":" << linenum << "$" << (autoidx++);
+ buf->str = sstr.str();
+ }
+ std::map<std::string, std::string> name_map;
+ std::stringstream sstr;
+ sstr << buf->str << "[" << index << "].";
+ buf->expand_genblock(varbuf->str, sstr.str(), name_map);
+
+ if (type == AST_GENFOR) {
+ for (size_t i = 0; i < buf->children.size(); i++) {
+ buf->children[i]->simplify(false, false, false, stage, -1, false, false);
+ current_ast_mod->children.push_back(buf->children[i]);
+ }
+ } else {
+ for (size_t i = 0; i < buf->children.size(); i++)
+ current_block->children.insert(current_block->children.begin() + current_block_idx++, buf->children[i]);
+ }
+ buf->children.clear();
+ delete buf;
+
+ // eval 3rd expression
+ buf = next_ast->children[1]->clone();
+ while (buf->simplify(true, false, false, stage, 32, true, false)) { }
+
+ if (buf->type != AST_CONSTANT)
+ log_error("Right hand side of 3rd expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum);
+
+ delete varbuf->children[0];
+ varbuf->children[0] = buf;
+ }
+
+ current_scope[varbuf->str] = backup_scope_varbuf;
+ delete varbuf;
+ delete_children();
+ did_something = true;
+ }
+
+ // transform block with name
+ if (type == AST_BLOCK && !str.empty())
+ {
+ std::map<std::string, std::string> name_map;
+ expand_genblock(std::string(), str + ".", name_map);
+
+ std::vector<AstNode*> new_children;
+ for (size_t i = 0; i < children.size(); i++)
+ if (children[i]->type == AST_WIRE || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM) {
+ children[i]->simplify(false, false, false, stage, -1, false, false);
+ current_ast_mod->children.push_back(children[i]);
+ current_scope[children[i]->str] = children[i];
+ } else
+ new_children.push_back(children[i]);
+
+ children.swap(new_children);
+ did_something = true;
+ str.clear();
+ }
+
+ // simplify unconditional generate block
+ if (type == AST_GENBLOCK && children.size() != 0)
+ {
+ if (!str.empty()) {
+ std::map<std::string, std::string> name_map;
+ expand_genblock(std::string(), str + ".", name_map);
+ }
+
+ for (size_t i = 0; i < children.size(); i++) {
+ children[i]->simplify(false, false, false, stage, -1, false, false);
+ current_ast_mod->children.push_back(children[i]);
+ }
+
+ children.clear();
+ did_something = true;
+ }
+
+ // simplify generate-if blocks
+ if (type == AST_GENIF && children.size() != 0)
+ {
+ AstNode *buf = children[0]->clone();
+ while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ if (buf->type != AST_CONSTANT) {
+ // for (auto f : log_files)
+ // dumpAst(f, "verilog-ast> ");
+ log_error("Condition for generate if at %s:%d is not constant!\n", filename.c_str(), linenum);
+ }
+ if (buf->asBool() != 0) {
+ delete buf;
+ buf = children[1]->clone();
+ } else {
+ delete buf;
+ buf = children.size() > 2 ? children[2]->clone() : NULL;
+ }
+
+ if (buf)
+ {
+ if (buf->type != AST_GENBLOCK)
+ buf = new AstNode(AST_GENBLOCK, buf);
+
+ if (!buf->str.empty()) {
+ std::map<std::string, std::string> name_map;
+ buf->expand_genblock(std::string(), buf->str + ".", name_map);
+ }
+
+ for (size_t i = 0; i < buf->children.size(); i++) {
+ buf->children[i]->simplify(false, false, false, stage, -1, false, false);
+ current_ast_mod->children.push_back(buf->children[i]);
+ }
+
+ buf->children.clear();
+ delete buf;
+ }
+
+ delete_children();
+ did_something = true;
+ }
+
+ // simplify generate-case blocks
+ if (type == AST_GENCASE && children.size() != 0)
+ {
+ AstNode *buf = children[0]->clone();
+ while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ if (buf->type != AST_CONSTANT) {
+ // for (auto f : log_files)
+ // dumpAst(f, "verilog-ast> ");
+ log_error("Condition for generate case at %s:%d is not constant!\n", filename.c_str(), linenum);
+ }
+
+ bool ref_signed = buf->is_signed;
+ RTLIL::Const ref_value = buf->bitsAsConst();
+ delete buf;
+
+ AstNode *selected_case = NULL;
+ for (size_t i = 1; i < children.size(); i++)
+ {
+ log_assert(children.at(i)->type == AST_COND || children.at(i)->type == AST_CONDX || children.at(i)->type == AST_CONDZ);
+
+ AstNode *this_genblock = NULL;
+ for (auto child : children.at(i)->children) {
+ log_assert(this_genblock == NULL);
+ if (child->type == AST_GENBLOCK)
+ this_genblock = child;
+ }
+
+ for (auto child : children.at(i)->children)
+ {
+ if (child->type == AST_DEFAULT) {
+ if (selected_case == NULL)
+ selected_case = this_genblock;
+ continue;
+ }
+ if (child->type == AST_GENBLOCK)
+ continue;
+
+ buf = child->clone();
+ while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ if (buf->type != AST_CONSTANT) {
+ // for (auto f : log_files)
+ // dumpAst(f, "verilog-ast> ");
+ log_error("Expression in generate case at %s:%d is not constant!\n", filename.c_str(), linenum);
+ }
+
+ bool is_selected = RTLIL::const_eq(ref_value, buf->bitsAsConst(), ref_signed && buf->is_signed, ref_signed && buf->is_signed, 1).as_bool();
+ delete buf;
+
+ if (is_selected) {
+ selected_case = this_genblock;
+ i = children.size();
+ break;
+ }
+ }
+ }
+
+ if (selected_case != NULL)
+ {
+ log_assert(selected_case->type == AST_GENBLOCK);
+ buf = selected_case->clone();
+
+ if (!buf->str.empty()) {
+ std::map<std::string, std::string> name_map;
+ buf->expand_genblock(std::string(), buf->str + ".", name_map);
+ }
+
+ for (size_t i = 0; i < buf->children.size(); i++) {
+ buf->children[i]->simplify(false, false, false, stage, -1, false, false);
+ current_ast_mod->children.push_back(buf->children[i]);
+ }
+
+ buf->children.clear();
+ delete buf;
+ }
+
+ delete_children();
+ did_something = true;
+ }
+
+ // unroll cell arrays
+ if (type == AST_CELLARRAY)
+ {
+ if (!children.at(0)->range_valid)
+ log_error("Non-constant array range on cell array at %s:%d.\n", filename.c_str(), linenum);
+
+ newNode = new AstNode(AST_GENBLOCK);
+ int num = max(children.at(0)->range_left, children.at(0)->range_right) - min(children.at(0)->range_left, children.at(0)->range_right) + 1;
+
+ for (int i = 0; i < num; i++) {
+ int idx = children.at(0)->range_left > children.at(0)->range_right ? children.at(0)->range_right + i : children.at(0)->range_right - i;
+ AstNode *new_cell = children.at(1)->clone();
+ newNode->children.push_back(new_cell);
+ new_cell->str += stringf("[%d]", idx);
+ if (new_cell->type == AST_PRIMITIVE) {
+ log_error("Cell arrays of primitives are currently not supported at %s:%d.\n", filename.c_str(), linenum);
+ } else {
+ log_assert(new_cell->children.at(0)->type == AST_CELLTYPE);
+ new_cell->children.at(0)->str = stringf("$array:%d:%d:%s", i, num, new_cell->children.at(0)->str.c_str());
+ }
+ }
+
+ goto apply_newNode;
+ }
+
+ // replace primitives with assignments
+ if (type == AST_PRIMITIVE)
+ {
+ if (children.size() < 2)
+ log_error("Insufficient number of arguments for primitive `%s' at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+
+ std::vector<AstNode*> children_list;
+ for (auto child : children) {
+ log_assert(child->type == AST_ARGUMENT);
+ log_assert(child->children.size() == 1);
+ children_list.push_back(child->children[0]);
+ child->children.clear();
+ delete child;
+ }
+ children.clear();
+
+ if (str == "bufif0" || str == "bufif1" || str == "notif0" || str == "notif1")
+ {
+ if (children_list.size() != 3)
+ log_error("Invalid number of arguments for primitive `%s' at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+
+ std::vector<RTLIL::State> z_const(1, RTLIL::State::Sz);
+
+ AstNode *mux_input = children_list.at(1);
+ if (str == "notif0" || str == "notif1") {
+ mux_input = new AstNode(AST_BIT_NOT, mux_input);
+ }
+ AstNode *node = new AstNode(AST_TERNARY, children_list.at(2));
+ if (str == "bufif0") {
+ node->children.push_back(AstNode::mkconst_bits(z_const, false));
+ node->children.push_back(mux_input);
+ } else {
+ node->children.push_back(mux_input);
+ node->children.push_back(AstNode::mkconst_bits(z_const, false));
+ }
+
+ str.clear();
+ type = AST_ASSIGN;
+ children.push_back(children_list.at(0));
+ children.push_back(node);
+ did_something = true;
+ }
+ else
+ {
+ AstNodeType op_type = AST_NONE;
+ bool invert_results = false;
+
+ if (str == "and")
+ op_type = AST_BIT_AND;
+ if (str == "nand")
+ op_type = AST_BIT_AND, invert_results = true;
+ if (str == "or")
+ op_type = AST_BIT_OR;
+ if (str == "nor")
+ op_type = AST_BIT_OR, invert_results = true;
+ if (str == "xor")
+ op_type = AST_BIT_XOR;
+ if (str == "xnor")
+ op_type = AST_BIT_XOR, invert_results = true;
+ if (str == "buf")
+ op_type = AST_POS;
+ if (str == "not")
+ op_type = AST_POS, invert_results = true;
+ log_assert(op_type != AST_NONE);
+
+ AstNode *node = children_list[1];
+ if (op_type != AST_POS)
+ for (size_t i = 2; i < children_list.size(); i++)
+ node = new AstNode(op_type, node, children_list[i]);
+ if (invert_results)
+ node = new AstNode(AST_BIT_NOT, node);
+
+ str.clear();
+ type = AST_ASSIGN;
+ children.push_back(children_list[0]);
+ children.push_back(node);
+ did_something = true;
+ }
+ }
+
+ // replace dynamic ranges in left-hand side expressions (e.g. "foo[bar] <= 1'b1;") with
+ // a big case block that selects the correct single-bit assignment.
+ if (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) {
+ if (children[0]->type != AST_IDENTIFIER || children[0]->children.size() == 0)
+ goto skip_dynamic_range_lvalue_expansion;
+ if (children[0]->children[0]->range_valid || did_something)
+ goto skip_dynamic_range_lvalue_expansion;
+ if (children[0]->id2ast == NULL || children[0]->id2ast->type != AST_WIRE)
+ goto skip_dynamic_range_lvalue_expansion;
+ if (!children[0]->id2ast->range_valid)
+ goto skip_dynamic_range_lvalue_expansion;
+ int source_width = children[0]->id2ast->range_left - children[0]->id2ast->range_right + 1;
+ int result_width = 1;
+ AstNode *shift_expr = NULL;
+ AstNode *range = children[0]->children[0];
+ if (range->children.size() == 1) {
+ shift_expr = range->children[0]->clone();
+ } else {
+ shift_expr = range->children[1]->clone();
+ AstNode *left_at_zero_ast = range->children[0]->clone();
+ AstNode *right_at_zero_ast = range->children[1]->clone();
+ 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_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n",
+ str.c_str(), filename.c_str(), linenum);
+ result_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
+ }
+ did_something = true;
+ newNode = new AstNode(AST_CASE, shift_expr);
+ for (int i = 0; i <= source_width-result_width; i++) {
+ int start_bit = children[0]->id2ast->range_right + i;
+ AstNode *cond = new AstNode(AST_COND, mkconst_int(start_bit, true));
+ AstNode *lvalue = children[0]->clone();
+ lvalue->delete_children();
+ lvalue->children.push_back(new AstNode(AST_RANGE,
+ mkconst_int(start_bit+result_width-1, true), mkconst_int(start_bit, true)));
+ cond->children.push_back(new AstNode(AST_BLOCK, new AstNode(type, lvalue, children[1]->clone())));
+ newNode->children.push_back(cond);
+ }
+ goto apply_newNode;
+ }
+skip_dynamic_range_lvalue_expansion:;
+
+ if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME) && current_block != NULL)
+ {
+ std::stringstream sstr;
+ sstr << "$formal$" << filename << ":" << linenum << "$" << (autoidx++);
+ std::string id_check = sstr.str() + "_CHECK", id_en = sstr.str() + "_EN";
+
+ AstNode *wire_check = new AstNode(AST_WIRE);
+ wire_check->str = id_check;
+ current_ast_mod->children.push_back(wire_check);
+ current_scope[wire_check->str] = wire_check;
+ while (wire_check->simplify(true, false, false, 1, -1, false, false)) { }
+
+ AstNode *wire_en = new AstNode(AST_WIRE);
+ wire_en->str = id_en;
+ current_ast_mod->children.push_back(wire_en);
+ if (current_always_clocked) {
+ current_ast_mod->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(0, false, 1)))));
+ current_ast_mod->children.back()->children[0]->children[0]->children[0]->str = id_en;
+ }
+ current_scope[wire_en->str] = wire_en;
+ while (wire_en->simplify(true, false, false, 1, -1, false, false)) { }
+
+ std::vector<RTLIL::State> x_bit;
+ x_bit.push_back(RTLIL::State::Sx);
+
+ AstNode *assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bit, false));
+ assign_check->children[0]->str = id_check;
+
+ AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, 1));
+ assign_en->children[0]->str = id_en;
+
+ AstNode *default_signals = new AstNode(AST_BLOCK);
+ default_signals->children.push_back(assign_check);
+ default_signals->children.push_back(assign_en);
+ current_top_block->children.insert(current_top_block->children.begin(), default_signals);
+
+ assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_REDUCE_BOOL, children[0]->clone()));
+ assign_check->children[0]->str = id_check;
+
+ if (current_always == nullptr || current_always->type != AST_INITIAL) {
+ assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(1, false, 1));
+ } else {
+ assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_FCALL));
+ assign_en->children[1]->str = "\\$initstate";
+ }
+ assign_en->children[0]->str = id_en;
+
+ newNode = new AstNode(AST_BLOCK);
+ newNode->children.push_back(assign_check);
+ newNode->children.push_back(assign_en);
+
+ AstNode *assertnode = new AstNode(type);
+ assertnode->children.push_back(new AstNode(AST_IDENTIFIER));
+ assertnode->children.push_back(new AstNode(AST_IDENTIFIER));
+ assertnode->children[0]->str = id_check;
+ assertnode->children[1]->str = id_en;
+ assertnode->attributes.swap(attributes);
+ current_ast_mod->children.push_back(assertnode);
+
+ goto apply_newNode;
+ }
+
+ if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME) && children.size() == 1)
+ {
+ children.push_back(mkconst_int(1, false, 1));
+ did_something = true;
+ }
+
+ // found right-hand side identifier for memory -> replace with memory read port
+ if (stage > 1 && type == AST_IDENTIFIER && id2ast != NULL && id2ast->type == AST_MEMORY && !in_lvalue &&
+ children.size() == 1 && children[0]->type == AST_RANGE && children[0]->children.size() == 1) {
+ newNode = new AstNode(AST_MEMRD, children[0]->children[0]->clone());
+ newNode->str = str;
+ newNode->id2ast = id2ast;
+ goto apply_newNode;
+ }
+
+ // assignment with nontrivial member in left-hand concat expression -> split assignment
+ if ((type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) && children[0]->type == AST_CONCAT && width_hint > 0)
+ {
+ bool found_nontrivial_member = false;
+
+ for (auto child : children[0]->children) {
+ if (child->type == AST_IDENTIFIER && child->id2ast != NULL && child->id2ast->type == AST_MEMORY)
+ found_nontrivial_member = true;
+ }
+
+ if (found_nontrivial_member)
+ {
+ newNode = new AstNode(AST_BLOCK);
+
+ AstNode *wire_tmp = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(width_hint-1, true), mkconst_int(0, true)));
+ wire_tmp->str = stringf("$splitcmplxassign$%s:%d$%d", filename.c_str(), linenum, autoidx++);
+ current_ast_mod->children.push_back(wire_tmp);
+ current_scope[wire_tmp->str] = wire_tmp;
+ wire_tmp->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
+ while (wire_tmp->simplify(true, false, false, 1, -1, false, false)) { }
+
+ AstNode *wire_tmp_id = new AstNode(AST_IDENTIFIER);
+ wire_tmp_id->str = wire_tmp->str;
+
+ newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, wire_tmp_id, children[1]->clone()));
+
+ int cursor = 0;
+ for (auto child : children[0]->children)
+ {
+ int child_width_hint = -1;
+ bool child_sign_hint = true;
+ child->detectSignWidth(child_width_hint, child_sign_hint);
+
+ AstNode *rhs = wire_tmp_id->clone();
+ rhs->children.push_back(new AstNode(AST_RANGE, AstNode::mkconst_int(cursor+child_width_hint-1, true), AstNode::mkconst_int(cursor, true)));
+ newNode->children.push_back(new AstNode(type, child->clone(), rhs));
+
+ cursor += child_width_hint;
+ }
+
+ goto apply_newNode;
+ }
+ }
+
+ // assignment with memory in left-hand side expression -> replace with memory write port
+ if (stage > 1 && (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) && children[0]->type == AST_IDENTIFIER &&
+ children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY && children[0]->id2ast->children.size() >= 2 &&
+ children[0]->id2ast->children[0]->range_valid && children[0]->id2ast->children[1]->range_valid &&
+ (children[0]->children.size() == 1 || children[0]->children.size() == 2) && children[0]->children[0]->type == AST_RANGE)
+ {
+ std::stringstream sstr;
+ 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);
+
+ int data_range_left = children[0]->id2ast->children[0]->range_left;
+ int data_range_right = children[0]->id2ast->children[0]->range_right;
+ int mem_data_range_offset = std::min(data_range_left, data_range_right);
+
+ int addr_width_hint = -1;
+ bool addr_sign_hint = true;
+ children[0]->children[0]->children[0]->detectSignWidthWorker(addr_width_hint, addr_sign_hint);
+ addr_bits = std::max(addr_bits, addr_width_hint);
+
+ AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true)));
+ wire_addr->str = id_addr;
+ current_ast_mod->children.push_back(wire_addr);
+ current_scope[wire_addr->str] = wire_addr;
+ while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { }
+
+ AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
+ wire_data->str = id_data;
+ wire_data->is_signed = mem_signed;
+ current_ast_mod->children.push_back(wire_data);
+ current_scope[wire_data->str] = wire_data;
+ while (wire_data->simplify(true, false, false, 1, -1, false, false)) { }
+
+ AstNode *wire_en = nullptr;
+ if (current_always->type != AST_INITIAL) {
+ wire_en = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
+ wire_en->str = id_en;
+ current_ast_mod->children.push_back(wire_en);
+ current_scope[wire_en->str] = wire_en;
+ while (wire_en->simplify(true, false, false, 1, -1, false, false)) { }
+ }
+
+ std::vector<RTLIL::State> x_bits_addr, x_bits_data, set_bits_en;
+ for (int i = 0; i < addr_bits; i++)
+ x_bits_addr.push_back(RTLIL::State::Sx);
+ for (int i = 0; i < mem_width; i++)
+ x_bits_data.push_back(RTLIL::State::Sx);
+ for (int i = 0; i < mem_width; i++)
+ set_bits_en.push_back(RTLIL::State::S1);
+
+ AstNode *assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_addr, false));
+ assign_addr->children[0]->str = id_addr;
+
+ AstNode *assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_data, false));
+ assign_data->children[0]->str = id_data;
+
+ AstNode *assign_en = nullptr;
+ if (current_always->type != AST_INITIAL) {
+ assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width));
+ assign_en->children[0]->str = id_en;
+ }
+
+ AstNode *default_signals = new AstNode(AST_BLOCK);
+ default_signals->children.push_back(assign_addr);
+ default_signals->children.push_back(assign_data);
+ if (current_always->type != AST_INITIAL)
+ default_signals->children.push_back(assign_en);
+ current_top_block->children.insert(current_top_block->children.begin(), default_signals);
+
+ assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());
+ assign_addr->children[0]->str = id_addr;
+
+ if (children[0]->children.size() == 2)
+ {
+ if (children[0]->children[1]->range_valid)
+ {
+ int offset = children[0]->children[1]->range_right;
+ int width = children[0]->children[1]->range_left - offset + 1;
+ offset -= mem_data_range_offset;
+
+ std::vector<RTLIL::State> padding_x(offset, RTLIL::State::Sx);
+
+ assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
+ new AstNode(AST_CONCAT, mkconst_bits(padding_x, false), children[1]->clone()));
+ assign_data->children[0]->str = id_data;
+
+ if (current_always->type != AST_INITIAL) {
+ for (int i = 0; i < mem_width; i++)
+ set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0;
+ assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
+ assign_en->children[0]->str = id_en;
+ }
+ }
+ else
+ {
+ AstNode *the_range = children[0]->children[1];
+ AstNode *left_at_zero_ast = the_range->children[0]->clone();
+ AstNode *right_at_zero_ast = the_range->children.size() >= 2 ? the_range->children[1]->clone() : left_at_zero_ast->clone();
+ AstNode *offset_ast = right_at_zero_ast->clone();
+
+ if (mem_data_range_offset)
+ offset_ast = new AstNode(AST_SUB, offset_ast, mkconst_int(mem_data_range_offset, true));
+
+ while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
+ while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
+ if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
+ log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
+ int width = 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()));
+ assign_data->children[0]->str = id_data;
+
+ if (current_always->type != AST_INITIAL) {
+ for (int i = 0; i < mem_width; i++)
+ set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0;
+ assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
+ new AstNode(AST_SHIFT_LEFT, mkconst_bits(set_bits_en, false), offset_ast->clone()));
+ assign_en->children[0]->str = id_en;
+ }
+
+ delete left_at_zero_ast;
+ delete right_at_zero_ast;
+ delete offset_ast;
+ }
+ }
+ else
+ {
+ assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone());
+ assign_data->children[0]->str = id_data;
+
+ if (current_always->type != AST_INITIAL) {
+ assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
+ assign_en->children[0]->str = id_en;
+ }
+ }
+
+ newNode = new AstNode(AST_BLOCK);
+ newNode->children.push_back(assign_addr);
+ newNode->children.push_back(assign_data);
+ if (current_always->type != AST_INITIAL)
+ newNode->children.push_back(assign_en);
+
+ AstNode *wrnode = new AstNode(current_always->type == AST_INITIAL ? AST_MEMINIT : AST_MEMWR);
+ wrnode->children.push_back(new AstNode(AST_IDENTIFIER));
+ wrnode->children.push_back(new AstNode(AST_IDENTIFIER));
+ if (current_always->type != AST_INITIAL)
+ wrnode->children.push_back(new AstNode(AST_IDENTIFIER));
+ else
+ wrnode->children.push_back(AstNode::mkconst_int(1, false));
+ wrnode->str = children[0]->str;
+ wrnode->id2ast = children[0]->id2ast;
+ wrnode->children[0]->str = id_addr;
+ wrnode->children[1]->str = id_data;
+ if (current_always->type != AST_INITIAL)
+ wrnode->children[2]->str = id_en;
+ current_ast_mod->children.push_back(wrnode);
+
+ goto apply_newNode;
+ }
+
+ // replace function and task calls with the code from the function or task
+ if ((type == AST_FCALL || type == AST_TCALL) && !str.empty())
+ {
+ if (type == AST_FCALL)
+ {
+ if (str == "\\$initstate")
+ {
+ int myidx = autoidx++;
+
+ AstNode *wire = new AstNode(AST_WIRE);
+ wire->str = stringf("$initstate$%d_wire", myidx);
+ current_ast_mod->children.push_back(wire);
+ while (wire->simplify(true, false, false, 1, -1, false, false)) { }
+
+ AstNode *cell = new AstNode(AST_CELL, new AstNode(AST_CELLTYPE), new AstNode(AST_ARGUMENT, new AstNode(AST_IDENTIFIER)));
+ cell->str = stringf("$initstate$%d", myidx);
+ cell->children[0]->str = "$initstate";
+ cell->children[1]->str = "\\Y";
+ cell->children[1]->children[0]->str = wire->str;
+ cell->children[1]->children[0]->id2ast = wire;
+ current_ast_mod->children.push_back(cell);
+ while (cell->simplify(true, false, false, 1, -1, false, false)) { }
+
+ newNode = new AstNode(AST_IDENTIFIER);
+ newNode->str = wire->str;
+ newNode->id2ast = wire;
+ goto apply_newNode;
+ }
+
+ if (str == "\\$past")
+ {
+ if (width_hint <= 0)
+ goto replace_fcall_later;
+
+ int num_steps = 1;
+
+ if (GetSize(children) != 1 && GetSize(children) != 2)
+ log_error("System function %s got %d arguments, expected 1 or 2 at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+
+ if (!current_always_clocked)
+ log_error("System function %s is only allowed in clocked blocks at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+
+ if (GetSize(children) == 2)
+ {
+ AstNode *buf = children[1]->clone();
+ while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ if (buf->type != AST_CONSTANT)
+ log_error("Failed to evaluate system function `%s' with non-constant value at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+
+ num_steps = buf->asInt(true);
+ delete buf;
+ }
+
+ AstNode *block = nullptr;
+
+ for (auto child : current_always->children)
+ if (child->type == AST_BLOCK)
+ block = child;
+
+ log_assert(block != nullptr);
+
+ int myidx = autoidx++;
+ AstNode *outreg = nullptr;
+
+ for (int i = 0; i < num_steps; i++)
+ {
+ AstNode *reg = new AstNode(AST_WIRE, new AstNode(AST_RANGE,
+ mkconst_int(width_hint-1, true), mkconst_int(0, true)));
+
+ reg->str = stringf("$past$%s:%d$%d$%d", filename.c_str(), linenum, myidx, i);
+ reg->is_reg = true;
+
+ current_ast_mod->children.push_back(reg);
+
+ while (reg->simplify(true, false, false, 1, -1, false, false)) { }
+
+ AstNode *regid = new AstNode(AST_IDENTIFIER);
+ regid->str = reg->str;
+ regid->id2ast = reg;
+
+ AstNode *rhs = nullptr;
+
+ if (outreg == nullptr) {
+ rhs = children.at(0)->clone();
+ } else {
+ rhs = new AstNode(AST_IDENTIFIER);
+ rhs->str = outreg->str;
+ rhs->id2ast = outreg;
+ }
+
+ block->children.push_back(new AstNode(AST_ASSIGN_LE, regid, rhs));
+ outreg = reg;
+ }
+
+ newNode = new AstNode(AST_IDENTIFIER);
+ newNode->str = outreg->str;
+ newNode->id2ast = outreg;
+ goto apply_newNode;
+ }
+
+ if (str == "\\$stable" || str == "\\$rose" || str == "\\$fell")
+ {
+ if (GetSize(children) != 1)
+ log_error("System function %s got %d arguments, expected 1 at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+
+ if (!current_always_clocked)
+ log_error("System function %s is only allowed in clocked blocks at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+
+ AstNode *present = children.at(0)->clone();
+ AstNode *past = clone();
+ past->str = "\\$past";
+
+ if (str == "\\$stable")
+ newNode = new AstNode(AST_EQ, past, present);
+
+ else if (str == "\\$rose")
+ newNode = new AstNode(AST_LOGIC_AND, new AstNode(AST_LOGIC_NOT, past), present);
+
+ else if (str == "\\$fell")
+ newNode = new AstNode(AST_LOGIC_AND, past, new AstNode(AST_LOGIC_NOT, present));
+
+ else
+ log_abort();
+
+ goto apply_newNode;
+ }
+
+ if (str == "\\$rose" || str == "\\$fell")
+ {
+ if (GetSize(children) != 1)
+ log_error("System function %s got %d arguments, expected 1 at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+
+ if (!current_always_clocked)
+ log_error("System function %s is only allowed in clocked blocks at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+
+ newNode = new AstNode(AST_EQ, children.at(0)->clone(), clone());
+ newNode->children.at(1)->str = "\\$past";
+ goto apply_newNode;
+ }
+
+ // $anyconst and $anyseq are mapped in AstNode::genRTLIL()
+ if (str == "\\$anyconst" || str == "\\$anyseq") {
+ recursion_counter--;
+ return false;
+ }
+
+ if (str == "\\$clog2")
+ {
+ if (children.size() != 1)
+ log_error("System function %s got %d arguments, expected 1 at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+
+ AstNode *buf = children[0]->clone();
+ while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ if (buf->type != AST_CONSTANT)
+ log_error("Failed to evaluate system function `%s' with non-constant value at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+
+ RTLIL::Const arg_value = buf->bitsAsConst();
+ if (arg_value.as_bool())
+ arg_value = const_sub(arg_value, 1, false, false, GetSize(arg_value));
+ delete buf;
+
+ uint32_t result = 0;
+ for (size_t i = 0; i < arg_value.bits.size(); i++)
+ if (arg_value.bits.at(i) == RTLIL::State::S1)
+ result = i + 1;
+
+ newNode = mkconst_int(result, false);
+ goto apply_newNode;
+ }
+
+ if (str == "\\$ln" || str == "\\$log10" || str == "\\$exp" || str == "\\$sqrt" || str == "\\$pow" ||
+ str == "\\$floor" || str == "\\$ceil" || str == "\\$sin" || str == "\\$cos" || str == "\\$tan" ||
+ str == "\\$asin" || str == "\\$acos" || str == "\\$atan" || str == "\\$atan2" || str == "\\$hypot" ||
+ str == "\\$sinh" || str == "\\$cosh" || str == "\\$tanh" || str == "\\$asinh" || str == "\\$acosh" || str == "\\$atanh")
+ {
+ bool func_with_two_arguments = str == "\\$pow" || str == "\\$atan2" || str == "\\$hypot";
+ double x = 0, y = 0;
+
+ if (func_with_two_arguments) {
+ if (children.size() != 2)
+ log_error("System function %s got %d arguments, expected 2 at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+ } else {
+ if (children.size() != 1)
+ log_error("System function %s got %d arguments, expected 1 at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+ }
+
+ if (children.size() >= 1) {
+ while (children[0]->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ if (!children[0]->isConst())
+ log_error("Failed to evaluate system function `%s' with non-constant argument at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+ int child_width_hint = width_hint;
+ bool child_sign_hint = sign_hint;
+ children[0]->detectSignWidth(child_width_hint, child_sign_hint);
+ x = children[0]->asReal(child_sign_hint);
+ }
+
+ if (children.size() >= 2) {
+ while (children[1]->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ if (!children[1]->isConst())
+ log_error("Failed to evaluate system function `%s' with non-constant argument at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+ int child_width_hint = width_hint;
+ bool child_sign_hint = sign_hint;
+ children[1]->detectSignWidth(child_width_hint, child_sign_hint);
+ y = children[1]->asReal(child_sign_hint);
+ }
+
+ newNode = new AstNode(AST_REALVALUE);
+ if (str == "\\$ln") newNode->realvalue = ::log(x);
+ else if (str == "\\$log10") newNode->realvalue = ::log10(x);
+ else if (str == "\\$exp") newNode->realvalue = ::exp(x);
+ else if (str == "\\$sqrt") newNode->realvalue = ::sqrt(x);
+ else if (str == "\\$pow") newNode->realvalue = ::pow(x, y);
+ else if (str == "\\$floor") newNode->realvalue = ::floor(x);
+ else if (str == "\\$ceil") newNode->realvalue = ::ceil(x);
+ else if (str == "\\$sin") newNode->realvalue = ::sin(x);
+ else if (str == "\\$cos") newNode->realvalue = ::cos(x);
+ else if (str == "\\$tan") newNode->realvalue = ::tan(x);
+ else if (str == "\\$asin") newNode->realvalue = ::asin(x);
+ else if (str == "\\$acos") newNode->realvalue = ::acos(x);
+ else if (str == "\\$atan") newNode->realvalue = ::atan(x);
+ else if (str == "\\$atan2") newNode->realvalue = ::atan2(x, y);
+ else if (str == "\\$hypot") newNode->realvalue = ::hypot(x, y);
+ else if (str == "\\$sinh") newNode->realvalue = ::sinh(x);
+ else if (str == "\\$cosh") newNode->realvalue = ::cosh(x);
+ else if (str == "\\$tanh") newNode->realvalue = ::tanh(x);
+ else if (str == "\\$asinh") newNode->realvalue = ::asinh(x);
+ else if (str == "\\$acosh") newNode->realvalue = ::acosh(x);
+ else if (str == "\\$atanh") newNode->realvalue = ::atanh(x);
+ else log_abort();
+ goto apply_newNode;
+ }
+
+ if (current_scope.count(str) != 0 && current_scope[str]->type == AST_DPI_FUNCTION)
+ {
+ AstNode *dpi_decl = current_scope[str];
+
+ std::string rtype, fname;
+ std::vector<std::string> argtypes;
+ std::vector<AstNode*> args;
+
+ rtype = RTLIL::unescape_id(dpi_decl->children.at(0)->str);
+ fname = RTLIL::unescape_id(dpi_decl->children.at(1)->str);
+
+ for (int i = 2; i < GetSize(dpi_decl->children); i++)
+ {
+ if (i-2 >= GetSize(children))
+ log_error("Insufficient number of arguments in DPI function call at %s:%d.\n", filename.c_str(), linenum);
+
+ argtypes.push_back(RTLIL::unescape_id(dpi_decl->children.at(i)->str));
+ args.push_back(children.at(i-2)->clone());
+ while (args.back()->simplify(true, false, false, stage, -1, false, true)) { }
+
+ if (args.back()->type != AST_CONSTANT && args.back()->type != AST_REALVALUE)
+ log_error("Failed to evaluate DPI function with non-constant argument at %s:%d.\n", filename.c_str(), linenum);
+ }
+
+ newNode = dpi_call(rtype, fname, argtypes, args);
+
+ for (auto arg : args)
+ delete arg;
+
+ goto apply_newNode;
+ }
+
+ if (current_scope.count(str) == 0 || current_scope[str]->type != AST_FUNCTION)
+ log_error("Can't resolve function name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ }
+
+ if (type == AST_TCALL)
+ {
+ if (str == "$finish" || str == "$stop")
+ {
+ if (!current_always || current_always->type != AST_INITIAL)
+ log_error("System task `%s' outside initial block is unsupported at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+
+ log_error("System task `%s' executed at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ }
+
+ if (str == "\\$readmemh" || str == "\\$readmemb")
+ {
+ if (GetSize(children) < 2 || GetSize(children) > 4)
+ log_error("System function %s got %d arguments, expected 2-4 at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+
+ AstNode *node_filename = children[0]->clone();
+ while (node_filename->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ if (node_filename->type != AST_CONSTANT)
+ log_error("Failed to evaluate system function `%s' with non-constant 1st argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+
+ AstNode *node_memory = children[1]->clone();
+ while (node_memory->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ if (node_memory->type != AST_IDENTIFIER || node_memory->id2ast == nullptr || node_memory->id2ast->type != AST_MEMORY)
+ log_error("Failed to evaluate system function `%s' with non-memory 2nd argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+
+ int start_addr = -1, finish_addr = -1;
+
+ if (GetSize(children) > 2) {
+ AstNode *node_addr = children[2]->clone();
+ while (node_addr->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ if (node_addr->type != AST_CONSTANT)
+ log_error("Failed to evaluate system function `%s' with non-constant 3rd argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ start_addr = int(node_addr->asInt(false));
+ }
+
+ if (GetSize(children) > 3) {
+ AstNode *node_addr = children[3]->clone();
+ while (node_addr->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ if (node_addr->type != AST_CONSTANT)
+ log_error("Failed to evaluate system function `%s' with non-constant 4th argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ finish_addr = int(node_addr->asInt(false));
+ }
+
+ bool unconditional_init = false;
+ if (current_always->type == AST_INITIAL) {
+ pool<AstNode*> queue;
+ log_assert(current_always->children[0]->type == AST_BLOCK);
+ queue.insert(current_always->children[0]);
+ while (!unconditional_init && !queue.empty()) {
+ pool<AstNode*> next_queue;
+ for (auto n : queue)
+ for (auto c : n->children) {
+ if (c == this)
+ unconditional_init = true;
+ next_queue.insert(c);
+ }
+ next_queue.swap(queue);
+ }
+ }
+
+ newNode = readmem(str == "\\$readmemh", node_filename->bitsAsConst().decode_string(), node_memory->id2ast, start_addr, finish_addr, unconditional_init);
+ goto apply_newNode;
+ }
+
+ if (current_scope.count(str) == 0 || current_scope[str]->type != AST_TASK)
+ log_error("Can't resolve task name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ }
+
+ AstNode *decl = current_scope[str];
+
+ std::stringstream sstr;
+ sstr << "$func$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++) << "$";
+ std::string prefix = sstr.str();
+
+ bool recommend_const_eval = false;
+ bool require_const_eval = in_param ? false : has_const_only_constructs(recommend_const_eval);
+ if ((in_param || recommend_const_eval || require_const_eval) && !decl->attributes.count("\\via_celltype"))
+ {
+ bool all_args_const = true;
+ for (auto child : children) {
+ while (child->simplify(true, false, false, 1, -1, false, true)) { }
+ if (child->type != AST_CONSTANT)
+ all_args_const = false;
+ }
+
+ if (all_args_const) {
+ AstNode *func_workspace = current_scope[str]->clone();
+ newNode = func_workspace->eval_const_function(this);
+ delete func_workspace;
+ goto apply_newNode;
+ }
+
+ if (in_param)
+ log_error("Non-constant function call in constant expression at %s:%d.\n", filename.c_str(), linenum);
+ if (require_const_eval)
+ log_error("Function %s can only be called with constant arguments at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ }
+
+ size_t arg_count = 0;
+ std::map<std::string, std::string> replace_rules;
+ vector<AstNode*> added_mod_children;
+ dict<std::string, AstNode*> wire_cache;
+
+ if (current_block == NULL)
+ {
+ log_assert(type == AST_FCALL);
+
+ AstNode *wire = NULL;
+ for (auto child : decl->children)
+ if (child->type == AST_WIRE && child->str == str)
+ wire = child->clone();
+ log_assert(wire != NULL);
+
+ wire->str = prefix + str;
+ wire->port_id = 0;
+ wire->is_input = false;
+ wire->is_output = false;
+
+ current_ast_mod->children.push_back(wire);
+ while (wire->simplify(true, false, false, 1, -1, false, false)) { }
+
+ AstNode *lvalue = new AstNode(AST_IDENTIFIER);
+ lvalue->str = wire->str;
+
+ AstNode *always = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK,
+ new AstNode(AST_ASSIGN_EQ, lvalue, clone())));
+ current_ast_mod->children.push_back(always);
+
+ goto replace_fcall_with_id;
+ }
+
+ if (decl->attributes.count("\\via_celltype"))
+ {
+ std::string celltype = decl->attributes.at("\\via_celltype")->asAttrConst().decode_string();
+ std::string outport = str;
+
+ if (celltype.find(' ') != std::string::npos) {
+ int pos = celltype.find(' ');
+ outport = RTLIL::escape_id(celltype.substr(pos+1));
+ celltype = RTLIL::escape_id(celltype.substr(0, pos));
+ } else
+ celltype = RTLIL::escape_id(celltype);
+
+ AstNode *cell = new AstNode(AST_CELL, new AstNode(AST_CELLTYPE));
+ cell->str = prefix.substr(0, GetSize(prefix)-1);
+ cell->children[0]->str = celltype;
+
+ for (auto attr : decl->attributes)
+ if (attr.first.str().rfind("\\via_celltype_defparam_", 0) == 0)
+ {
+ AstNode *cell_arg = new AstNode(AST_PARASET, attr.second->clone());
+ cell_arg->str = RTLIL::escape_id(attr.first.str().substr(strlen("\\via_celltype_defparam_")));
+ cell->children.push_back(cell_arg);
+ }
+
+ for (auto child : decl->children)
+ if (child->type == AST_WIRE && (child->is_input || child->is_output || (type == AST_FCALL && child->str == str)))
+ {
+ AstNode *wire = child->clone();
+ wire->str = prefix + wire->str;
+ wire->port_id = 0;
+ wire->is_input = false;
+ wire->is_output = false;
+ current_ast_mod->children.push_back(wire);
+ while (wire->simplify(true, false, false, 1, -1, false, false)) { }
+
+ AstNode *wire_id = new AstNode(AST_IDENTIFIER);
+ wire_id->str = wire->str;
+
+ if ((child->is_input || child->is_output) && arg_count < children.size())
+ {
+ AstNode *arg = children[arg_count++]->clone();
+ AstNode *assign = child->is_input ?
+ new AstNode(AST_ASSIGN_EQ, wire_id->clone(), arg) :
+ new AstNode(AST_ASSIGN_EQ, arg, wire_id->clone());
+
+ 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;
+ }
+ }
+
+ AstNode *cell_arg = new AstNode(AST_ARGUMENT, wire_id);
+ cell_arg->str = child->str == str ? outport : child->str;
+ cell->children.push_back(cell_arg);
+ }
+
+ current_ast_mod->children.push_back(cell);
+ goto replace_fcall_with_id;
+ }
+
+ for (auto child : decl->children)
+ if (child->type == AST_WIRE || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM)
+ {
+ AstNode *wire = nullptr;
+
+ if (wire_cache.count(child->str))
+ {
+ wire = wire_cache.at(child->str);
+ if (wire->children.empty()) {
+ for (auto c : child->children)
+ wire->children.push_back(c->clone());
+ } else {
+ if (!child->children.empty())
+ log_error("Incompatible re-declaration of wire %s at %s:%d.\n", child->str.c_str(), filename.c_str(), linenum);
+ }
+ }
+ else
+ {
+ wire = child->clone();
+ wire->str = prefix + wire->str;
+ 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_cache[child->str] = wire;
+
+ current_ast_mod->children.push_back(wire);
+ added_mod_children.push_back(wire);
+ }
+
+ if (child->type == AST_WIRE)
+ while (wire->simplify(true, false, false, 1, -1, false, false)) { }
+
+ replace_rules[child->str] = wire->str;
+ current_scope[wire->str] = wire;
+
+ if ((child->is_input || child->is_output) && arg_count < children.size())
+ {
+ AstNode *arg = children[arg_count++]->clone();
+ AstNode *wire_id = new AstNode(AST_IDENTIFIER);
+ wire_id->str = wire->str;
+ 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;
+ }
+ }
+ }
+
+ for (auto child : added_mod_children) {
+ child->replace_ids(prefix, replace_rules);
+ while (child->simplify(true, false, false, 1, -1, false, false)) { }
+ }
+
+ for (auto child : decl->children)
+ if (child->type != AST_WIRE && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM)
+ {
+ AstNode *stmt = child->clone();
+ stmt->replace_ids(prefix, replace_rules);
+
+ 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;
+ }
+ }
+
+ replace_fcall_with_id:
+ if (type == AST_FCALL) {
+ delete_children();
+ type = AST_IDENTIFIER;
+ str = prefix + str;
+ }
+ if (type == AST_TCALL)
+ str = "";
+ did_something = true;
+ }
+
+replace_fcall_later:;
+
+ // perform const folding when activated
+ if (const_fold)
+ {
+ bool string_op;
+ std::vector<RTLIL::State> tmp_bits;
+ RTLIL::Const (*const_func)(const RTLIL::Const&, const RTLIL::Const&, bool, bool, int);
+ RTLIL::Const dummy_arg;
+
+ switch (type)
+ {
+ case AST_IDENTIFIER:
+ if (current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM)) {
+ if (current_scope[str]->children[0]->type == AST_CONSTANT) {
+ if (children.size() != 0 && children[0]->type == AST_RANGE && children[0]->range_valid) {
+ std::vector<RTLIL::State> data;
+ bool param_upto = current_scope[str]->range_valid && current_scope[str]->range_swapped;
+ int param_offset = current_scope[str]->range_valid ? current_scope[str]->range_right : 0;
+ int param_width = current_scope[str]->range_valid ? current_scope[str]->range_left - current_scope[str]->range_right + 1 :
+ GetSize(current_scope[str]->children[0]->bits);
+ int tmp_range_left = children[0]->range_left, tmp_range_right = children[0]->range_right;
+ if (param_upto) {
+ tmp_range_left = (param_width + 2*param_offset) - children[0]->range_right - 1;
+ tmp_range_right = (param_width + 2*param_offset) - children[0]->range_left - 1;
+ }
+ for (int i = tmp_range_right; i <= tmp_range_left; i++) {
+ int index = i - param_offset;
+ if (0 <= index && index < param_width)
+ data.push_back(current_scope[str]->children[0]->bits[index]);
+ else
+ data.push_back(RTLIL::State::Sx);
+ }
+ newNode = mkconst_bits(data, false);
+ } else
+ if (children.size() == 0)
+ newNode = current_scope[str]->children[0]->clone();
+ } else
+ if (current_scope[str]->children[0]->isConst())
+ newNode = current_scope[str]->children[0]->clone();
+ }
+ else if (at_zero && current_scope.count(str) > 0 && (current_scope[str]->type == AST_WIRE || current_scope[str]->type == AST_AUTOWIRE)) {
+ newNode = mkconst_int(0, sign_hint, width_hint);
+ }
+ break;
+ case AST_BIT_NOT:
+ if (children[0]->type == AST_CONSTANT) {
+ RTLIL::Const y = RTLIL::const_not(children[0]->bitsAsConst(width_hint, sign_hint), dummy_arg, sign_hint, false, width_hint);
+ newNode = mkconst_bits(y.bits, sign_hint);
+ }
+ break;
+ case AST_TO_SIGNED:
+ case AST_TO_UNSIGNED:
+ if (children[0]->type == AST_CONSTANT) {
+ RTLIL::Const y = children[0]->bitsAsConst(width_hint, sign_hint);
+ newNode = mkconst_bits(y.bits, type == AST_TO_SIGNED);
+ }
+ break;
+ if (0) { case AST_BIT_AND: const_func = RTLIL::const_and; }
+ if (0) { case AST_BIT_OR: const_func = RTLIL::const_or; }
+ if (0) { case AST_BIT_XOR: const_func = RTLIL::const_xor; }
+ if (0) { case AST_BIT_XNOR: const_func = RTLIL::const_xnor; }
+ if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
+ RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint),
+ children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint);
+ newNode = mkconst_bits(y.bits, sign_hint);
+ }
+ break;
+ if (0) { case AST_REDUCE_AND: const_func = RTLIL::const_reduce_and; }
+ if (0) { case AST_REDUCE_OR: const_func = RTLIL::const_reduce_or; }
+ if (0) { case AST_REDUCE_XOR: const_func = RTLIL::const_reduce_xor; }
+ if (0) { case AST_REDUCE_XNOR: const_func = RTLIL::const_reduce_xnor; }
+ if (0) { case AST_REDUCE_BOOL: const_func = RTLIL::const_reduce_bool; }
+ if (children[0]->type == AST_CONSTANT) {
+ RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), dummy_arg, false, false, -1);
+ newNode = mkconst_bits(y.bits, false);
+ }
+ break;
+ case AST_LOGIC_NOT:
+ if (children[0]->type == AST_CONSTANT) {
+ RTLIL::Const y = RTLIL::const_logic_not(RTLIL::Const(children[0]->bits), dummy_arg, children[0]->is_signed, false, -1);
+ newNode = mkconst_bits(y.bits, false);
+ } else
+ if (children[0]->isConst()) {
+ newNode = mkconst_int(children[0]->asReal(sign_hint) == 0, false, 1);
+ }
+ break;
+ if (0) { case AST_LOGIC_AND: const_func = RTLIL::const_logic_and; }
+ if (0) { case AST_LOGIC_OR: const_func = RTLIL::const_logic_or; }
+ if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
+ RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), RTLIL::Const(children[1]->bits),
+ children[0]->is_signed, children[1]->is_signed, -1);
+ newNode = mkconst_bits(y.bits, false);
+ } else
+ if (children[0]->isConst() && children[1]->isConst()) {
+ if (type == AST_LOGIC_AND)
+ newNode = mkconst_int((children[0]->asReal(sign_hint) != 0) && (children[1]->asReal(sign_hint) != 0), false, 1);
+ else
+ newNode = mkconst_int((children[0]->asReal(sign_hint) != 0) || (children[1]->asReal(sign_hint) != 0), false, 1);
+ }
+ break;
+ if (0) { case AST_SHIFT_LEFT: const_func = RTLIL::const_shl; }
+ if (0) { case AST_SHIFT_RIGHT: const_func = RTLIL::const_shr; }
+ if (0) { case AST_SHIFT_SLEFT: const_func = RTLIL::const_sshl; }
+ if (0) { case AST_SHIFT_SRIGHT: const_func = RTLIL::const_sshr; }
+ if (0) { case AST_POW: const_func = RTLIL::const_pow; }
+ if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
+ RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint),
+ RTLIL::Const(children[1]->bits), sign_hint, type == AST_POW ? children[1]->is_signed : false, width_hint);
+ newNode = mkconst_bits(y.bits, sign_hint);
+ } else
+ if (type == AST_POW && children[0]->isConst() && children[1]->isConst()) {
+ newNode = new AstNode(AST_REALVALUE);
+ newNode->realvalue = pow(children[0]->asReal(sign_hint), children[1]->asReal(sign_hint));
+ }
+ break;
+ if (0) { case AST_LT: const_func = RTLIL::const_lt; }
+ if (0) { case AST_LE: const_func = RTLIL::const_le; }
+ if (0) { case AST_EQ: const_func = RTLIL::const_eq; }
+ if (0) { case AST_NE: const_func = RTLIL::const_ne; }
+ if (0) { case AST_EQX: const_func = RTLIL::const_eqx; }
+ if (0) { case AST_NEX: const_func = RTLIL::const_nex; }
+ if (0) { case AST_GE: const_func = RTLIL::const_ge; }
+ if (0) { case AST_GT: const_func = RTLIL::const_gt; }
+ if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
+ int cmp_width = max(children[0]->bits.size(), children[1]->bits.size());
+ bool cmp_signed = children[0]->is_signed && children[1]->is_signed;
+ RTLIL::Const y = const_func(children[0]->bitsAsConst(cmp_width, cmp_signed),
+ children[1]->bitsAsConst(cmp_width, cmp_signed), cmp_signed, cmp_signed, 1);
+ newNode = mkconst_bits(y.bits, false);
+ } else
+ if (children[0]->isConst() && children[1]->isConst()) {
+ bool cmp_signed = (children[0]->type == AST_REALVALUE || children[0]->is_signed) && (children[1]->type == AST_REALVALUE || children[1]->is_signed);
+ switch (type) {
+ case AST_LT: newNode = mkconst_int(children[0]->asReal(cmp_signed) < children[1]->asReal(cmp_signed), false, 1); break;
+ case AST_LE: newNode = mkconst_int(children[0]->asReal(cmp_signed) <= children[1]->asReal(cmp_signed), false, 1); break;
+ case AST_EQ: newNode = mkconst_int(children[0]->asReal(cmp_signed) == children[1]->asReal(cmp_signed), false, 1); break;
+ case AST_NE: newNode = mkconst_int(children[0]->asReal(cmp_signed) != children[1]->asReal(cmp_signed), false, 1); break;
+ case AST_EQX: newNode = mkconst_int(children[0]->asReal(cmp_signed) == children[1]->asReal(cmp_signed), false, 1); break;
+ case AST_NEX: newNode = mkconst_int(children[0]->asReal(cmp_signed) != children[1]->asReal(cmp_signed), false, 1); break;
+ case AST_GE: newNode = mkconst_int(children[0]->asReal(cmp_signed) >= children[1]->asReal(cmp_signed), false, 1); break;
+ case AST_GT: newNode = mkconst_int(children[0]->asReal(cmp_signed) > children[1]->asReal(cmp_signed), false, 1); break;
+ default: log_abort();
+ }
+ }
+ break;
+ if (0) { case AST_ADD: const_func = RTLIL::const_add; }
+ if (0) { case AST_SUB: const_func = RTLIL::const_sub; }
+ if (0) { case AST_MUL: const_func = RTLIL::const_mul; }
+ if (0) { case AST_DIV: const_func = RTLIL::const_div; }
+ if (0) { case AST_MOD: const_func = RTLIL::const_mod; }
+ if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
+ RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint),
+ children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint);
+ newNode = mkconst_bits(y.bits, sign_hint);
+ } else
+ if (children[0]->isConst() && children[1]->isConst()) {
+ newNode = new AstNode(AST_REALVALUE);
+ switch (type) {
+ case AST_ADD: newNode->realvalue = children[0]->asReal(sign_hint) + children[1]->asReal(sign_hint); break;
+ case AST_SUB: newNode->realvalue = children[0]->asReal(sign_hint) - children[1]->asReal(sign_hint); break;
+ case AST_MUL: newNode->realvalue = children[0]->asReal(sign_hint) * children[1]->asReal(sign_hint); break;
+ case AST_DIV: newNode->realvalue = children[0]->asReal(sign_hint) / children[1]->asReal(sign_hint); break;
+ case AST_MOD: newNode->realvalue = fmod(children[0]->asReal(sign_hint), children[1]->asReal(sign_hint)); break;
+ default: log_abort();
+ }
+ }
+ break;
+ if (0) { case AST_POS: const_func = RTLIL::const_pos; }
+ if (0) { case AST_NEG: const_func = RTLIL::const_neg; }
+ if (children[0]->type == AST_CONSTANT) {
+ RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), dummy_arg, sign_hint, false, width_hint);
+ newNode = mkconst_bits(y.bits, sign_hint);
+ } else
+ if (children[0]->isConst()) {
+ newNode = new AstNode(AST_REALVALUE);
+ if (type == AST_POS)
+ newNode->realvalue = +children[0]->asReal(sign_hint);
+ else
+ newNode->realvalue = -children[0]->asReal(sign_hint);
+ }
+ break;
+ case AST_TERNARY:
+ if (children[0]->isConst())
+ {
+ bool found_sure_true = false;
+ bool found_maybe_true = false;
+
+ if (children[0]->type == AST_CONSTANT)
+ for (auto &bit : children[0]->bits) {
+ if (bit == RTLIL::State::S1)
+ found_sure_true = true;
+ if (bit > RTLIL::State::S1)
+ found_maybe_true = true;
+ }
+ else
+ found_sure_true = children[0]->asReal(sign_hint) != 0;
+
+ AstNode *choice = NULL, *not_choice = NULL;
+ if (found_sure_true)
+ choice = children[1], not_choice = children[2];
+ else if (!found_maybe_true)
+ choice = children[2], not_choice = children[1];
+
+ if (choice != NULL) {
+ if (choice->type == AST_CONSTANT) {
+ int other_width_hint = width_hint;
+ bool other_sign_hint = sign_hint, other_real = false;
+ not_choice->detectSignWidth(other_width_hint, other_sign_hint, &other_real);
+ if (other_real) {
+ newNode = new AstNode(AST_REALVALUE);
+ choice->detectSignWidth(width_hint, sign_hint);
+ newNode->realvalue = choice->asReal(sign_hint);
+ } else {
+ RTLIL::Const y = choice->bitsAsConst(width_hint, sign_hint);
+ if (choice->is_string && y.bits.size() % 8 == 0 && sign_hint == false)
+ newNode = mkconst_str(y.bits);
+ else
+ newNode = mkconst_bits(y.bits, sign_hint);
+ }
+ } else
+ if (choice->isConst()) {
+ newNode = choice->clone();
+ }
+ } else if (children[1]->type == AST_CONSTANT && children[2]->type == AST_CONSTANT) {
+ RTLIL::Const a = children[1]->bitsAsConst(width_hint, sign_hint);
+ RTLIL::Const b = children[2]->bitsAsConst(width_hint, sign_hint);
+ log_assert(a.bits.size() == b.bits.size());
+ for (size_t i = 0; i < a.bits.size(); i++)
+ if (a.bits[i] != b.bits[i])
+ a.bits[i] = RTLIL::State::Sx;
+ newNode = mkconst_bits(a.bits, sign_hint);
+ } else if (children[1]->isConst() && children[2]->isConst()) {
+ newNode = new AstNode(AST_REALVALUE);
+ if (children[1]->asReal(sign_hint) == children[2]->asReal(sign_hint))
+ newNode->realvalue = children[1]->asReal(sign_hint);
+ else
+ // IEEE Std 1800-2012 Sec. 11.4.11 states that the entry in Table 7-1 for
+ // the data type in question should be returned if the ?: is ambiguous. The
+ // value in Table 7-1 for the 'real' type is 0.0.
+ newNode->realvalue = 0.0;
+ }
+ }
+ break;
+ case AST_CONCAT:
+ string_op = !children.empty();
+ for (auto it = children.begin(); it != children.end(); it++) {
+ if ((*it)->type != AST_CONSTANT)
+ goto not_const;
+ if (!(*it)->is_string)
+ string_op = false;
+ tmp_bits.insert(tmp_bits.end(), (*it)->bits.begin(), (*it)->bits.end());
+ }
+ newNode = string_op ? mkconst_str(tmp_bits) : mkconst_bits(tmp_bits, false);
+ break;
+ case AST_REPLICATE:
+ if (children.at(0)->type != AST_CONSTANT || children.at(1)->type != AST_CONSTANT)
+ goto not_const;
+ for (int i = 0; i < children[0]->bitsAsConst().as_int(); i++)
+ tmp_bits.insert(tmp_bits.end(), children.at(1)->bits.begin(), children.at(1)->bits.end());
+ newNode = children.at(1)->is_string ? mkconst_str(tmp_bits) : mkconst_bits(tmp_bits, false);
+ break;
+ default:
+ not_const:
+ break;
+ }
+ }
+
+ // if any of the above set 'newNode' -> use 'newNode' as template to update 'this'
+ if (newNode) {
+apply_newNode:
+ // fprintf(stderr, "----\n");
+ // dumpAst(stderr, "- ");
+ // newNode->dumpAst(stderr, "+ ");
+ log_assert(newNode != NULL);
+ newNode->filename = filename;
+ newNode->linenum = linenum;
+ newNode->cloneInto(this);
+ delete newNode;
+ did_something = true;
+ }
+
+ if (!did_something)
+ basic_prep = true;
+
+ recursion_counter--;
+ return did_something;
+}
+
+static void replace_result_wire_name_in_function(AstNode *node, std::string &from, std::string &to)
+{
+ for (auto &it : node->children)
+ replace_result_wire_name_in_function(it, from, to);
+ if (node->str == from)
+ node->str = to;
+}
+
+// replace a readmem[bh] TCALL ast node with a block of memory assignments
+AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init)
+{
+ int mem_width, mem_size, addr_bits;
+ memory->meminfo(mem_width, mem_size, addr_bits);
+
+ AstNode *block = new AstNode(AST_BLOCK);
+
+ AstNode *meminit = nullptr;
+ int next_meminit_cursor=0;
+ vector<State> meminit_bits;
+ int meminit_size=0;
+
+ std::ifstream f;
+ f.open(mem_filename.c_str());
+
+ if (f.fail())
+ log_error("Can not open file `%s` for %s at %s:%d.\n", mem_filename.c_str(), str.c_str(), filename.c_str(), linenum);
+
+ log_assert(GetSize(memory->children) == 2 && memory->children[1]->type == AST_RANGE && memory->children[1]->range_valid);
+ int range_left = memory->children[1]->range_left, range_right = memory->children[1]->range_right;
+ int range_min = min(range_left, range_right), range_max = max(range_left, range_right);
+
+ if (start_addr < 0)
+ start_addr = range_min;
+
+ if (finish_addr < 0)
+ finish_addr = range_max + 1;
+
+ bool in_comment = false;
+ int increment = start_addr <= finish_addr ? +1 : -1;
+ int cursor = start_addr;
+
+ while (!f.eof())
+ {
+ std::string line, token;
+ std::getline(f, line);
+
+ for (int i = 0; i < GetSize(line); i++) {
+ if (in_comment && line.substr(i, 2) == "*/") {
+ line[i] = ' ';
+ line[i+1] = ' ';
+ in_comment = false;
+ continue;
+ }
+ if (!in_comment && line.substr(i, 2) == "/*")
+ in_comment = true;
+ if (in_comment)
+ line[i] = ' ';
+ }
+
+ while (1)
+ {
+ token = next_token(line, " \t\r\n");
+ if (token.empty() || token.substr(0, 2) == "//")
+ break;
+
+ if (token[0] == '@') {
+ token = token.substr(1);
+ const char *nptr = token.c_str();
+ char *endptr;
+ cursor = strtol(nptr, &endptr, 16);
+ if (!*nptr || *endptr)
+ log_error("Can not parse address `%s` for %s at %s:%d.\n", nptr, str.c_str(), filename.c_str(), linenum);
+ continue;
+ }
+
+ AstNode *value = VERILOG_FRONTEND::const2ast(stringf("%d'%c", mem_width, is_readmemh ? 'h' : 'b') + token);
+
+ if (unconditional_init)
+ {
+ if (meminit == nullptr || cursor != next_meminit_cursor)
+ {
+ if (meminit != nullptr) {
+ meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false);
+ meminit->children[2] = AstNode::mkconst_int(meminit_size, false);
+ }
+
+ meminit = new AstNode(AST_MEMINIT);
+ meminit->children.push_back(AstNode::mkconst_int(cursor, false));
+ meminit->children.push_back(nullptr);
+ meminit->children.push_back(nullptr);
+ meminit->str = memory->str;
+ meminit->id2ast = memory;
+ meminit_bits.clear();
+ meminit_size = 0;
+
+ current_ast_mod->children.push_back(meminit);
+ next_meminit_cursor = cursor;
+ }
+
+ meminit_size++;
+ next_meminit_cursor++;
+ meminit_bits.insert(meminit_bits.end(), value->bits.begin(), value->bits.end());
+ delete value;
+ }
+ else
+ {
+ 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;
+ }
+
+ cursor += increment;
+ if ((cursor == finish_addr+increment) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min))
+ break;
+ }
+
+ if ((cursor == finish_addr+increment) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min))
+ break;
+ }
+
+ if (meminit != nullptr) {
+ meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false);
+ meminit->children[2] = AstNode::mkconst_int(meminit_size, false);
+ }
+
+ return block;
+}
+
+// annotate the names of all wires and other named objects in a generate block
+void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map)
+{
+ if (!index_var.empty() && type == AST_IDENTIFIER && str == index_var) {
+ current_scope[index_var]->children[0]->cloneInto(this);
+ return;
+ }
+
+ if ((type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL) && name_map.count(str) > 0)
+ str = name_map[str];
+
+ std::map<std::string, std::string> backup_name_map;
+
+ for (size_t i = 0; i < children.size(); i++) {
+ AstNode *child = children[i];
+ if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM ||
+ child->type == AST_FUNCTION || child->type == AST_TASK || child->type == AST_CELL) {
+ if (backup_name_map.size() == 0)
+ backup_name_map = name_map;
+ std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix;
+ size_t pos = child->str.rfind('.');
+ if (pos == std::string::npos)
+ pos = child->str[0] == '\\' ? 1 : 0;
+ else
+ pos = pos + 1;
+ new_name = child->str.substr(0, pos) + new_name + child->str.substr(pos);
+ if (new_name[0] != '$' && new_name[0] != '\\')
+ new_name = prefix[0] + new_name;
+ name_map[child->str] = new_name;
+ if (child->type == AST_FUNCTION)
+ replace_result_wire_name_in_function(child, child->str, new_name);
+ else
+ child->str = new_name;
+ current_scope[new_name] = child;
+ }
+ }
+
+ 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)
+ child->expand_genblock(index_var, prefix, name_map);
+ }
+
+ if (backup_name_map.size() > 0)
+ name_map.swap(backup_name_map);
+}
+
+// rename stuff (used when tasks of functions are instantiated)
+void AstNode::replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules)
+{
+ if (type == AST_BLOCK)
+ {
+ std::map<std::string, std::string> new_rules = rules;
+ std::string new_prefix = prefix + str;
+
+ for (auto child : children)
+ if (child->type == AST_WIRE) {
+ new_rules[child->str] = new_prefix + child->str;
+ child->str = new_prefix + child->str;
+ }
+
+ for (auto child : children)
+ if (child->type != AST_WIRE)
+ child->replace_ids(new_prefix, new_rules);
+ }
+ else
+ {
+ if (type == AST_IDENTIFIER && rules.count(str) > 0)
+ str = rules.at(str);
+ for (auto child : children)
+ child->replace_ids(prefix, rules);
+ }
+}
+
+// helper function for mem2reg_as_needed_pass1
+static void mark_memories_assign_lhs_complex(dict<AstNode*, pool<std::string>> &mem2reg_places,
+ dict<AstNode*, uint32_t> &mem2reg_candidates, AstNode *that)
+{
+ for (auto &child : that->children)
+ mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, child);
+
+ if (that->type == AST_IDENTIFIER && that->id2ast && that->id2ast->type == AST_MEMORY) {
+ AstNode *mem = that->id2ast;
+ if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_CMPLX_LHS))
+ mem2reg_places[mem].insert(stringf("%s:%d", that->filename.c_str(), that->linenum));
+ mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_CMPLX_LHS;
+ }
+}
+
+// find memories that should be replaced by registers
+void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg_places,
+ 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;
+
+ if (type == AST_ASSIGN || type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ)
+ {
+ // mark all memories that are used in a complex expression on the left side of an assignment
+ for (auto &lhs_child : children[0]->children)
+ mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, lhs_child);
+
+ if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY)
+ {
+ AstNode *mem = children[0]->id2ast;
+
+ // activate mem2reg if this is assigned in an async proc
+ if (flags & AstNode::MEM2REG_FL_ASYNC) {
+ if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_ASYNC))
+ mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), linenum));
+ mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_ASYNC;
+ }
+
+ // remember if this is assigned blocking (=)
+ if (type == AST_ASSIGN_EQ) {
+ if (!(proc_flags[mem] & AstNode::MEM2REG_FL_EQ1))
+ mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), linenum));
+ proc_flags[mem] |= AstNode::MEM2REG_FL_EQ1;
+ }
+
+ // remember where this is
+ if (flags & MEM2REG_FL_INIT) {
+ if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_INIT))
+ mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), linenum));
+ mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_INIT;
+ } else {
+ if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_ELSE))
+ mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), linenum));
+ mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_ELSE;
+ }
+ }
+
+ ignore_children_counter = 1;
+ }
+
+ if (type == AST_IDENTIFIER && id2ast && id2ast->type == AST_MEMORY)
+ {
+ AstNode *mem = id2ast;
+
+ // flag if used after blocking assignment (in same proc)
+ if ((proc_flags[mem] & AstNode::MEM2REG_FL_EQ1) && !(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_EQ2)) {
+ mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), linenum));
+ mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_EQ2;
+ }
+ }
+
+ // also activate if requested, either by using mem2reg attribute or by declaring array as 'wire' instead of 'reg'
+ if (type == AST_MEMORY && (get_bool_attribute("\\mem2reg") || (flags & AstNode::MEM2REG_FL_ALL) || !is_reg))
+ mem2reg_candidates[this] |= AstNode::MEM2REG_FL_FORCED;
+
+ if (type == AST_MODULE && get_bool_attribute("\\mem2reg"))
+ children_flags |= AstNode::MEM2REG_FL_ALL;
+
+ dict<AstNode*, uint32_t> *proc_flags_p = NULL;
+
+ if (type == AST_ALWAYS) {
+ int count_edge_events = 0;
+ for (auto child : children)
+ if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE)
+ count_edge_events++;
+ if (count_edge_events != 1)
+ children_flags |= AstNode::MEM2REG_FL_ASYNC;
+ proc_flags_p = new dict<AstNode*, uint32_t>;
+ }
+
+ if (type == AST_INITIAL) {
+ children_flags |= AstNode::MEM2REG_FL_INIT;
+ proc_flags_p = new dict<AstNode*, uint32_t>;
+ }
+
+ uint32_t backup_flags = flags;
+ flags |= children_flags;
+ log_assert((flags & ~0x000000ff) == 0);
+
+ for (auto child : children)
+ if (ignore_children_counter > 0)
+ ignore_children_counter--;
+ 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;
+
+ if (proc_flags_p) {
+#ifndef NDEBUG
+ for (auto it : *proc_flags_p)
+ log_assert((it.second & ~0xff000000) == 0);
+#endif
+ delete proc_flags_p;
+ }
+}
+
+bool AstNode::mem2reg_check(pool<AstNode*> &mem2reg_set)
+{
+ if (type != AST_IDENTIFIER || !id2ast || !mem2reg_set.count(id2ast))
+ return false;
+
+ if (children.empty() || children[0]->type != AST_RANGE || GetSize(children[0]->children) != 1)
+ log_error("Invalid array access at %s:%d.\n", filename.c_str(), linenum);
+
+ return true;
+}
+
+void AstNode::mem2reg_remove(pool<AstNode*> &mem2reg_set, vector<AstNode*> &delnodes)
+{
+ log_assert(mem2reg_set.count(this) == 0);
+
+ if (mem2reg_set.count(id2ast))
+ id2ast = nullptr;
+
+ for (size_t i = 0; i < children.size(); i++) {
+ if (mem2reg_set.count(children[i]) > 0) {
+ delnodes.push_back(children[i]);
+ children.erase(children.begin() + (i--));
+ } else {
+ children[i]->mem2reg_remove(mem2reg_set, delnodes);
+ }
+ }
+}
+
+// actually replace memories with registers
+bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block, AstNode *&async_block)
+{
+ bool did_something = false;
+
+ if (type == AST_BLOCK)
+ block = this;
+
+ if (type == AST_FUNCTION || type == AST_TASK)
+ return false;
+
+ if (type == AST_ASSIGN && block == NULL && children[0]->mem2reg_check(mem2reg_set))
+ {
+ if (async_block == NULL) {
+ async_block = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK));
+ mod->children.push_back(async_block);
+ }
+
+ AstNode *newNode = clone();
+ newNode->type = AST_ASSIGN_EQ;
+ async_block->children[0]->children.push_back(newNode);
+
+ newNode = new AstNode(AST_NONE);
+ newNode->cloneInto(this);
+ delete newNode;
+
+ did_something = true;
+ }
+
+ if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && children[0]->mem2reg_check(mem2reg_set) &&
+ children[0]->children[0]->children[0]->type != AST_CONSTANT)
+ {
+ std::stringstream sstr;
+ sstr << "$mem2reg_wr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++);
+ std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA";
+
+ 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);
+
+ AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true)));
+ wire_addr->str = id_addr;
+ wire_addr->is_reg = true;
+ wire_addr->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
+ mod->children.push_back(wire_addr);
+ while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { }
+
+ AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
+ wire_data->str = id_data;
+ wire_data->is_reg = true;
+ wire_data->is_signed = mem_signed;
+ wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
+ mod->children.push_back(wire_data);
+ while (wire_data->simplify(true, false, false, 1, -1, false, false)) { }
+
+ log_assert(block != NULL);
+ size_t assign_idx = 0;
+ while (assign_idx < block->children.size() && block->children[assign_idx] != this)
+ assign_idx++;
+ log_assert(assign_idx < block->children.size());
+
+ 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;
+ block->children.insert(block->children.begin()+assign_idx+1, assign_addr);
+
+ AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER));
+ case_node->children[0]->str = id_addr;
+ for (int i = 0; i < mem_size; i++) {
+ if (children[0]->children[0]->children[0]->type == AST_CONSTANT && int(children[0]->children[0]->children[0]->integer) != i)
+ continue;
+ AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK));
+ AstNode *assign_reg = new AstNode(type, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER));
+ if (children[0]->children.size() == 2)
+ assign_reg->children[0]->children.push_back(children[0]->children[1]->clone());
+ assign_reg->children[0]->str = stringf("%s[%d]", children[0]->str.c_str(), i);
+ assign_reg->children[1]->str = id_data;
+ cond_node->children[1]->children.push_back(assign_reg);
+ case_node->children.push_back(cond_node);
+ }
+ block->children.insert(block->children.begin()+assign_idx+2, case_node);
+
+ children[0]->delete_children();
+ children[0]->range_valid = false;
+ children[0]->id2ast = NULL;
+ children[0]->str = id_data;
+ type = AST_ASSIGN_EQ;
+
+ did_something = true;
+ }
+
+ if (mem2reg_check(mem2reg_set))
+ {
+ AstNode *bit_part_sel = NULL;
+ if (children.size() == 2)
+ bit_part_sel = children[1]->clone();
+
+ if (children[0]->children[0]->type == AST_CONSTANT)
+ {
+ int id = children[0]->children[0]->integer;
+ str = stringf("%s[%d]", str.c_str(), id);
+
+ delete_children();
+ range_valid = false;
+ id2ast = NULL;
+ }
+ else
+ {
+ std::stringstream sstr;
+ sstr << "$mem2reg_rd$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++);
+ std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA";
+
+ int mem_width, mem_size, addr_bits;
+ bool mem_signed = id2ast->is_signed;
+ id2ast->meminfo(mem_width, mem_size, addr_bits);
+
+ AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true)));
+ wire_addr->str = id_addr;
+ wire_addr->is_reg = true;
+ if (block)
+ wire_addr->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
+ mod->children.push_back(wire_addr);
+ while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { }
+
+ AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
+ wire_data->str = id_data;
+ wire_data->is_reg = true;
+ wire_data->is_signed = mem_signed;
+ if (block)
+ wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
+ mod->children.push_back(wire_data);
+ while (wire_data->simplify(true, false, false, 1, -1, false, false)) { }
+
+ AstNode *assign_addr = new AstNode(block ? AST_ASSIGN_EQ : AST_ASSIGN, new AstNode(AST_IDENTIFIER), children[0]->children[0]->clone());
+ assign_addr->children[0]->str = id_addr;
+
+ AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER));
+ case_node->children[0]->str = id_addr;
+
+ for (int i = 0; i < mem_size; i++) {
+ if (children[0]->children[0]->type == AST_CONSTANT && int(children[0]->children[0]->integer) != i)
+ continue;
+ AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK));
+ AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER));
+ assign_reg->children[0]->str = id_data;
+ assign_reg->children[1]->str = stringf("%s[%d]", str.c_str(), i);
+ cond_node->children[1]->children.push_back(assign_reg);
+ case_node->children.push_back(cond_node);
+ }
+
+ std::vector<RTLIL::State> x_bits;
+ for (int i = 0; i < mem_width; i++)
+ x_bits.push_back(RTLIL::State::Sx);
+
+ AstNode *cond_node = new AstNode(AST_COND, new AstNode(AST_DEFAULT), new AstNode(AST_BLOCK));
+ AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), AstNode::mkconst_bits(x_bits, false));
+ assign_reg->children[0]->str = id_data;
+ cond_node->children[1]->children.push_back(assign_reg);
+ case_node->children.push_back(cond_node);
+
+ if (block)
+ {
+ size_t assign_idx = 0;
+ while (assign_idx < block->children.size() && !block->children[assign_idx]->contains(this))
+ assign_idx++;
+ log_assert(assign_idx < block->children.size());
+ block->children.insert(block->children.begin()+assign_idx, case_node);
+ block->children.insert(block->children.begin()+assign_idx, assign_addr);
+ }
+ else
+ {
+ AstNode *proc = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK));
+ proc->children[0]->children.push_back(case_node);
+ mod->children.push_back(proc);
+ mod->children.push_back(assign_addr);
+ }
+
+ delete_children();
+ range_valid = false;
+ id2ast = NULL;
+ str = id_data;
+ }
+
+ if (bit_part_sel)
+ children.push_back(bit_part_sel);
+ }
+
+ log_assert(id2ast == NULL || mem2reg_set.count(id2ast) == 0);
+
+ auto children_list = children;
+ for (size_t i = 0; i < children_list.size(); i++)
+ if (children_list[i]->mem2reg_as_needed_pass2(mem2reg_set, mod, block, async_block))
+ did_something = true;
+
+ return did_something;
+}
+
+// calculate memory dimensions
+void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits)
+{
+ log_assert(type == AST_MEMORY);
+
+ mem_width = children[0]->range_left - children[0]->range_right + 1;
+ mem_size = children[1]->range_left - children[1]->range_right;
+
+ if (mem_size < 0)
+ mem_size *= -1;
+ mem_size += min(children[1]->range_left, children[1]->range_right) + 1;
+
+ addr_bits = 1;
+ while ((1 << addr_bits) < mem_size)
+ addr_bits++;
+}
+
+bool AstNode::has_const_only_constructs(bool &recommend_const_eval)
+{
+ if (type == AST_FOR)
+ recommend_const_eval = true;
+ if (type == AST_WHILE || type == AST_REPEAT)
+ return true;
+ if (type == AST_FCALL && current_scope.count(str))
+ if (current_scope[str]->has_const_only_constructs(recommend_const_eval))
+ return true;
+ for (auto child : children)
+ if (child->AstNode::has_const_only_constructs(recommend_const_eval))
+ return true;
+ return false;
+}
+
+// helper function for AstNode::eval_const_function()
+void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall)
+{
+ if (type == AST_IDENTIFIER && variables.count(str)) {
+ int offset = variables.at(str).offset, width = variables.at(str).val.bits.size();
+ if (!children.empty()) {
+ if (children.size() != 1 || children.at(0)->type != AST_RANGE)
+ log_error("Memory access in constant function is not supported in %s:%d (called from %s:%d).\n",
+ filename.c_str(), 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_error("Non-constant range in %s:%d (called from %s:%d).\n",
+ filename.c_str(), 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);
+ }
+ offset -= variables.at(str).offset;
+ std::vector<RTLIL::State> &var_bits = variables.at(str).val.bits;
+ std::vector<RTLIL::State> new_bits(var_bits.begin() + offset, var_bits.begin() + offset + width);
+ AstNode *newNode = mkconst_bits(new_bits, variables.at(str).is_signed);
+ newNode->cloneInto(this);
+ delete newNode;
+ return;
+ }
+
+ for (auto &child : children)
+ child->replace_variables(variables, fcall);
+}
+
+// evaluate functions with all-const arguments
+AstNode *AstNode::eval_const_function(AstNode *fcall)
+{
+ std::map<std::string, AstNode*> backup_scope;
+ std::map<std::string, AstNode::varinfo_t> variables;
+ bool delete_temp_block = false;
+ AstNode *block = NULL;
+
+ size_t argidx = 0;
+ for (auto child : children)
+ {
+ if (child->type == AST_BLOCK)
+ {
+ log_assert(block == NULL);
+ block = child;
+ continue;
+ }
+
+ if (child->type == AST_WIRE)
+ {
+ while (child->simplify(true, false, false, 1, -1, false, true)) { }
+ if (!child->range_valid)
+ log_error("Can't determine size of variable %s in %s:%d (called from %s:%d).\n",
+ child->str.c_str(), child->filename.c_str(), child->linenum, 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;
+ if (child->is_input && argidx < fcall->children.size())
+ variables[child->str].val = fcall->children.at(argidx++)->bitsAsConst(variables[child->str].val.bits.size());
+ backup_scope[child->str] = current_scope[child->str];
+ current_scope[child->str] = child;
+ continue;
+ }
+
+ log_assert(block == NULL);
+ delete_temp_block = true;
+ block = new AstNode(AST_BLOCK);
+ block->children.push_back(child->clone());
+ }
+
+ log_assert(block != NULL);
+ log_assert(variables.count(str) != 0);
+
+ while (!block->children.empty())
+ {
+ AstNode *stmt = block->children.front();
+
+#if 0
+ log("-----------------------------------\n");
+ for (auto &it : variables)
+ log("%20s %40s\n", it.first.c_str(), log_signal(it.second.val));
+ stmt->dumpAst(NULL, "stmt> ");
+#endif
+
+ if (stmt->type == AST_ASSIGN_EQ)
+ {
+ if (stmt->children.at(0)->type == AST_IDENTIFIER && stmt->children.at(0)->children.size() != 0 &&
+ stmt->children.at(0)->children.at(0)->type == AST_RANGE)
+ stmt->children.at(0)->children.at(0)->replace_variables(variables, fcall);
+ stmt->children.at(1)->replace_variables(variables, fcall);
+ while (stmt->simplify(true, false, false, 1, -1, false, true)) { }
+
+ if (stmt->type != AST_ASSIGN_EQ)
+ continue;
+
+ if (stmt->children.at(1)->type != AST_CONSTANT)
+ log_error("Non-constant expression in constant function at %s:%d (called from %s:%d). X\n",
+ stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+
+ if (stmt->children.at(0)->type != AST_IDENTIFIER)
+ log_error("Unsupported composite left hand side in constant function at %s:%d (called from %s:%d).\n",
+ stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+
+ if (!variables.count(stmt->children.at(0)->str))
+ log_error("Assignment to non-local variable in constant function at %s:%d (called from %s:%d).\n",
+ stmt->filename.c_str(), stmt->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());
+ } else {
+ AstNode *range = stmt->children.at(0)->children.at(0);
+ if (!range->range_valid)
+ log_error("Non-constant range in %s:%d (called from %s:%d).\n",
+ range->filename.c_str(), range->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];
+ RTLIL::Const r = stmt->children.at(1)->bitsAsConst(v.val.bits.size());
+ for (int i = 0; i < width; i++)
+ v.val.bits.at(i+offset-v.offset) = r.bits.at(i);
+ }
+
+ delete block->children.front();
+ block->children.erase(block->children.begin());
+ continue;
+ }
+
+ if (stmt->type == AST_FOR)
+ {
+ block->children.insert(block->children.begin(), stmt->children.at(0));
+ stmt->children.at(3)->children.push_back(stmt->children.at(2));
+ stmt->children.erase(stmt->children.begin() + 2);
+ stmt->children.erase(stmt->children.begin());
+ stmt->type = AST_WHILE;
+ continue;
+ }
+
+ if (stmt->type == AST_WHILE)
+ {
+ AstNode *cond = stmt->children.at(0)->clone();
+ cond->replace_variables(variables, fcall);
+ while (cond->simplify(true, false, false, 1, -1, false, true)) { }
+
+ if (cond->type != AST_CONSTANT)
+ log_error("Non-constant expression in constant function at %s:%d (called from %s:%d).\n",
+ stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+
+ if (cond->asBool()) {
+ block->children.insert(block->children.begin(), stmt->children.at(1)->clone());
+ } else {
+ delete block->children.front();
+ block->children.erase(block->children.begin());
+ }
+
+ delete cond;
+ continue;
+ }
+
+ if (stmt->type == AST_REPEAT)
+ {
+ AstNode *num = stmt->children.at(0)->clone();
+ num->replace_variables(variables, fcall);
+ while (num->simplify(true, false, false, 1, -1, false, true)) { }
+
+ if (num->type != AST_CONSTANT)
+ log_error("Non-constant expression in constant function at %s:%d (called from %s:%d).\n",
+ stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+
+ block->children.erase(block->children.begin());
+ for (int i = 0; i < num->bitsAsConst().as_int(); i++)
+ block->children.insert(block->children.begin(), stmt->children.at(1)->clone());
+
+ delete stmt;
+ delete num;
+ continue;
+ }
+
+ if (stmt->type == AST_CASE)
+ {
+ AstNode *expr = stmt->children.at(0)->clone();
+ expr->replace_variables(variables, fcall);
+ while (expr->simplify(true, false, false, 1, -1, false, true)) { }
+
+ AstNode *sel_case = NULL;
+ for (size_t i = 1; i < stmt->children.size(); i++)
+ {
+ bool found_match = false;
+ log_assert(stmt->children.at(i)->type == AST_COND || stmt->children.at(i)->type == AST_CONDX || stmt->children.at(i)->type == AST_CONDZ);
+
+ if (stmt->children.at(i)->children.front()->type == AST_DEFAULT) {
+ sel_case = stmt->children.at(i)->children.back();
+ continue;
+ }
+
+ for (size_t j = 0; j+1 < stmt->children.at(i)->children.size() && !found_match; j++)
+ {
+ AstNode *cond = stmt->children.at(i)->children.at(j)->clone();
+ cond->replace_variables(variables, fcall);
+
+ cond = new AstNode(AST_EQ, expr->clone(), cond);
+ while (cond->simplify(true, false, false, 1, -1, false, true)) { }
+
+ if (cond->type != AST_CONSTANT)
+ log_error("Non-constant expression in constant function at %s:%d (called from %s:%d).\n",
+ stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+
+ found_match = cond->asBool();
+ delete cond;
+ }
+
+ if (found_match) {
+ sel_case = stmt->children.at(i)->children.back();
+ break;
+ }
+ }
+
+ block->children.erase(block->children.begin());
+ if (sel_case)
+ block->children.insert(block->children.begin(), sel_case->clone());
+ delete stmt;
+ delete expr;
+ continue;
+ }
+
+ if (stmt->type == AST_BLOCK)
+ {
+ block->children.erase(block->children.begin());
+ block->children.insert(block->children.begin(), stmt->children.begin(), stmt->children.end());
+ stmt->children.clear();
+ delete stmt;
+ continue;
+ }
+
+ log_error("Unsupported language construct in constant function at %s:%d (called from %s:%d).\n",
+ stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+ log_abort();
+ }
+
+ if (delete_temp_block)
+ delete block;
+
+ for (auto &it : backup_scope)
+ if (it.second == NULL)
+ current_scope.erase(it.first);
+ else
+ current_scope[it.first] = it.second;
+
+ return AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed);
+}
+
+YOSYS_NAMESPACE_END
+
diff --git a/frontends/blif/Makefile.inc b/frontends/blif/Makefile.inc
new file mode 100644
index 00000000..9729184e
--- /dev/null
+++ b/frontends/blif/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += frontends/blif/blifparse.o
+
diff --git a/frontends/blif/blifparse.cc b/frontends/blif/blifparse.cc
new file mode 100644
index 00000000..6d4d6087
--- /dev/null
+++ b/frontends/blif/blifparse.cc
@@ -0,0 +1,495 @@
+/*
+ * 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 "blifparse.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+static bool read_next_line(char *&buffer, size_t &buffer_size, int &line_count, std::istream &f)
+{
+ string strbuf;
+ int buffer_len = 0;
+ buffer[0] = 0;
+
+ while (1)
+ {
+ buffer_len += strlen(buffer + buffer_len);
+ while (buffer_len > 0 && (buffer[buffer_len-1] == ' ' || buffer[buffer_len-1] == '\t' ||
+ buffer[buffer_len-1] == '\r' || buffer[buffer_len-1] == '\n'))
+ buffer[--buffer_len] = 0;
+
+ if (buffer_size-buffer_len < 4096) {
+ buffer_size *= 2;
+ buffer = (char*)realloc(buffer, buffer_size);
+ }
+
+ if (buffer_len == 0 || buffer[buffer_len-1] == '\\') {
+ if (buffer_len > 0 && buffer[buffer_len-1] == '\\')
+ buffer[--buffer_len] = 0;
+ line_count++;
+ if (!std::getline(f, strbuf))
+ return false;
+ while (buffer_size-buffer_len < strbuf.size()+1) {
+ buffer_size *= 2;
+ buffer = (char*)realloc(buffer, buffer_size);
+ }
+ strcpy(buffer+buffer_len, strbuf.c_str());
+ } else
+ return true;
+ }
+}
+
+void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bool run_clean, bool sop_mode)
+{
+ RTLIL::Module *module = nullptr;
+ RTLIL::Const *lutptr = NULL;
+ RTLIL::Cell *sopcell = NULL;
+ RTLIL::State lut_default_state = RTLIL::State::Sx;
+ int blif_maxnum = 0, sopmode = -1;
+
+ auto blif_wire = [&](const std::string &wire_name) -> Wire*
+ {
+ if (wire_name[0] == '$')
+ {
+ for (int i = 0; i+1 < GetSize(wire_name); i++)
+ {
+ if (wire_name[i] != '$')
+ continue;
+
+ int len = 0;
+ while (i+len+1 < GetSize(wire_name) && '0' <= wire_name[i+len+1] && wire_name[i+len+1] <= '9')
+ len++;
+
+ if (len > 0) {
+ string num_str = wire_name.substr(i+1, len);
+ int num = atoi(num_str.c_str()) & 0x0fffffff;
+ blif_maxnum = std::max(blif_maxnum, num);
+ }
+ }
+ }
+
+ IdString wire_id = RTLIL::escape_id(wire_name);
+ Wire *wire = module->wire(wire_id);
+
+ if (wire == nullptr)
+ wire = module->addWire(wire_id);
+
+ return wire;
+ };
+
+ dict<RTLIL::IdString, RTLIL::Const> *obj_attributes = nullptr;
+ dict<RTLIL::IdString, RTLIL::Const> *obj_parameters = nullptr;
+
+ size_t buffer_size = 4096;
+ char *buffer = (char*)malloc(buffer_size);
+ int line_count = 0;
+
+ while (1)
+ {
+ if (!read_next_line(buffer, buffer_size, line_count, f)) {
+ if (module != nullptr)
+ goto error;
+ free(buffer);
+ return;
+ }
+
+ continue_without_read:
+ if (buffer[0] == '#')
+ continue;
+
+ if (buffer[0] == '.')
+ {
+ if (lutptr) {
+ for (auto &bit : lutptr->bits)
+ if (bit == RTLIL::State::Sx)
+ bit = lut_default_state;
+ lutptr = NULL;
+ lut_default_state = RTLIL::State::Sx;
+ }
+
+ if (sopcell) {
+ sopcell = NULL;
+ sopmode = -1;
+ }
+
+ char *cmd = strtok(buffer, " \t\r\n");
+
+ if (!strcmp(cmd, ".model")) {
+ if (module != nullptr)
+ goto error;
+ module = new RTLIL::Module;
+ module->name = RTLIL::escape_id(strtok(NULL, " \t\r\n"));
+ obj_attributes = &module->attributes;
+ obj_parameters = nullptr;
+ if (design->module(module->name))
+ log_error("Duplicate definition of module %s in line %d!\n", log_id(module->name), line_count);
+ design->add(module);
+ continue;
+ }
+
+ if (module == nullptr)
+ goto error;
+
+ if (!strcmp(cmd, ".end"))
+ {
+ module->fixup_ports();
+
+ if (run_clean)
+ {
+ Const buffer_lut(vector<RTLIL::State>({State::S0, State::S1}));
+ vector<Cell*> remove_cells;
+
+ for (auto cell : module->cells())
+ if (cell->type == "$lut" && cell->getParam("\\LUT") == buffer_lut) {
+ module->connect(cell->getPort("\\Y"), cell->getPort("\\A"));
+ remove_cells.push_back(cell);
+ }
+
+ for (auto cell : remove_cells)
+ module->remove(cell);
+
+ Wire *true_wire = module->wire("$true");
+ Wire *false_wire = module->wire("$false");
+ Wire *undef_wire = module->wire("$undef");
+
+ if (true_wire != nullptr)
+ module->rename(true_wire, stringf("$true$%d", ++blif_maxnum));
+
+ if (false_wire != nullptr)
+ module->rename(false_wire, stringf("$false$%d", ++blif_maxnum));
+
+ if (undef_wire != nullptr)
+ module->rename(undef_wire, stringf("$undef$%d", ++blif_maxnum));
+
+ autoidx = std::max(autoidx, blif_maxnum+1);
+ blif_maxnum = 0;
+ }
+
+ module = nullptr;
+ obj_attributes = nullptr;
+ obj_parameters = nullptr;
+ continue;
+ }
+
+ if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs")) {
+ char *p;
+ while ((p = strtok(NULL, " \t\r\n")) != NULL) {
+ RTLIL::IdString wire_name(stringf("\\%s", p));
+ RTLIL::Wire *wire = module->wire(wire_name);
+ if (wire == nullptr)
+ wire = module->addWire(wire_name);
+ if (!strcmp(cmd, ".inputs"))
+ wire->port_input = true;
+ else
+ wire->port_output = true;
+ }
+ obj_attributes = nullptr;
+ obj_parameters = nullptr;
+ continue;
+ }
+
+ if (!strcmp(cmd, ".attr") || !strcmp(cmd, ".param")) {
+ char *n = strtok(NULL, " \t\r\n");
+ char *v = strtok(NULL, "\r\n");
+ IdString id_n = RTLIL::escape_id(n);
+ Const const_v;
+ if (v[0] == '"') {
+ std::string str(v+1);
+ if (str.back() == '"')
+ str.resize(str.size()-1);
+ const_v = Const(str);
+ } else {
+ int n = strlen(v);
+ const_v.bits.resize(n);
+ for (int i = 0; i < n; i++)
+ const_v.bits[i] = v[n-i-1] != '0' ? State::S1 : State::S0;
+ }
+ if (!strcmp(cmd, ".attr")) {
+ if (obj_attributes == nullptr)
+ goto error;
+ (*obj_attributes)[id_n] = const_v;
+ } else {
+ if (obj_parameters == nullptr)
+ goto error;
+ (*obj_parameters)[id_n] = const_v;
+ }
+ continue;
+ }
+
+ if (!strcmp(cmd, ".latch"))
+ {
+ char *d = strtok(NULL, " \t\r\n");
+ char *q = strtok(NULL, " \t\r\n");
+ char *edge = strtok(NULL, " \t\r\n");
+ char *clock = strtok(NULL, " \t\r\n");
+ char *init = strtok(NULL, " \t\r\n");
+ RTLIL::Cell *cell = nullptr;
+
+ if (clock == nullptr && edge != nullptr) {
+ init = edge;
+ edge = nullptr;
+ }
+
+ if (init != nullptr && (init[0] == '0' || init[0] == '1'))
+ blif_wire(q)->attributes["\\init"] = Const(init[0] == '1' ? 1 : 0, 1);
+
+ if (clock == nullptr)
+ goto no_latch_clock;
+
+ if (!strcmp(edge, "re"))
+ cell = module->addDff(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q));
+ else if (!strcmp(edge, "fe"))
+ cell = module->addDff(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q), false);
+ else if (!strcmp(edge, "ah"))
+ cell = module->addDlatch(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q));
+ else if (!strcmp(edge, "al"))
+ cell = module->addDlatch(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q), false);
+ else {
+ no_latch_clock:
+ if (dff_name.empty()) {
+ cell = module->addFf(NEW_ID, blif_wire(d), blif_wire(q));
+ } else {
+ cell = module->addCell(NEW_ID, dff_name);
+ cell->setPort("\\D", blif_wire(d));
+ cell->setPort("\\Q", blif_wire(q));
+ }
+ }
+
+ obj_attributes = &cell->attributes;
+ obj_parameters = &cell->parameters;
+ continue;
+ }
+
+ if (!strcmp(cmd, ".gate") || !strcmp(cmd, ".subckt"))
+ {
+ char *p = strtok(NULL, " \t\r\n");
+ if (p == NULL)
+ goto error;
+
+ IdString celltype = RTLIL::escape_id(p);
+ RTLIL::Cell *cell = module->addCell(NEW_ID, celltype);
+
+ while ((p = strtok(NULL, " \t\r\n")) != NULL) {
+ char *q = strchr(p, '=');
+ if (q == NULL || !q[0])
+ goto error;
+ *(q++) = 0;
+ cell->setPort(RTLIL::escape_id(p), *q ? blif_wire(q) : SigSpec());
+ }
+
+ obj_attributes = &cell->attributes;
+ obj_parameters = &cell->parameters;
+ continue;
+ }
+
+ obj_attributes = nullptr;
+ obj_parameters = nullptr;
+
+ if (!strcmp(cmd, ".barbuf"))
+ {
+ char *p = strtok(NULL, " \t\r\n");
+ if (p == NULL)
+ goto error;
+
+ char *q = strtok(NULL, " \t\r\n");
+ if (q == NULL)
+ goto error;
+
+ module->connect(blif_wire(q), blif_wire(p));
+ continue;
+ }
+
+ if (!strcmp(cmd, ".names"))
+ {
+ char *p;
+ RTLIL::SigSpec input_sig, output_sig;
+ while ((p = strtok(NULL, " \t\r\n")) != NULL)
+ input_sig.append(blif_wire(p));
+ output_sig = input_sig.extract(input_sig.size()-1, 1);
+ input_sig = input_sig.extract(0, input_sig.size()-1);
+
+ if (input_sig.size() == 0)
+ {
+ RTLIL::State state = RTLIL::State::Sa;
+ while (1) {
+ if (!read_next_line(buffer, buffer_size, line_count, f))
+ goto error;
+ for (int i = 0; buffer[i]; i++) {
+ if (buffer[i] == ' ' || buffer[i] == '\t')
+ continue;
+ if (i == 0 && buffer[i] == '.')
+ goto finished_parsing_constval;
+ if (buffer[i] == '0') {
+ if (state == RTLIL::State::S1)
+ goto error;
+ state = RTLIL::State::S0;
+ continue;
+ }
+ if (buffer[i] == '1') {
+ if (state == RTLIL::State::S0)
+ goto error;
+ state = RTLIL::State::S1;
+ continue;
+ }
+ goto error;
+ }
+ }
+
+ finished_parsing_constval:
+ if (state == RTLIL::State::Sa)
+ state = RTLIL::State::S0;
+ if (output_sig.as_wire()->name == "$undef")
+ state = RTLIL::State::Sx;
+ module->connect(RTLIL::SigSig(output_sig, state));
+ goto continue_without_read;
+ }
+
+ if (sop_mode)
+ {
+ sopcell = module->addCell(NEW_ID, "$sop");
+ sopcell->parameters["\\WIDTH"] = RTLIL::Const(input_sig.size());
+ sopcell->parameters["\\DEPTH"] = 0;
+ sopcell->parameters["\\TABLE"] = RTLIL::Const();
+ sopcell->setPort("\\A", input_sig);
+ sopcell->setPort("\\Y", output_sig);
+ sopmode = -1;
+ }
+ else
+ {
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$lut");
+ cell->parameters["\\WIDTH"] = RTLIL::Const(input_sig.size());
+ cell->parameters["\\LUT"] = RTLIL::Const(RTLIL::State::Sx, 1 << input_sig.size());
+ cell->setPort("\\A", input_sig);
+ cell->setPort("\\Y", output_sig);
+ lutptr = &cell->parameters.at("\\LUT");
+ lut_default_state = RTLIL::State::Sx;
+ }
+ continue;
+ }
+
+ goto error;
+ }
+
+ if (lutptr == NULL && sopcell == NULL)
+ goto error;
+
+ char *input = strtok(buffer, " \t\r\n");
+ char *output = strtok(NULL, " \t\r\n");
+
+ if (input == NULL || output == NULL || (strcmp(output, "0") && strcmp(output, "1")))
+ goto error;
+
+ int input_len = strlen(input);
+
+ if (sopcell)
+ {
+ log_assert(sopcell->parameters["\\WIDTH"].as_int() == input_len);
+ sopcell->parameters["\\DEPTH"] = sopcell->parameters["\\DEPTH"].as_int() + 1;
+
+ for (int i = 0; i < input_len; i++)
+ switch (input[i]) {
+ case '0':
+ sopcell->parameters["\\TABLE"].bits.push_back(State::S1);
+ sopcell->parameters["\\TABLE"].bits.push_back(State::S0);
+ break;
+ case '1':
+ sopcell->parameters["\\TABLE"].bits.push_back(State::S0);
+ sopcell->parameters["\\TABLE"].bits.push_back(State::S1);
+ break;
+ default:
+ sopcell->parameters["\\TABLE"].bits.push_back(State::S0);
+ sopcell->parameters["\\TABLE"].bits.push_back(State::S0);
+ break;
+ }
+
+ if (sopmode == -1) {
+ sopmode = (*output == '1');
+ if (!sopmode) {
+ SigSpec outnet = sopcell->getPort("\\Y");
+ SigSpec tempnet = module->addWire(NEW_ID);
+ module->addNotGate(NEW_ID, tempnet, outnet);
+ sopcell->setPort("\\Y", tempnet);
+ }
+ } else
+ log_assert(sopmode == (*output == '1'));
+ }
+
+ if (lutptr)
+ {
+ if (input_len > 8)
+ goto error;
+
+ for (int i = 0; i < (1 << input_len); i++) {
+ for (int j = 0; j < input_len; j++) {
+ char c1 = input[j];
+ if (c1 != '-') {
+ char c2 = (i & (1 << j)) != 0 ? '1' : '0';
+ if (c1 != c2)
+ goto try_next_value;
+ }
+ }
+ lutptr->bits.at(i) = !strcmp(output, "0") ? RTLIL::State::S0 : RTLIL::State::S1;
+ try_next_value:;
+ }
+
+ lut_default_state = !strcmp(output, "0") ? RTLIL::State::S1 : RTLIL::State::S0;
+ }
+ }
+
+error:
+ log_error("Syntax error in line %d!\n", line_count);
+}
+
+struct BlifFrontend : public Frontend {
+ BlifFrontend() : Frontend("blif", "read BLIF file") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" read_blif [filename]\n");
+ log("\n");
+ log("Load modules from a BLIF file into the current design.\n");
+ log("\n");
+ log(" -sop\n");
+ log(" Create $sop cells instead of $lut cells\n");
+ log("\n");
+ }
+ virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool sop_mode = false;
+
+ log_header(design, "Executing BLIF frontend.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-sop") {
+ sop_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ parse_blif(design, *f, "", true, sop_mode);
+ }
+} BlifFrontend;
+
+YOSYS_NAMESPACE_END
+
diff --git a/frontends/blif/blifparse.h b/frontends/blif/blifparse.h
new file mode 100644
index 00000000..058087d8
--- /dev/null
+++ b/frontends/blif/blifparse.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef ABC_BLIFPARSE
+#define ABC_BLIFPARSE
+
+#include "kernel/yosys.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+extern void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bool run_clean = false, bool sop_mode = false);
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/frontends/ilang/.gitignore b/frontends/ilang/.gitignore
new file mode 100644
index 00000000..43106a81
--- /dev/null
+++ b/frontends/ilang/.gitignore
@@ -0,0 +1,4 @@
+ilang_lexer.cc
+ilang_parser.output
+ilang_parser.tab.cc
+ilang_parser.tab.h
diff --git a/frontends/ilang/Makefile.inc b/frontends/ilang/Makefile.inc
new file mode 100644
index 00000000..e2a476c9
--- /dev/null
+++ b/frontends/ilang/Makefile.inc
@@ -0,0 +1,20 @@
+
+GENFILES += frontends/ilang/ilang_parser.tab.cc
+GENFILES += frontends/ilang/ilang_parser.tab.h
+GENFILES += frontends/ilang/ilang_parser.output
+GENFILES += frontends/ilang/ilang_lexer.cc
+
+frontends/ilang/ilang_parser.tab.cc: frontends/ilang/ilang_parser.y
+ $(Q) mkdir -p $(dir $@)
+ $(P) $(BISON) -d -r all -b frontends/ilang/ilang_parser $<
+ $(Q) mv frontends/ilang/ilang_parser.tab.c frontends/ilang/ilang_parser.tab.cc
+
+frontends/ilang/ilang_parser.tab.h: frontends/ilang/ilang_parser.tab.cc
+
+frontends/ilang/ilang_lexer.cc: frontends/ilang/ilang_lexer.l
+ $(Q) mkdir -p $(dir $@)
+ $(P) flex -o frontends/ilang/ilang_lexer.cc $<
+
+OBJS += frontends/ilang/ilang_parser.tab.o frontends/ilang/ilang_lexer.o
+OBJS += frontends/ilang/ilang_frontend.o
+
diff --git a/frontends/ilang/ilang_frontend.cc b/frontends/ilang/ilang_frontend.cc
new file mode 100644
index 00000000..ed678998
--- /dev/null
+++ b/frontends/ilang/ilang_frontend.cc
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * A very simple and straightforward frontend for the RTLIL text
+ * representation (as generated by the 'ilang' backend).
+ *
+ */
+
+#include "ilang_frontend.h"
+#include "kernel/register.h"
+#include "kernel/log.h"
+
+void rtlil_frontend_ilang_yyerror(char const *s)
+{
+ YOSYS_NAMESPACE_PREFIX log_error("Parser error in line %d: %s\n", rtlil_frontend_ilang_yyget_lineno(), s);
+}
+
+YOSYS_NAMESPACE_BEGIN
+
+struct IlangFrontend : public Frontend {
+ IlangFrontend() : Frontend("ilang", "read modules from ilang file") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" read_ilang [filename]\n");
+ log("\n");
+ log("Load modules from an ilang file to the current design. (ilang is a text\n");
+ log("representation of a design in yosys's internal format.)\n");
+ log("\n");
+ }
+ virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing ILANG frontend.\n");
+ extra_args(f, filename, args, 1);
+ log("Input filename: %s\n", filename.c_str());
+
+ ILANG_FRONTEND::lexin = f;
+ ILANG_FRONTEND::current_design = design;
+ rtlil_frontend_ilang_yydebug = false;
+ rtlil_frontend_ilang_yyrestart(NULL);
+ rtlil_frontend_ilang_yyparse();
+ rtlil_frontend_ilang_yylex_destroy();
+ }
+} IlangFrontend;
+
+YOSYS_NAMESPACE_END
+
diff --git a/frontends/ilang/ilang_frontend.h b/frontends/ilang/ilang_frontend.h
new file mode 100644
index 00000000..ad3ffec9
--- /dev/null
+++ b/frontends/ilang/ilang_frontend.h
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * A very simple and straightforward frontend for the RTLIL text
+ * representation (as generated by the 'ilang' backend).
+ *
+ */
+
+#ifndef ILANG_FRONTEND_H
+#define ILANG_FRONTEND_H
+
+#include "kernel/yosys.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+namespace ILANG_FRONTEND {
+ extern std::istream *lexin;
+ extern RTLIL::Design *current_design;
+}
+
+YOSYS_NAMESPACE_END
+
+extern int rtlil_frontend_ilang_yydebug;
+int rtlil_frontend_ilang_yylex(void);
+void rtlil_frontend_ilang_yyerror(char const *s);
+void rtlil_frontend_ilang_yyrestart(FILE *f);
+int rtlil_frontend_ilang_yyparse(void);
+int rtlil_frontend_ilang_yylex_destroy(void);
+int rtlil_frontend_ilang_yyget_lineno(void);
+
+#endif
+
diff --git a/frontends/ilang/ilang_lexer.l b/frontends/ilang/ilang_lexer.l
new file mode 100644
index 00000000..84238854
--- /dev/null
+++ b/frontends/ilang/ilang_lexer.l
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * A very simple and straightforward frontend for the RTLIL text
+ * representation (as generated by the 'ilang' backend).
+ *
+ */
+
+%{
+
+#ifdef __clang__
+// bison generates code using the 'register' storage class specifier
+#pragma clang diagnostic ignored "-Wdeprecated-register"
+#endif
+
+#include "frontends/ilang/ilang_frontend.h"
+#include "ilang_parser.tab.h"
+
+USING_YOSYS_NAMESPACE
+
+#define YY_INPUT(buf,result,max_size) \
+ result = readsome(*ILANG_FRONTEND::lexin, buf, max_size)
+
+%}
+
+%option yylineno
+%option noyywrap
+%option nounput
+%option prefix="rtlil_frontend_ilang_yy"
+
+%x STRING
+
+%%
+
+"autoidx" { return TOK_AUTOIDX; }
+"module" { return TOK_MODULE; }
+"attribute" { return TOK_ATTRIBUTE; }
+"parameter" { return TOK_PARAMETER; }
+"signed" { return TOK_SIGNED; }
+"wire" { return TOK_WIRE; }
+"memory" { return TOK_MEMORY; }
+"width" { return TOK_WIDTH; }
+"upto" { return TOK_UPTO; }
+"offset" { return TOK_OFFSET; }
+"size" { return TOK_SIZE; }
+"input" { return TOK_INPUT; }
+"output" { return TOK_OUTPUT; }
+"inout" { return TOK_INOUT; }
+"cell" { return TOK_CELL; }
+"connect" { return TOK_CONNECT; }
+"switch" { return TOK_SWITCH; }
+"case" { return TOK_CASE; }
+"assign" { return TOK_ASSIGN; }
+"sync" { return TOK_SYNC; }
+"low" { return TOK_LOW; }
+"high" { return TOK_HIGH; }
+"posedge" { return TOK_POSEDGE; }
+"negedge" { return TOK_NEGEDGE; }
+"edge" { return TOK_EDGE; }
+"always" { return TOK_ALWAYS; }
+"global" { return TOK_GLOBAL; }
+"init" { return TOK_INIT; }
+"update" { return TOK_UPDATE; }
+"process" { return TOK_PROCESS; }
+"end" { return TOK_END; }
+
+[a-z]+ { return TOK_INVALID; }
+
+"\\"[^ \t\r\n]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; }
+"$"[^ \t\r\n]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; }
+"."[0-9]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; }
+
+[0-9]+'[01xzm-]* { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_VALUE; }
+-?[0-9]+ { rtlil_frontend_ilang_yylval.integer = atoi(yytext); return TOK_INT; }
+
+\" { BEGIN(STRING); }
+<STRING>\\. { yymore(); }
+<STRING>\" {
+ BEGIN(0);
+ char *yystr = strdup(yytext);
+ yystr[strlen(yytext) - 1] = 0;
+ int i = 0, j = 0;
+ while (yystr[i]) {
+ if (yystr[i] == '\\' && yystr[i + 1]) {
+ i++;
+ if (yystr[i] == 'n')
+ yystr[i] = '\n';
+ else if (yystr[i] == 't')
+ yystr[i] = '\t';
+ else if ('0' <= yystr[i] && yystr[i] <= '7') {
+ yystr[i] = yystr[i] - '0';
+ if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') {
+ yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0';
+ i++;
+ }
+ if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') {
+ yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0';
+ i++;
+ }
+ }
+ }
+ yystr[j++] = yystr[i++];
+ }
+ yystr[j] = 0;
+ rtlil_frontend_ilang_yylval.string = yystr;
+ return TOK_STRING;
+}
+<STRING>. { yymore(); }
+
+"#"[^\n]* /* ignore comments */
+[ \t] /* ignore non-newline whitespaces */
+[\r\n]+ { return TOK_EOL; }
+
+. { return *yytext; }
+
+%%
+
+// this is a hack to avoid the 'yyinput defined but not used' error msgs
+void *rtlil_frontend_ilang_avoid_input_warnings() {
+ return (void*)&yyinput;
+}
+
diff --git a/frontends/ilang/ilang_parser.y b/frontends/ilang/ilang_parser.y
new file mode 100644
index 00000000..bfc062fe
--- /dev/null
+++ b/frontends/ilang/ilang_parser.y
@@ -0,0 +1,430 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * A very simple and straightforward frontend for the RTLIL text
+ * representation (as generated by the 'ilang' backend).
+ *
+ */
+
+%{
+#include <list>
+#include "frontends/ilang/ilang_frontend.h"
+YOSYS_NAMESPACE_BEGIN
+namespace ILANG_FRONTEND {
+ std::istream *lexin;
+ RTLIL::Design *current_design;
+ RTLIL::Module *current_module;
+ RTLIL::Wire *current_wire;
+ RTLIL::Memory *current_memory;
+ RTLIL::Cell *current_cell;
+ RTLIL::Process *current_process;
+ std::vector<std::vector<RTLIL::SwitchRule*>*> switch_stack;
+ std::vector<RTLIL::CaseRule*> case_stack;
+ dict<RTLIL::IdString, RTLIL::Const> attrbuf;
+}
+using namespace ILANG_FRONTEND;
+YOSYS_NAMESPACE_END
+USING_YOSYS_NAMESPACE
+%}
+
+%name-prefix "rtlil_frontend_ilang_yy"
+
+%union {
+ char *string;
+ int integer;
+ YOSYS_NAMESPACE_PREFIX RTLIL::Const *data;
+ YOSYS_NAMESPACE_PREFIX RTLIL::SigSpec *sigspec;
+ std::vector<YOSYS_NAMESPACE_PREFIX RTLIL::SigSpec> *rsigspec;
+}
+
+%token <string> TOK_ID TOK_VALUE TOK_STRING
+%token <integer> TOK_INT
+%token TOK_AUTOIDX TOK_MODULE TOK_WIRE TOK_WIDTH TOK_INPUT TOK_OUTPUT TOK_INOUT
+%token TOK_CELL TOK_CONNECT TOK_SWITCH TOK_CASE TOK_ASSIGN TOK_SYNC
+%token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS TOK_GLOBAL TOK_INIT
+%token TOK_UPDATE TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET
+%token TOK_PARAMETER TOK_ATTRIBUTE TOK_MEMORY TOK_SIZE TOK_SIGNED TOK_UPTO
+
+%type <rsigspec> sigspec_list_reversed
+%type <sigspec> sigspec sigspec_list
+%type <integer> sync_type
+%type <data> constant
+
+%expect 0
+%debug
+
+%%
+
+input:
+ optional_eol {
+ attrbuf.clear();
+ } design {
+ if (attrbuf.size() != 0)
+ rtlil_frontend_ilang_yyerror("dangling attribute");
+ };
+
+EOL:
+ optional_eol TOK_EOL;
+
+optional_eol:
+ optional_eol TOK_EOL | /* empty */;
+
+design:
+ design module |
+ design attr_stmt |
+ design autoidx_stmt |
+ /* empty */;
+
+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());
+ current_module = new RTLIL::Module;
+ current_module->name = $2;
+ current_module->attributes = attrbuf;
+ 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();
+ } EOL;
+
+module_body:
+ module_body module_stmt |
+ /* empty */;
+
+module_stmt:
+ param_stmt | attr_stmt | wire_stmt | memory_stmt | cell_stmt | proc_stmt | conn_stmt;
+
+param_stmt:
+ TOK_PARAMETER TOK_ID EOL {
+ current_module->avail_parameters.insert($2);
+ free($2);
+ };
+
+attr_stmt:
+ TOK_ATTRIBUTE TOK_ID constant EOL {
+ attrbuf[$2] = *$3;
+ delete $3;
+ free($2);
+ };
+
+autoidx_stmt:
+ TOK_AUTOIDX TOK_INT EOL {
+ autoidx = max(autoidx, $2);
+ };
+
+wire_stmt:
+ TOK_WIRE {
+ current_wire = current_module->addWire("$__ilang_frontend_tmp__");
+ current_wire->attributes = attrbuf;
+ attrbuf.clear();
+ } wire_options TOK_ID EOL {
+ if (current_module->wires_.count($4) != 0)
+ rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of wire %s.", $4).c_str());
+ current_module->rename(current_wire, $4);
+ free($4);
+ };
+
+wire_options:
+ wire_options TOK_WIDTH TOK_INT {
+ current_wire->width = $3;
+ } |
+ wire_options TOK_UPTO {
+ current_wire->upto = true;
+ } |
+ wire_options TOK_OFFSET TOK_INT {
+ current_wire->start_offset = $3;
+ } |
+ wire_options TOK_INPUT TOK_INT {
+ current_wire->port_id = $3;
+ current_wire->port_input = true;
+ current_wire->port_output = false;
+ } |
+ wire_options TOK_OUTPUT TOK_INT {
+ current_wire->port_id = $3;
+ current_wire->port_input = false;
+ current_wire->port_output = true;
+ } |
+ wire_options TOK_INOUT TOK_INT {
+ current_wire->port_id = $3;
+ current_wire->port_input = true;
+ current_wire->port_output = true;
+ } |
+ /* empty */;
+
+memory_stmt:
+ TOK_MEMORY {
+ current_memory = new RTLIL::Memory;
+ current_memory->attributes = attrbuf;
+ attrbuf.clear();
+ } memory_options TOK_ID EOL {
+ if (current_module->memories.count($4) != 0)
+ rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of memory %s.", $4).c_str());
+ current_memory->name = $4;
+ current_module->memories[$4] = current_memory;
+ free($4);
+ };
+
+memory_options:
+ memory_options TOK_WIDTH TOK_INT {
+ current_memory->width = $3;
+ } |
+ memory_options TOK_SIZE TOK_INT {
+ current_memory->size = $3;
+ } |
+ memory_options TOK_OFFSET TOK_INT {
+ current_memory->start_offset = $3;
+ } |
+ /* empty */;
+
+cell_stmt:
+ TOK_CELL TOK_ID TOK_ID EOL {
+ if (current_module->cells_.count($3) != 0)
+ rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of cell %s.", $3).c_str());
+ current_cell = current_module->addCell($3, $2);
+ current_cell->attributes = attrbuf;
+ attrbuf.clear();
+ free($2);
+ free($3);
+ } cell_body TOK_END EOL;
+
+cell_body:
+ cell_body TOK_PARAMETER TOK_ID constant EOL {
+ current_cell->parameters[$3] = *$4;
+ free($3);
+ delete $4;
+ } |
+ cell_body TOK_PARAMETER TOK_SIGNED TOK_ID constant EOL {
+ current_cell->parameters[$4] = *$5;
+ current_cell->parameters[$4].flags |= RTLIL::CONST_FLAG_SIGNED;
+ free($4);
+ delete $5;
+ } |
+ cell_body TOK_CONNECT TOK_ID sigspec EOL {
+ if (current_cell->hasPort($3))
+ rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of cell port %s.", $3).c_str());
+ current_cell->setPort($3, *$4);
+ delete $4;
+ free($3);
+ } |
+ /* empty */;
+
+proc_stmt:
+ TOK_PROCESS TOK_ID EOL {
+ if (current_module->processes.count($2) != 0)
+ rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of process %s.", $2).c_str());
+ current_process = new RTLIL::Process;
+ current_process->name = $2;
+ current_process->attributes = attrbuf;
+ current_module->processes[$2] = current_process;
+ switch_stack.clear();
+ switch_stack.push_back(&current_process->root_case.switches);
+ case_stack.clear();
+ case_stack.push_back(&current_process->root_case);
+ attrbuf.clear();
+ free($2);
+ } case_body sync_list TOK_END EOL;
+
+switch_stmt:
+ attr_list TOK_SWITCH sigspec EOL {
+ RTLIL::SwitchRule *rule = new RTLIL::SwitchRule;
+ rule->signal = *$3;
+ rule->attributes = attrbuf;
+ switch_stack.back()->push_back(rule);
+ attrbuf.clear();
+ delete $3;
+ } switch_body TOK_END EOL;
+
+attr_list:
+ /* empty */ |
+ attr_list attr_stmt;
+
+switch_body:
+ switch_body TOK_CASE {
+ RTLIL::CaseRule *rule = new RTLIL::CaseRule;
+ switch_stack.back()->back()->cases.push_back(rule);
+ switch_stack.push_back(&rule->switches);
+ case_stack.push_back(rule);
+ } compare_list EOL case_body {
+ switch_stack.pop_back();
+ case_stack.pop_back();
+ } |
+ /* empty */;
+
+compare_list:
+ sigspec {
+ case_stack.back()->compare.push_back(*$1);
+ delete $1;
+ } |
+ compare_list ',' sigspec {
+ case_stack.back()->compare.push_back(*$3);
+ delete $3;
+ } |
+ /* empty */;
+
+case_body:
+ case_body switch_stmt |
+ case_body assign_stmt |
+ /* empty */;
+
+assign_stmt:
+ TOK_ASSIGN sigspec sigspec EOL {
+ case_stack.back()->actions.push_back(RTLIL::SigSig(*$2, *$3));
+ delete $2;
+ delete $3;
+ };
+
+sync_list:
+ sync_list TOK_SYNC sync_type sigspec EOL {
+ RTLIL::SyncRule *rule = new RTLIL::SyncRule;
+ rule->type = RTLIL::SyncType($3);
+ rule->signal = *$4;
+ current_process->syncs.push_back(rule);
+ delete $4;
+ } update_list |
+ sync_list TOK_SYNC TOK_ALWAYS EOL {
+ RTLIL::SyncRule *rule = new RTLIL::SyncRule;
+ rule->type = RTLIL::SyncType::STa;
+ rule->signal = RTLIL::SigSpec();
+ current_process->syncs.push_back(rule);
+ } update_list |
+ sync_list TOK_SYNC TOK_GLOBAL EOL {
+ RTLIL::SyncRule *rule = new RTLIL::SyncRule;
+ rule->type = RTLIL::SyncType::STg;
+ rule->signal = RTLIL::SigSpec();
+ current_process->syncs.push_back(rule);
+ } update_list |
+ sync_list TOK_SYNC TOK_INIT EOL {
+ RTLIL::SyncRule *rule = new RTLIL::SyncRule;
+ rule->type = RTLIL::SyncType::STi;
+ rule->signal = RTLIL::SigSpec();
+ current_process->syncs.push_back(rule);
+ } update_list |
+ /* empty */;
+
+sync_type:
+ TOK_LOW { $$ = RTLIL::ST0; } |
+ TOK_HIGH { $$ = RTLIL::ST1; } |
+ TOK_POSEDGE { $$ = RTLIL::STp; } |
+ TOK_NEGEDGE { $$ = RTLIL::STn; } |
+ TOK_EDGE { $$ = RTLIL::STe; };
+
+update_list:
+ update_list TOK_UPDATE sigspec sigspec EOL {
+ current_process->syncs.back()->actions.push_back(RTLIL::SigSig(*$3, *$4));
+ delete $3;
+ delete $4;
+ } |
+ /* empty */;
+
+constant:
+ TOK_VALUE {
+ char *ep;
+ int width = strtol($1, &ep, 10);
+ std::list<RTLIL::State> bits;
+ while (*(++ep) != 0) {
+ RTLIL::State bit = RTLIL::Sx;
+ switch (*ep) {
+ case '0': bit = RTLIL::S0; break;
+ case '1': bit = RTLIL::S1; break;
+ case 'x': bit = RTLIL::Sx; break;
+ case 'z': bit = RTLIL::Sz; break;
+ case '-': bit = RTLIL::Sa; break;
+ case 'm': bit = RTLIL::Sm; break;
+ }
+ bits.push_front(bit);
+ }
+ if (bits.size() == 0)
+ bits.push_back(RTLIL::Sx);
+ while ((int)bits.size() < width) {
+ RTLIL::State bit = bits.back();
+ if (bit == RTLIL::S1)
+ bit = RTLIL::S0;
+ bits.push_back(bit);
+ }
+ while ((int)bits.size() > width)
+ bits.pop_back();
+ $$ = new RTLIL::Const;
+ for (auto it = bits.begin(); it != bits.end(); it++)
+ $$->bits.push_back(*it);
+ free($1);
+ } |
+ TOK_INT {
+ $$ = new RTLIL::Const($1, 32);
+ } |
+ TOK_STRING {
+ $$ = new RTLIL::Const($1);
+ free($1);
+ };
+
+sigspec:
+ constant {
+ $$ = new RTLIL::SigSpec(*$1);
+ delete $1;
+ } |
+ TOK_ID {
+ 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]);
+ 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);
+ } |
+ 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_list '}' {
+ $$ = $2;
+ };
+
+sigspec_list_reversed:
+ sigspec_list_reversed sigspec {
+ $$->push_back(*$2);
+ delete $2;
+ } |
+ /* empty */ {
+ $$ = new std::vector<RTLIL::SigSpec>;
+ };
+
+sigspec_list: sigspec_list_reversed {
+ $$ = new RTLIL::SigSpec;
+ for (auto it = $1->rbegin(); it != $1->rend(); it++)
+ $$->append(*it);
+ delete $1;
+ };
+
+conn_stmt:
+ TOK_CONNECT sigspec sigspec EOL {
+ if (attrbuf.size() != 0)
+ rtlil_frontend_ilang_yyerror("dangling attribute");
+ current_module->connect(*$2, *$3);
+ delete $2;
+ delete $3;
+ };
+
diff --git a/frontends/liberty/Makefile.inc b/frontends/liberty/Makefile.inc
new file mode 100644
index 00000000..a02ef5e4
--- /dev/null
+++ b/frontends/liberty/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += frontends/liberty/liberty.o
+
diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc
new file mode 100644
index 00000000..4666c818
--- /dev/null
+++ b/frontends/liberty/liberty.cc
@@ -0,0 +1,660 @@
+/*
+ * 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 "passes/techmap/libparse.h"
+#include "kernel/register.h"
+#include "kernel/log.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+struct token_t {
+ char type;
+ RTLIL::SigSpec sig;
+ token_t (char t) : type(t) { }
+ token_t (char t, RTLIL::SigSpec s) : type(t), sig(s) { }
+};
+
+static RTLIL::SigSpec parse_func_identifier(RTLIL::Module *module, const char *&expr)
+{
+ log_assert(*expr != 0);
+
+ 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++;
+
+ if (id_len == 0)
+ log_error("Expected identifier at `%s'.\n", expr);
+
+ if (id_len == 1 && (*expr == '0' || *expr == '1'))
+ return *(expr++) == '0' ? RTLIL::State::S0 : RTLIL::State::S1;
+
+ std::string id = RTLIL::escape_id(std::string(expr, id_len));
+ if (!module->wires_.count(id))
+ log_error("Can't resolve wire name %s.\n", RTLIL::unescape_id(id).c_str());
+
+ expr += id_len;
+ return module->wires_.at(id);
+}
+
+static RTLIL::SigSpec create_inv_cell(RTLIL::Module *module, RTLIL::SigSpec A)
+{
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$_NOT_");
+ cell->setPort("\\A", A);
+ cell->setPort("\\Y", module->addWire(NEW_ID));
+ return cell->getPort("\\Y");
+}
+
+static RTLIL::SigSpec create_xor_cell(RTLIL::Module *module, RTLIL::SigSpec A, RTLIL::SigSpec B)
+{
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$_XOR_");
+ cell->setPort("\\A", A);
+ cell->setPort("\\B", B);
+ cell->setPort("\\Y", module->addWire(NEW_ID));
+ return cell->getPort("\\Y");
+}
+
+static RTLIL::SigSpec create_and_cell(RTLIL::Module *module, RTLIL::SigSpec A, RTLIL::SigSpec B)
+{
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$_AND_");
+ cell->setPort("\\A", A);
+ cell->setPort("\\B", B);
+ cell->setPort("\\Y", module->addWire(NEW_ID));
+ return cell->getPort("\\Y");
+}
+
+static RTLIL::SigSpec create_or_cell(RTLIL::Module *module, RTLIL::SigSpec A, RTLIL::SigSpec B)
+{
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$_OR_");
+ cell->setPort("\\A", A);
+ cell->setPort("\\B", B);
+ cell->setPort("\\Y", module->addWire(NEW_ID));
+ return cell->getPort("\\Y");
+}
+
+static bool parse_func_reduce(RTLIL::Module *module, std::vector<token_t> &stack, token_t next_token)
+{
+ int top = int(stack.size())-1;
+
+ if (0 <= top-1 && stack[top].type == 0 && stack[top-1].type == '!') {
+ token_t t = token_t(0, create_inv_cell(module, stack[top].sig));
+ stack.pop_back();
+ stack.pop_back();
+ stack.push_back(t);
+ return true;
+ }
+
+ if (0 <= top-1 && stack[top].type == '\'' && stack[top-1].type == 0) {
+ token_t t = token_t(0, create_inv_cell(module, stack[top-1].sig));
+ stack.pop_back();
+ stack.pop_back();
+ stack.push_back(t);
+ return true;
+ }
+
+ if (0 <= top && stack[top].type == 0) {
+ if (next_token.type == '\'')
+ return false;
+ stack[top].type = 1;
+ return true;
+ }
+
+ if (0 <= top-2 && stack[top-2].type == 1 && stack[top-1].type == '^' && stack[top].type == 1) {
+ token_t t = token_t(1, create_xor_cell(module, stack[top-2].sig, stack[top].sig));
+ stack.pop_back();
+ stack.pop_back();
+ stack.pop_back();
+ stack.push_back(t);
+ return true;
+ }
+
+ if (0 <= top && stack[top].type == 1) {
+ if (next_token.type == '^')
+ return false;
+ stack[top].type = 2;
+ return true;
+ }
+
+ if (0 <= top-1 && stack[top-1].type == 2 && stack[top].type == 2) {
+ token_t t = token_t(2, create_and_cell(module, stack[top-1].sig, stack[top].sig));
+ stack.pop_back();
+ stack.pop_back();
+ stack.push_back(t);
+ return true;
+ }
+
+ if (0 <= top-2 && stack[top-2].type == 2 && (stack[top-1].type == '*' || stack[top-1].type == '&') && stack[top].type == 2) {
+ token_t t = token_t(2, create_and_cell(module, stack[top-2].sig, stack[top].sig));
+ stack.pop_back();
+ stack.pop_back();
+ stack.pop_back();
+ stack.push_back(t);
+ return true;
+ }
+
+ if (0 <= top && stack[top].type == 2) {
+ if (next_token.type == '*' || next_token.type == '&' || next_token.type == 0 || next_token.type == '(')
+ return false;
+ stack[top].type = 3;
+ return true;
+ }
+
+ if (0 <= top-2 && stack[top-2].type == 3 && (stack[top-1].type == '+' || stack[top-1].type == '|') && stack[top].type == 3) {
+ token_t t = token_t(3, create_or_cell(module, stack[top-2].sig, stack[top].sig));
+ stack.pop_back();
+ stack.pop_back();
+ stack.pop_back();
+ stack.push_back(t);
+ return true;
+ }
+
+ if (0 <= top-2 && stack[top-2].type == '(' && stack[top-1].type == 3 && stack[top].type == ')') {
+ token_t t = token_t(0, stack[top-1].sig);
+ stack.pop_back();
+ stack.pop_back();
+ stack.pop_back();
+ stack.push_back(t);
+ return true;
+ }
+
+ return false;
+}
+
+static RTLIL::SigSpec parse_func_expr(RTLIL::Module *module, const char *expr)
+{
+ const char *orig_expr = expr;
+ std::vector<token_t> stack;
+
+ while (*expr)
+ {
+ if (*expr == ' ' || *expr == '\t' || *expr == '\r' || *expr == '\n' || *expr == '"') {
+ expr++;
+ continue;
+ }
+
+ token_t next_token(0);
+ if (*expr == '(' || *expr == ')' || *expr == '\'' || *expr == '!' || *expr == '^' || *expr == '*' || *expr == '+' || *expr == '|')
+ next_token = token_t(*(expr++));
+ else
+ next_token = token_t(0, parse_func_identifier(module, expr));
+
+ while (parse_func_reduce(module, stack, next_token)) {}
+ stack.push_back(next_token);
+ }
+
+ while (parse_func_reduce(module, stack, token_t('.'))) {}
+
+#if 0
+ for (size_t i = 0; i < stack.size(); i++)
+ if (stack[i].type < 16)
+ log("%3d: %d %s\n", int(i), stack[i].type, log_signal(stack[i].sig));
+ else
+ log("%3d: %c\n", int(i), stack[i].type);
+#endif
+
+ if (stack.size() != 1 || stack.back().type != 3)
+ log_error("Parser error in function expr `%s'.\n", orig_expr);
+
+ return stack.back().sig;
+}
+
+static void create_ff(RTLIL::Module *module, LibertyAst *node)
+{
+ RTLIL::SigSpec iq_sig(module->addWire(RTLIL::escape_id(node->args.at(0))));
+ RTLIL::SigSpec iqn_sig(module->addWire(RTLIL::escape_id(node->args.at(1))));
+
+ RTLIL::SigSpec clk_sig, data_sig, clear_sig, preset_sig;
+ bool clk_polarity = true, clear_polarity = true, preset_polarity = true;
+
+ for (auto child : node->children) {
+ if (child->id == "clocked_on")
+ clk_sig = parse_func_expr(module, child->value.c_str());
+ if (child->id == "next_state")
+ data_sig = parse_func_expr(module, child->value.c_str());
+ if (child->id == "clear")
+ clear_sig = parse_func_expr(module, child->value.c_str());
+ if (child->id == "preset")
+ preset_sig = parse_func_expr(module, child->value.c_str());
+ }
+
+ if (clk_sig.size() == 0 || data_sig.size() == 0)
+ log_error("FF cell %s has no next_state and/or clocked_on attribute.\n", log_id(module->name));
+
+ for (bool rerun_invert_rollback = true; rerun_invert_rollback;)
+ {
+ rerun_invert_rollback = false;
+
+ for (auto &it : module->cells_) {
+ if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == clk_sig) {
+ clk_sig = it.second->getPort("\\A");
+ clk_polarity = !clk_polarity;
+ rerun_invert_rollback = true;
+ }
+ if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == clear_sig) {
+ clear_sig = it.second->getPort("\\A");
+ clear_polarity = !clear_polarity;
+ rerun_invert_rollback = true;
+ }
+ if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == preset_sig) {
+ preset_sig = it.second->getPort("\\A");
+ preset_polarity = !preset_polarity;
+ rerun_invert_rollback = true;
+ }
+ }
+ }
+
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$_NOT_");
+ cell->setPort("\\A", iq_sig);
+ cell->setPort("\\Y", iqn_sig);
+
+ cell = module->addCell(NEW_ID, "");
+ cell->setPort("\\D", data_sig);
+ cell->setPort("\\Q", iq_sig);
+ cell->setPort("\\C", clk_sig);
+
+ if (clear_sig.size() == 0 && preset_sig.size() == 0) {
+ cell->type = stringf("$_DFF_%c_", clk_polarity ? 'P' : 'N');
+ }
+
+ if (clear_sig.size() == 1 && preset_sig.size() == 0) {
+ cell->type = stringf("$_DFF_%c%c0_", clk_polarity ? 'P' : 'N', clear_polarity ? 'P' : 'N');
+ cell->setPort("\\R", clear_sig);
+ }
+
+ if (clear_sig.size() == 0 && preset_sig.size() == 1) {
+ cell->type = stringf("$_DFF_%c%c1_", clk_polarity ? 'P' : 'N', preset_polarity ? 'P' : 'N');
+ cell->setPort("\\R", preset_sig);
+ }
+
+ if (clear_sig.size() == 1 && preset_sig.size() == 1) {
+ cell->type = stringf("$_DFFSR_%c%c%c_", clk_polarity ? 'P' : 'N', preset_polarity ? 'P' : 'N', clear_polarity ? 'P' : 'N');
+ cell->setPort("\\S", preset_sig);
+ cell->setPort("\\R", clear_sig);
+ }
+
+ log_assert(!cell->type.empty());
+}
+
+static void create_latch(RTLIL::Module *module, LibertyAst *node)
+{
+ RTLIL::SigSpec iq_sig(module->addWire(RTLIL::escape_id(node->args.at(0))));
+ RTLIL::SigSpec iqn_sig(module->addWire(RTLIL::escape_id(node->args.at(1))));
+
+ RTLIL::SigSpec enable_sig, data_sig, clear_sig, preset_sig;
+ bool enable_polarity = true, clear_polarity = true, preset_polarity = true;
+
+ for (auto child : node->children) {
+ if (child->id == "enable")
+ enable_sig = parse_func_expr(module, child->value.c_str());
+ if (child->id == "data_in")
+ data_sig = parse_func_expr(module, child->value.c_str());
+ if (child->id == "clear")
+ clear_sig = parse_func_expr(module, child->value.c_str());
+ if (child->id == "preset")
+ preset_sig = parse_func_expr(module, child->value.c_str());
+ }
+
+ if (enable_sig.size() == 0 || data_sig.size() == 0)
+ log_error("Latch cell %s has no data_in and/or enable attribute.\n", log_id(module->name));
+
+ for (bool rerun_invert_rollback = true; rerun_invert_rollback;)
+ {
+ rerun_invert_rollback = false;
+
+ for (auto &it : module->cells_) {
+ if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == enable_sig) {
+ enable_sig = it.second->getPort("\\A");
+ enable_polarity = !enable_polarity;
+ rerun_invert_rollback = true;
+ }
+ if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == clear_sig) {
+ clear_sig = it.second->getPort("\\A");
+ clear_polarity = !clear_polarity;
+ rerun_invert_rollback = true;
+ }
+ if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == preset_sig) {
+ preset_sig = it.second->getPort("\\A");
+ preset_polarity = !preset_polarity;
+ rerun_invert_rollback = true;
+ }
+ }
+ }
+
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$_NOT_");
+ cell->setPort("\\A", iq_sig);
+ cell->setPort("\\Y", iqn_sig);
+
+ if (clear_sig.size() == 1)
+ {
+ RTLIL::SigSpec clear_negative = clear_sig;
+ RTLIL::SigSpec clear_enable = clear_sig;
+
+ if (clear_polarity == true || clear_polarity != enable_polarity)
+ {
+ RTLIL::Cell *inv = module->addCell(NEW_ID, "$_NOT_");
+ inv->setPort("\\A", clear_sig);
+ inv->setPort("\\Y", module->addWire(NEW_ID));
+
+ if (clear_polarity == true)
+ clear_negative = inv->getPort("\\Y");
+ if (clear_polarity != enable_polarity)
+ clear_enable = inv->getPort("\\Y");
+ }
+
+ RTLIL::Cell *data_gate = module->addCell(NEW_ID, "$_AND_");
+ data_gate->setPort("\\A", data_sig);
+ data_gate->setPort("\\B", clear_negative);
+ data_gate->setPort("\\Y", data_sig = module->addWire(NEW_ID));
+
+ RTLIL::Cell *enable_gate = module->addCell(NEW_ID, enable_polarity ? "$_OR_" : "$_AND_");
+ enable_gate->setPort("\\A", enable_sig);
+ enable_gate->setPort("\\B", clear_enable);
+ enable_gate->setPort("\\Y", data_sig = module->addWire(NEW_ID));
+ }
+
+ if (preset_sig.size() == 1)
+ {
+ RTLIL::SigSpec preset_positive = preset_sig;
+ RTLIL::SigSpec preset_enable = preset_sig;
+
+ if (preset_polarity == false || preset_polarity != enable_polarity)
+ {
+ RTLIL::Cell *inv = module->addCell(NEW_ID, "$_NOT_");
+ inv->setPort("\\A", preset_sig);
+ inv->setPort("\\Y", module->addWire(NEW_ID));
+
+ if (preset_polarity == false)
+ preset_positive = inv->getPort("\\Y");
+ if (preset_polarity != enable_polarity)
+ preset_enable = inv->getPort("\\Y");
+ }
+
+ RTLIL::Cell *data_gate = module->addCell(NEW_ID, "$_OR_");
+ data_gate->setPort("\\A", data_sig);
+ data_gate->setPort("\\B", preset_positive);
+ data_gate->setPort("\\Y", data_sig = module->addWire(NEW_ID));
+
+ RTLIL::Cell *enable_gate = module->addCell(NEW_ID, enable_polarity ? "$_OR_" : "$_AND_");
+ enable_gate->setPort("\\A", enable_sig);
+ enable_gate->setPort("\\B", preset_enable);
+ enable_gate->setPort("\\Y", data_sig = module->addWire(NEW_ID));
+ }
+
+ cell = module->addCell(NEW_ID, stringf("$_DLATCH_%c_", enable_polarity ? 'P' : 'N'));
+ cell->setPort("\\D", data_sig);
+ cell->setPort("\\Q", iq_sig);
+ cell->setPort("\\E", enable_sig);
+}
+
+void parse_type_map(std::map<std::string, std::tuple<int, int, bool>> &type_map, LibertyAst *ast)
+{
+ for (auto type_node : ast->children)
+ {
+ if (type_node->id != "type" || type_node->args.size() != 1)
+ continue;
+
+ std::string type_name = type_node->args.at(0);
+ int bit_width = -1, bit_from = -1, bit_to = -1;
+ bool upto = false;
+
+ for (auto child : type_node->children)
+ {
+ if (child->id == "base_type" && child->value != "array")
+ goto next_type;
+
+ if (child->id == "data_type" && child->value != "bit")
+ goto next_type;
+
+ if (child->id == "bit_width")
+ bit_width = atoi(child->value.c_str());
+
+ if (child->id == "bit_from")
+ bit_from = atoi(child->value.c_str());
+
+ if (child->id == "bit_to")
+ bit_to = atoi(child->value.c_str());
+
+ if (child->id == "downto" && (child->value == "0" || child->value == "false" || child->value == "FALSE"))
+ upto = true;
+ }
+
+ if (bit_width != (std::max(bit_from, bit_to) - std::min(bit_from, bit_to) + 1))
+ log_error("Incompatible array type '%s': bit_width=%d, bit_from=%d, bit_to=%d.\n",
+ type_name.c_str(), bit_width, bit_from, bit_to);
+
+ type_map[type_name] = std::tuple<int, int, bool>(bit_width, std::min(bit_from, bit_to), upto);
+ next_type:;
+ }
+}
+
+struct LibertyFrontend : public Frontend {
+ LibertyFrontend() : Frontend("liberty", "read cells from liberty file") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" read_liberty [filename]\n");
+ log("\n");
+ log("Read cells from liberty file as modules into current design.\n");
+ log("\n");
+ log(" -lib\n");
+ log(" only create empty blackbox modules\n");
+ log("\n");
+ log(" -ignore_redef\n");
+ log(" ignore re-definitions of modules. (the default behavior is to\n");
+ log(" create an error message.)\n");
+ log("\n");
+ log(" -ignore_miss_func\n");
+ log(" ignore cells with missing function specification of outputs\n");
+ log("\n");
+ log(" -ignore_miss_dir\n");
+ log(" ignore cells with a missing or invalid direction\n");
+ log(" specification on a pin\n");
+ log("\n");
+ log(" -setattr <attribute_name>\n");
+ log(" set the specified attribute (to the value 1) on all loaded modules\n");
+ log("\n");
+ }
+ virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool flag_lib = false;
+ bool flag_ignore_redef = false;
+ bool flag_ignore_miss_func = false;
+ bool flag_ignore_miss_dir = false;
+ std::vector<std::string> attributes;
+
+ log_header(design, "Executing Liberty frontend.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-lib") {
+ flag_lib = true;
+ continue;
+ }
+ if (arg == "-ignore_redef") {
+ flag_ignore_redef = true;
+ continue;
+ }
+ if (arg == "-ignore_miss_func") {
+ flag_ignore_miss_func = true;
+ continue;
+ }
+ if (arg == "-ignore_miss_dir") {
+ flag_ignore_miss_dir = true;
+ continue;
+ }
+ if (arg == "-setattr" && argidx+1 < args.size()) {
+ attributes.push_back(RTLIL::escape_id(args[++argidx]));
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ LibertyParser parser(*f);
+ int cell_count = 0;
+
+ std::map<std::string, std::tuple<int, int, bool>> global_type_map;
+ parse_type_map(global_type_map, parser.ast);
+
+ for (auto cell : parser.ast->children)
+ {
+ if (cell->id != "cell" || cell->args.size() != 1)
+ continue;
+
+ std::string cell_name = RTLIL::escape_id(cell->args.at(0));
+
+ if (design->has(cell_name)) {
+ if (flag_ignore_redef)
+ continue;
+ log_error("Duplicate definition of cell/module %s.\n", RTLIL::unescape_id(cell_name).c_str());
+ }
+
+ // log("Processing cell type %s.\n", RTLIL::unescape_id(cell_name).c_str());
+
+ std::map<std::string, std::tuple<int, int, bool>> type_map = global_type_map;
+ parse_type_map(type_map, cell);
+
+ RTLIL::Module *module = new RTLIL::Module;
+ module->name = cell_name;
+
+ if (flag_lib)
+ module->set_bool_attribute("\\blackbox");
+
+ for (auto &attr : attributes)
+ module->attributes[attr] = 1;
+
+ for (auto node : cell->children)
+ {
+ if (node->id == "pin" && node->args.size() == 1) {
+ LibertyAst *dir = node->find("direction");
+ if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal"))
+ {
+ if (!flag_ignore_miss_dir)
+ {
+ log_error("Missing or invalid direction for pin %s on cell %s.\n", node->args.at(0).c_str(), log_id(module->name));
+ } else {
+ log("Ignoring cell %s with missing or invalid direction for pin %s.\n", log_id(module->name), node->args.at(0).c_str());
+ delete module;
+ goto skip_cell;
+ }
+ }
+ if (!flag_lib || dir->value != "internal")
+ module->addWire(RTLIL::escape_id(node->args.at(0)));
+ }
+
+ if (node->id == "bus" && node->args.size() == 1)
+ {
+ if (!flag_lib)
+ log_error("Error in cell %s: bus interfaces are only supported in -lib mode.\n", log_id(cell_name));
+
+ LibertyAst *dir = node->find("direction");
+
+ if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal"))
+ log_error("Missing or invalid direction for bus %s on cell %s.\n", node->args.at(0).c_str(), log_id(module->name));
+
+ if (dir->value == "internal")
+ continue;
+
+ 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",
+ node->args.at(0).c_str(), log_id(cell_name));
+
+ int bus_type_width = std::get<0>(type_map.at(bus_type_node->value));
+ int bus_type_offset = std::get<1>(type_map.at(bus_type_node->value));
+ bool bus_type_upto = std::get<2>(type_map.at(bus_type_node->value));
+
+ Wire *wire = module->addWire(RTLIL::escape_id(node->args.at(0)), bus_type_width);
+ wire->start_offset = bus_type_offset;
+ wire->upto = bus_type_upto;
+
+ if (dir->value == "input" || dir->value == "inout")
+ wire->port_input = true;
+
+ if (dir->value == "output" || dir->value == "inout")
+ wire->port_output = true;
+ }
+ }
+
+ for (auto node : cell->children)
+ {
+ if (!flag_lib) {
+ if (node->id == "ff" && node->args.size() == 2)
+ create_ff(module, node);
+ if (node->id == "latch" && node->args.size() == 2)
+ create_latch(module, node);
+ }
+
+ if (node->id == "pin" && node->args.size() == 1)
+ {
+ LibertyAst *dir = node->find("direction");
+
+ if (flag_lib && dir->value == "internal")
+ continue;
+
+ RTLIL::Wire *wire = module->wires_.at(RTLIL::escape_id(node->args.at(0)));
+
+ if (dir && dir->value == "inout") {
+ wire->port_input = true;
+ wire->port_output = true;
+ }
+
+ if (dir && dir->value == "input") {
+ wire->port_input = true;
+ continue;
+ }
+
+ if (dir && dir->value == "output")
+ wire->port_output = true;
+
+ if (flag_lib)
+ continue;
+
+ LibertyAst *func = node->find("function");
+ if (func == NULL)
+ {
+ if (!flag_ignore_miss_func)
+ {
+ log_error("Missing function on output %s of cell %s.\n", log_id(wire->name), log_id(module->name));
+ } else {
+ log("Ignoring cell %s with missing function on output %s.\n", log_id(module->name), log_id(wire->name));
+ delete module;
+ goto skip_cell;
+ }
+ }
+
+ RTLIL::SigSpec out_sig = parse_func_expr(module, func->value.c_str());
+ module->connect(RTLIL::SigSig(wire, out_sig));
+ }
+ }
+
+ module->fixup_ports();
+ design->add(module);
+ cell_count++;
+skip_cell:;
+ }
+
+ log("Imported %d cell types from liberty file.\n", cell_count);
+ }
+} LibertyFrontend;
+
+YOSYS_NAMESPACE_END
+
diff --git a/frontends/verific/Makefile.inc b/frontends/verific/Makefile.inc
new file mode 100644
index 00000000..68ef9aed
--- /dev/null
+++ b/frontends/verific/Makefile.inc
@@ -0,0 +1,17 @@
+
+OBJS += frontends/verific/verific.o
+
+ifeq ($(ENABLE_VERIFIC),1)
+
+EXTRA_TARGETS += share/verific
+
+share/verific:
+ $(P) rm -rf share/verific.new
+ $(Q) mkdir -p share/verific.new
+ $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1987/. share/verific.new/vhdl_vdbs_1987
+ $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1993/. share/verific.new/vhdl_vdbs_1993
+ $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_2008/. share/verific.new/vhdl_vdbs_2008
+ $(Q) mv share/verific.new share/verific
+
+endif
+
diff --git a/frontends/verific/build_amd64.txt b/frontends/verific/build_amd64.txt
new file mode 100644
index 00000000..d6952820
--- /dev/null
+++ b/frontends/verific/build_amd64.txt
@@ -0,0 +1,30 @@
+
+Notes on building yosys with verific support on amd64 when you
+only have the i386 eval version of Verific:
+
+
+1.) Use a Makefile.conf like the following one:
+
+--snip--
+CONFIG := clang
+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 frontends/verific/test_navre.ys
+
diff --git a/frontends/verific/test_navre.ys b/frontends/verific/test_navre.ys
new file mode 100644
index 00000000..a56b725a
--- /dev/null
+++ b/frontends/verific/test_navre.ys
@@ -0,0 +1,18 @@
+verific -vlog2k ../yosys-bigsim/softusb_navre/rtl/softusb_navre.v
+verific -import softusb_navre
+
+memory softusb_navre
+flatten softusb_navre
+rename softusb_navre gate
+
+read_verilog ../yosys-bigsim/softusb_navre/rtl/softusb_navre.v
+cd softusb_navre; proc; opt; memory; opt; cd ..
+rename softusb_navre gold
+
+expose -dff -shared gold gate
+miter -equiv -ignore_gold_x -make_assert -make_outputs -make_outcmp gold gate miter
+
+cd miter
+flatten; opt -undriven
+sat -verify -maxsteps 5 -set-init-undef -set-def-inputs -prove-asserts -tempinduct-def \
+ -seq 1 -set-at 1 in_rst 1 # -show-inputs -show-outputs
diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc
new file mode 100644
index 00000000..7dd36a74
--- /dev/null
+++ b/frontends/verific/verific.cc
@@ -0,0 +1,1005 @@
+/*
+ * 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/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifndef _WIN32
+# include <unistd.h>
+# include <dirent.h>
+#endif
+
+USING_YOSYS_NAMESPACE
+
+#ifdef YOSYS_ENABLE_VERIFIC
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Woverloaded-virtual"
+
+#include "veri_file.h"
+#include "vhdl_file.h"
+#include "VeriModule.h"
+#include "VhdlUnits.h"
+#include "DataBase.h"
+#include "Message.h"
+
+#pragma clang diagnostic pop
+
+#ifdef VERIFIC_NAMESPACE
+using namespace Verific ;
+#endif
+
+static void msg_func(msg_type_t msg_type, const char *message_id, linefile_type linefile, const char *msg, va_list args)
+{
+ log("VERIFIC-%s [%s] ",
+ msg_type == VERIFIC_NONE ? "NONE" :
+ msg_type == VERIFIC_ERROR ? "ERROR" :
+ msg_type == VERIFIC_WARNING ? "WARNING" :
+ msg_type == VERIFIC_IGNORE ? "IGNORE" :
+ msg_type == VERIFIC_INFO ? "INFO" :
+ msg_type == VERIFIC_COMMENT ? "COMMENT" :
+ msg_type == VERIFIC_PROGRAM_ERROR ? "PROGRAM_ERROR" : "UNKNOWN", message_id);
+ if (linefile)
+ log("%s:%d: ", LineFile::GetFileName(linefile), LineFile::GetLineNo(linefile));
+ logv(msg, args);
+ log("\n");
+}
+
+static void import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, DesignObj *obj)
+{
+ MapIter mi;
+ Att *attr;
+
+ if (obj->Linefile())
+ attributes["\\src"] = stringf("%s:%d", LineFile::GetFileName(obj->Linefile()), LineFile::GetLineNo(obj->Linefile()));
+
+ // FIXME: Parse numeric attributes
+ FOREACH_ATTRIBUTE(obj, mi, attr)
+ attributes[RTLIL::escape_id(attr->Key())] = RTLIL::Const(std::string(attr->Value()));
+}
+
+static RTLIL::SigSpec operatorInput(Instance *inst, std::map<Net*, RTLIL::SigBit> &net_map)
+{
+ RTLIL::SigSpec sig;
+ for (int i = int(inst->InputSize())-1; i >= 0; i--)
+ if (inst->GetInputBit(i))
+ sig.append(net_map.at(inst->GetInputBit(i)));
+ else
+ sig.append(RTLIL::State::Sz);
+ return sig;
+}
+
+static RTLIL::SigSpec operatorInput1(Instance *inst, std::map<Net*, RTLIL::SigBit> &net_map)
+{
+ RTLIL::SigSpec sig;
+ for (int i = int(inst->Input1Size())-1; i >= 0; i--)
+ if (inst->GetInput1Bit(i))
+ sig.append(net_map.at(inst->GetInput1Bit(i)));
+ else
+ sig.append(RTLIL::State::Sz);
+ return sig;
+}
+
+static RTLIL::SigSpec operatorInput2(Instance *inst, std::map<Net*, RTLIL::SigBit> &net_map)
+{
+ RTLIL::SigSpec sig;
+ for (int i = int(inst->Input2Size())-1; i >= 0; i--)
+ if (inst->GetInput2Bit(i))
+ sig.append(net_map.at(inst->GetInput2Bit(i)));
+ else
+ sig.append(RTLIL::State::Sz);
+ return sig;
+}
+
+static RTLIL::SigSpec operatorInport(Instance *inst, const char *portname, std::map<Net*, RTLIL::SigBit> &net_map)
+{
+ PortBus *portbus = inst->View()->GetPortBus(portname);
+ if (portbus) {
+ RTLIL::SigSpec sig;
+ for (unsigned i = 0; i < portbus->Size(); i++) {
+ Net *net = inst->GetNet(portbus->ElementAtIndex(i));
+ if (net) {
+ if (net->IsGnd())
+ sig.append(RTLIL::State::S0);
+ else if (net->IsPwr())
+ sig.append(RTLIL::State::S1);
+ else
+ sig.append(net_map.at(net));
+ } else
+ sig.append(RTLIL::State::Sz);
+ }
+ return sig;
+ } else {
+ Port *port = inst->View()->GetPort(portname);
+ log_assert(port != NULL);
+ Net *net = inst->GetNet(port);
+ return net_map.at(net);
+ }
+}
+
+static RTLIL::SigSpec operatorOutput(Instance *inst, std::map<Net*, RTLIL::SigBit> &net_map, RTLIL::Module *module)
+{
+ RTLIL::SigSpec sig;
+ RTLIL::Wire *dummy_wire = NULL;
+ for (int i = int(inst->OutputSize())-1; i >= 0; i--)
+ if (inst->GetOutputBit(i)) {
+ sig.append(net_map.at(inst->GetOutputBit(i)));
+ dummy_wire = NULL;
+ } else {
+ if (dummy_wire == NULL)
+ dummy_wire = module->addWire(NEW_ID);
+ else
+ dummy_wire->width++;
+ sig.append(RTLIL::SigSpec(dummy_wire, dummy_wire->width - 1));
+ }
+ return sig;
+}
+
+static bool import_netlist_instance_gates(RTLIL::Module *module, std::map<Net*, RTLIL::SigBit> &net_map, Instance *inst)
+{
+ if (inst->Type() == PRIM_AND) {
+ module->addAndGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ 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);
+ module->addNotGate(RTLIL::escape_id(inst->Name()), tmp, net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_OR) {
+ module->addOrGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ 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);
+ module->addNotGate(RTLIL::escape_id(inst->Name()), tmp, net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_XOR) {
+ module->addXorGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_XNOR) {
+ module->addXnorGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_BUF) {
+ module->addBufGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_INV) {
+ module->addNotGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_MUX) {
+ module->addMuxGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetControl()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_TRI) {
+ module->addMuxGate(RTLIL::escape_id(inst->Name()), RTLIL::State::Sz, net_map.at(inst->GetInput()), net_map.at(inst->GetControl()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ 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);
+ module->addXorGate(RTLIL::escape_id(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);
+ return true;
+ }
+
+ if (inst->Type() == PRIM_DFFRS)
+ {
+ if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd())
+ module->addDffGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ else if (inst->GetSet()->IsGnd())
+ module->addAdffGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetReset()),
+ net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()), false);
+ else if (inst->GetReset()->IsGnd())
+ module->addAdffGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetSet()),
+ net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()), true);
+ else
+ module->addDffsrGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetSet()), net_map.at(inst->GetReset()),
+ net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ return false;
+}
+
+static bool import_netlist_instance_cells(RTLIL::Module *module, std::map<Net*, RTLIL::SigBit> &net_map, Instance *inst)
+{
+ if (inst->Type() == PRIM_AND) {
+ module->addAnd(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ 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(RTLIL::escape_id(inst->Name()), tmp, net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_OR) {
+ module->addOr(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ 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(RTLIL::escape_id(inst->Name()), tmp, net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_XOR) {
+ module->addXor(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_XNOR) {
+ module->addXnor(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_INV) {
+ module->addNot(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_MUX) {
+ module->addMux(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetControl()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_TRI) {
+ module->addMux(RTLIL::escape_id(inst->Name()), RTLIL::State::Sz, net_map.at(inst->GetInput()), net_map.at(inst->GetControl()), net_map.at(inst->GetOutput()));
+ 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);
+ 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(RTLIL::escape_id(inst->Name()), a_plus_b, net_map.at(inst->GetCin()), y);
+ return true;
+ }
+
+ if (inst->Type() == PRIM_DFFRS)
+ {
+ if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd())
+ module->addDff(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ else if (inst->GetSet()->IsGnd())
+ module->addAdff(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetReset()),
+ net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()), RTLIL::State::S0);
+ else if (inst->GetReset()->IsGnd())
+ module->addAdff(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetSet()),
+ net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()), RTLIL::State::S1);
+ else
+ module->addDffsr(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetSet()), net_map.at(inst->GetReset()),
+ net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_DLATCHRS)
+ {
+ if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd())
+ module->addDlatch(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetControl()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ else
+ module->addDlatchsr(RTLIL::escape_id(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()));
+ return true;
+ }
+
+ #define IN operatorInput(inst, net_map)
+ #define IN1 operatorInput1(inst, net_map)
+ #define IN2 operatorInput2(inst, net_map)
+ #define OUT operatorOutput(inst, net_map, module)
+ #define SIGNED inst->View()->IsSigned()
+
+ if (inst->Type() == OPER_ADDER) {
+ RTLIL::SigSpec out = OUT;
+ if (inst->GetCout() != NULL)
+ out.append(net_map.at(inst->GetCout()));
+ if (inst->GetCin()->IsGnd()) {
+ module->addAdd(RTLIL::escape_id(inst->Name()), IN1, IN2, out, SIGNED);
+ } else {
+ RTLIL::SigSpec tmp = module->addWire(NEW_ID, GetSize(out));
+ module->addAdd(NEW_ID, IN1, IN2, tmp, SIGNED);
+ module->addAdd(RTLIL::escape_id(inst->Name()), tmp, net_map.at(inst->GetCin()), out, false);
+ }
+ return true;
+ }
+
+ if (inst->Type() == OPER_MULTIPLIER) {
+ module->addMul(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_DIVIDER) {
+ module->addDiv(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_MODULO) {
+ module->addMod(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_REMAINDER) {
+ module->addMod(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_SHIFT_LEFT) {
+ module->addShl(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, false);
+ return true;
+ }
+
+ if (inst->Type() == OPER_ENABLED_DECODER) {
+ RTLIL::SigSpec vec;
+ vec.append(net_map.at(inst->GetControl()));
+ for (unsigned i = 1; i < inst->OutputSize(); i++) {
+ vec.append(RTLIL::State::S0);
+ }
+ module->addShl(RTLIL::escape_id(inst->Name()), vec, IN, OUT, false);
+ return true;
+ }
+
+ if (inst->Type() == OPER_DECODER) {
+ RTLIL::SigSpec vec;
+ vec.append(RTLIL::State::S1);
+ for (unsigned i = 1; i < inst->OutputSize(); i++) {
+ vec.append(RTLIL::State::S0);
+ }
+ module->addShl(RTLIL::escape_id(inst->Name()), vec, IN, OUT, false);
+ return true;
+ }
+
+ if (inst->Type() == OPER_SHIFT_RIGHT) {
+ Net *net_cin = inst->GetCin();
+ Net *net_a_msb = inst->GetInput1Bit(0);
+ if (net_cin->IsGnd())
+ module->addShr(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, false);
+ else if (net_cin == net_a_msb)
+ module->addSshr(RTLIL::escape_id(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());
+ return true;
+ }
+
+ if (inst->Type() == OPER_REDUCE_AND) {
+ module->addReduceAnd(RTLIL::escape_id(inst->Name()), IN, net_map.at(inst->GetOutput()), SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_REDUCE_OR) {
+ module->addReduceOr(RTLIL::escape_id(inst->Name()), IN, net_map.at(inst->GetOutput()), SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_REDUCE_XOR) {
+ module->addReduceXor(RTLIL::escape_id(inst->Name()), IN, net_map.at(inst->GetOutput()), SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_REDUCE_XNOR) {
+ module->addReduceXnor(RTLIL::escape_id(inst->Name()), IN, net_map.at(inst->GetOutput()), SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_LESSTHAN) {
+ Net *net_cin = inst->GetCin();
+ if (net_cin->IsGnd())
+ module->addLt(RTLIL::escape_id(inst->Name()), IN1, IN2, net_map.at(inst->GetOutput()), SIGNED);
+ else if (net_cin->IsPwr())
+ module->addLe(RTLIL::escape_id(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());
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_AND) {
+ module->addAnd(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_OR) {
+ module->addOr(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_XOR) {
+ module->addXor(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_XNOR) {
+ module->addXnor(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_BUF) {
+ module->addPos(RTLIL::escape_id(inst->Name()), IN, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_INV) {
+ module->addNot(RTLIL::escape_id(inst->Name()), IN, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_MINUS) {
+ module->addSub(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_UMINUS) {
+ module->addNeg(RTLIL::escape_id(inst->Name()), IN, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_EQUAL) {
+ module->addEq(RTLIL::escape_id(inst->Name()), IN1, IN2, net_map.at(inst->GetOutput()), SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_NEQUAL) {
+ module->addNe(RTLIL::escape_id(inst->Name()), IN1, IN2, net_map.at(inst->GetOutput()), SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_MUX) {
+ module->addMux(RTLIL::escape_id(inst->Name()), IN1, IN2, net_map.at(inst->GetControl()), OUT);
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_TRI) {
+ module->addMux(RTLIL::escape_id(inst->Name()), RTLIL::SigSpec(RTLIL::State::Sz, inst->OutputSize()), IN, net_map.at(inst->GetControl()), OUT);
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_DFFRS) {
+ RTLIL::SigSpec sig_set = operatorInport(inst, "set", net_map);
+ RTLIL::SigSpec sig_reset = operatorInport(inst, "reset", net_map);
+ if (sig_set.is_fully_const() && !sig_set.as_bool() && sig_reset.is_fully_const() && !sig_reset.as_bool())
+ module->addDff(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), IN, OUT);
+ else
+ module->addDffsr(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), sig_set, sig_reset, IN, OUT);
+ return true;
+ }
+
+ #undef IN
+ #undef IN1
+ #undef IN2
+ #undef OUT
+ #undef SIGNED
+
+ return false;
+}
+
+static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*> &nl_todo, bool mode_gates)
+{
+ std::string module_name = nl->IsOperator() ? std::string("$verific$") + nl->Owner()->Name() : RTLIL::escape_id(nl->Owner()->Name());
+
+ if (design->has(module_name)) {
+ if (!nl->IsOperator())
+ log_cmd_error("Re-definition of module `%s'.\n", nl->Owner()->Name());
+ return;
+ }
+
+ RTLIL::Module *module = new RTLIL::Module;
+ module->name = module_name;
+ design->add(module);
+
+ log("Importing module %s.\n", RTLIL::id2cstr(module->name));
+
+ std::map<Net*, RTLIL::SigBit> net_map;
+
+ SetIter si;
+ MapIter mi, mi2;
+ Port *port;
+ PortBus *portbus;
+ Net *net;
+ NetBus *netbus;
+ Instance *inst;
+ PortRef *pr;
+
+ FOREACH_PORT_OF_NETLIST(nl, mi, port)
+ {
+ if (port->Bus())
+ continue;
+
+ // log(" importing port %s.\n", port->Name());
+
+ RTLIL::Wire *wire = module->addWire(RTLIL::escape_id(port->Name()));
+ import_attributes(wire->attributes, port);
+
+ wire->port_id = nl->IndexOf(port) + 1;
+
+ if (port->GetDir() == DIR_INOUT || port->GetDir() == DIR_IN)
+ wire->port_input = true;
+ if (port->GetDir() == DIR_INOUT || port->GetDir() == DIR_OUT)
+ wire->port_output = true;
+
+ if (port->GetNet()) {
+ net = port->GetNet();
+ if (net_map.count(net) == 0)
+ net_map[net] = wire;
+ else if (wire->port_input)
+ module->connect(net_map.at(net), wire);
+ else
+ module->connect(wire, net_map.at(net));
+ }
+ }
+
+ FOREACH_PORTBUS_OF_NETLIST(nl, mi, portbus)
+ {
+ // log(" importing portbus %s.\n", portbus->Name());
+
+ RTLIL::Wire *wire = module->addWire(RTLIL::escape_id(portbus->Name()), portbus->Size());
+ wire->start_offset = min(portbus->LeftIndex(), portbus->RightIndex());
+ import_attributes(wire->attributes, portbus);
+
+ if (portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_IN)
+ wire->port_input = true;
+ if (portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_OUT)
+ wire->port_output = true;
+
+ for (int i = portbus->LeftIndex();; i += portbus->IsUp() ? +1 : -1) {
+ if (portbus->ElementAtIndex(i) && portbus->ElementAtIndex(i)->GetNet()) {
+ net = portbus->ElementAtIndex(i)->GetNet();
+ RTLIL::SigBit bit(wire, i - wire->start_offset);
+ if (net_map.count(net) == 0)
+ net_map[net] = bit;
+ else if (wire->port_input)
+ module->connect(net_map.at(net), bit);
+ else
+ module->connect(bit, net_map.at(net));
+ }
+ if (i == portbus->RightIndex())
+ break;
+ }
+ }
+
+ module->fixup_ports();
+
+ FOREACH_NET_OF_NETLIST(nl, mi, net)
+ {
+ if (net->IsRamNet())
+ {
+ RTLIL::Memory *memory = new RTLIL::Memory;
+ memory->name = RTLIL::escape_id(net->Name());
+ log_assert(module->count_id(memory->name) == 0);
+ module->memories[memory->name] = memory;
+
+ int number_of_bits = net->Size();
+ int bits_in_word = number_of_bits;
+ FOREACH_PORTREF_OF_NET(net, si, pr) {
+ if (pr->GetInst()->Type() == OPER_READ_PORT) {
+ bits_in_word = min<int>(bits_in_word, pr->GetInst()->OutputSize());
+ continue;
+ }
+ if (pr->GetInst()->Type() == OPER_WRITE_PORT || pr->GetInst()->Type() == OPER_CLOCKED_WRITE_PORT) {
+ bits_in_word = min<int>(bits_in_word, pr->GetInst()->Input2Size());
+ continue;
+ }
+ log_error("Verific RamNet %s is connected to unsupported instance type %s (%s).\n",
+ net->Name(), pr->GetInst()->View()->Owner()->Name(), pr->GetInst()->Name());
+ }
+
+ memory->width = bits_in_word;
+ memory->size = number_of_bits / bits_in_word;
+ continue;
+ }
+
+ if (net_map.count(net)) {
+ // log(" skipping net %s.\n", net->Name());
+ continue;
+ }
+
+ if (net->Bus())
+ continue;
+
+ // log(" importing net %s.\n", net->Name());
+
+ RTLIL::IdString wire_name = module->uniquify(RTLIL::escape_id(net->Name()));
+ RTLIL::Wire *wire = module->addWire(wire_name);
+ import_attributes(wire->attributes, net);
+
+ net_map[net] = wire;
+ }
+
+ FOREACH_NETBUS_OF_NETLIST(nl, mi, netbus)
+ {
+ bool found_new_net = false;
+ for (int i = netbus->LeftIndex();; i += netbus->IsUp() ? +1 : -1) {
+ net = netbus->ElementAtIndex(i);
+ if (net_map.count(net) == 0)
+ found_new_net = true;
+ if (i == netbus->RightIndex())
+ break;
+ }
+
+ if (found_new_net)
+ {
+ // log(" importing netbus %s.\n", netbus->Name());
+
+ RTLIL::IdString wire_name = module->uniquify(RTLIL::escape_id(netbus->Name()));
+ RTLIL::Wire *wire = module->addWire(wire_name, netbus->Size());
+ wire->start_offset = min(netbus->LeftIndex(), netbus->RightIndex());
+ import_attributes(wire->attributes, netbus);
+
+ for (int i = netbus->LeftIndex();; i += netbus->IsUp() ? +1 : -1) {
+ if (netbus->ElementAtIndex(i)) {
+ net = netbus->ElementAtIndex(i);
+ RTLIL::SigBit bit(wire, i - wire->start_offset);
+ if (net_map.count(net) == 0)
+ net_map[net] = bit;
+ else
+ module->connect(bit, net_map.at(net));
+ }
+ if (i == netbus->RightIndex())
+ break;
+ }
+ }
+ else
+ {
+ // log(" skipping netbus %s.\n", netbus->Name());
+ }
+ }
+
+ FOREACH_INSTANCE_OF_NETLIST(nl, mi, inst)
+ {
+ // log(" importing cell %s (%s).\n", inst->Name(), inst->View()->Owner()->Name());
+
+ if (inst->Type() == PRIM_PWR) {
+ module->connect(net_map.at(inst->GetOutput()), RTLIL::State::S1);
+ continue;
+ }
+
+ if (inst->Type() == PRIM_GND) {
+ module->connect(net_map.at(inst->GetOutput()), RTLIL::State::S0);
+ continue;
+ }
+
+ if (inst->Type() == PRIM_BUF) {
+ module->addBufGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ continue;
+ }
+
+ if (inst->Type() == PRIM_X) {
+ module->connect(net_map.at(inst->GetOutput()), RTLIL::State::Sx);
+ continue;
+ }
+
+ if (inst->Type() == PRIM_Z) {
+ module->connect(net_map.at(inst->GetOutput()), RTLIL::State::Sz);
+ continue;
+ }
+
+ if (inst->Type() == OPER_READ_PORT)
+ {
+ RTLIL::Memory *memory = module->memories.at(RTLIL::escape_id(inst->GetInput()->Name()));
+ if (memory->width != int(inst->OutputSize()))
+ log_error("Import of asymetric memories from Verific is not supported yet: %s %s\n", inst->Name(), inst->GetInput()->Name());
+
+ RTLIL::SigSpec addr = operatorInput1(inst, net_map);
+ RTLIL::SigSpec data = operatorOutput(inst, net_map, module);
+
+ RTLIL::Cell *cell = module->addCell(RTLIL::escape_id(inst->Name()), "$memrd");
+ cell->parameters["\\MEMID"] = memory->name.str();
+ cell->parameters["\\CLK_ENABLE"] = false;
+ cell->parameters["\\CLK_POLARITY"] = true;
+ cell->parameters["\\TRANSPARENT"] = false;
+ cell->parameters["\\ABITS"] = GetSize(addr);
+ cell->parameters["\\WIDTH"] = GetSize(data);
+ cell->setPort("\\CLK", RTLIL::State::Sx);
+ cell->setPort("\\EN", RTLIL::State::Sx);
+ cell->setPort("\\ADDR", addr);
+ cell->setPort("\\DATA", data);
+ continue;
+ }
+
+ 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 asymetric memories from Verific is not supported yet: %s %s\n", inst->Name(), inst->GetInput()->Name());
+
+ RTLIL::SigSpec addr = operatorInput1(inst, net_map);
+ RTLIL::SigSpec data = operatorInput2(inst, net_map);
+
+ RTLIL::Cell *cell = module->addCell(RTLIL::escape_id(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()));
+ }
+ continue;
+ }
+
+ if (!mode_gates) {
+ if (import_netlist_instance_cells(module, net_map, inst))
+ continue;
+ if (inst->IsOperator())
+ log_warning("Unsupported Verific operator: %s (fallback to gate level implementation provided by verific)\n", inst->View()->Owner()->Name());
+ } else {
+ if (import_netlist_instance_gates(module, net_map, inst))
+ continue;
+ }
+
+ if (inst->IsPrimitive())
+ log_error("Unsupported Verific primitive %s of type %s\n", inst->Name(), inst->View()->Owner()->Name());
+
+ nl_todo.insert(inst->View());
+
+ RTLIL::Cell *cell = module->addCell(RTLIL::escape_id(inst->Name()), inst->IsOperator() ?
+ std::string("$verific$") + inst->View()->Owner()->Name() : RTLIL::escape_id(inst->View()->Owner()->Name()));
+
+ dict<IdString, vector<SigBit>> cell_port_conns;
+
+ FOREACH_PORTREF_OF_INST(inst, mi2, pr) {
+ // log(" .%s(%s)\n", pr->GetPort()->Name(), pr->GetNet()->Name());
+ const char *port_name = pr->GetPort()->Name();
+ int port_offset = 0;
+ if (pr->GetPort()->Bus()) {
+ port_name = pr->GetPort()->Bus()->Name();
+ port_offset = pr->GetPort()->Bus()->IndexOf(pr->GetPort()) -
+ min(pr->GetPort()->Bus()->LeftIndex(), pr->GetPort()->Bus()->RightIndex());
+ }
+ 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));
+ for (auto bit : zwires)
+ sigvec.push_back(bit);
+ }
+ sigvec[port_offset] = net_map.at(pr->GetNet());
+ }
+
+ for (auto &it : cell_port_conns) {
+ // log(" .%s(%s)\n", log_id(it.first), log_signal(it.second));
+ cell->setPort(it.first, it.second);
+ }
+ }
+}
+
+#endif /* YOSYS_ENABLE_VERIFIC */
+
+YOSYS_NAMESPACE_BEGIN
+
+struct VerificPass : public Pass {
+ VerificPass() : Pass("verific", "load Verilog and VHDL designs using Verific") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" verific {-vlog95|-vlog2k|-sv2005|-sv2009|-sv} <verilog-file>..\n");
+ log("\n");
+ log("Load the specified Verilog/SystemVerilog files into Verific.\n");
+ log("\n");
+ log("\n");
+ log(" verific {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008} <vhdl-file>..\n");
+ log("\n");
+ log("Load the specified VHDL files into Verific.\n");
+ log("\n");
+ log("\n");
+ log(" verific -import [-gates] {-all | <top-module>..}\n");
+ log("\n");
+ log("Elaborate the design for the specified top modules, import to Yosys and\n");
+ log("reset the internal state of Verific. A gate-level netlist is created\n");
+ log("when called with -gates.\n");
+ log("\n");
+ log("Visit http://verific.com/ for more information on Verific.\n");
+ log("\n");
+ }
+#ifdef YOSYS_ENABLE_VERIFIC
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing VERIFIC (loading Verilog and VHDL designs using Verific).\n");
+
+ Message::SetConsoleOutput(0);
+ Message::RegisterCallBackMsg(msg_func);
+
+ if (args.size() > 1 && args[1] == "-vlog95") {
+ for (size_t argidx = 2; argidx < args.size(); argidx++)
+ if (!veri_file::Analyze(args[argidx].c_str(), veri_file::VERILOG_95))
+ log_cmd_error("Reading `%s' in VERILOG_95 mode failed.\n", args[argidx].c_str());
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-vlog2k") {
+ for (size_t argidx = 2; argidx < args.size(); argidx++)
+ if (!veri_file::Analyze(args[argidx].c_str(), veri_file::VERILOG_2K))
+ log_cmd_error("Reading `%s' in VERILOG_2K mode failed.\n", args[argidx].c_str());
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-sv2005") {
+ for (size_t argidx = 2; argidx < args.size(); argidx++)
+ if (!veri_file::Analyze(args[argidx].c_str(), veri_file::SYSTEM_VERILOG_2005))
+ log_cmd_error("Reading `%s' in SYSTEM_VERILOG_2005 mode failed.\n", args[argidx].c_str());
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-sv2009") {
+ for (size_t argidx = 2; argidx < args.size(); argidx++)
+ if (!veri_file::Analyze(args[argidx].c_str(), veri_file::SYSTEM_VERILOG_2009))
+ log_cmd_error("Reading `%s' in SYSTEM_VERILOG_2009 mode failed.\n", args[argidx].c_str());
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-sv") {
+ for (size_t argidx = 2; argidx < args.size(); argidx++)
+ if (!veri_file::Analyze(args[argidx].c_str(), veri_file::SYSTEM_VERILOG))
+ log_cmd_error("Reading `%s' in SYSTEM_VERILOG mode failed.\n", args[argidx].c_str());
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-vhdl87") {
+ vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1987").c_str());
+ for (size_t argidx = 2; argidx < args.size(); argidx++)
+ if (!vhdl_file::Analyze(args[argidx].c_str(), "work", vhdl_file::VHDL_87))
+ log_cmd_error("Reading `%s' in VHDL_87 mode failed.\n", args[argidx].c_str());
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-vhdl93") {
+ vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1993").c_str());
+ for (size_t argidx = 2; argidx < args.size(); argidx++)
+ if (!vhdl_file::Analyze(args[argidx].c_str(), "work", vhdl_file::VHDL_93))
+ log_cmd_error("Reading `%s' in VHDL_93 mode failed.\n", args[argidx].c_str());
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-vhdl2k") {
+ vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1993").c_str());
+ for (size_t argidx = 2; argidx < args.size(); argidx++)
+ if (!vhdl_file::Analyze(args[argidx].c_str(), "work", vhdl_file::VHDL_2K))
+ log_cmd_error("Reading `%s' in VHDL_2K mode failed.\n", args[argidx].c_str());
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-vhdl2008") {
+ vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_2008").c_str());
+ for (size_t argidx = 2; argidx < args.size(); argidx++)
+ if (!vhdl_file::Analyze(args[argidx].c_str(), "work", vhdl_file::VHDL_2008))
+ log_cmd_error("Reading `%s' in VHDL_2008 mode failed.\n", args[argidx].c_str());
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-import")
+ {
+ std::set<Netlist*> nl_todo, nl_done;
+ bool mode_all = false, mode_gates = false;
+
+ size_t argidx = 2;
+ for (; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-all") {
+ mode_all = true;
+ continue;
+ }
+ if (args[argidx] == "-gates") {
+ mode_gates = true;
+ continue;
+ }
+ break;
+ }
+
+ if (argidx > args.size() && args[argidx].substr(0, 1) == "-")
+ cmd_error(args, argidx, "unknown option");
+
+ if (mode_all)
+ {
+ if (argidx != args.size())
+ log_cmd_error("Got -all and an explicit list of top modules.\n");
+
+ MapIter m1, m2, m3;
+ VeriModule *mod;
+ FOREACH_VERILOG_MODULE(m1, mod)
+ args.push_back(mod->Name());
+
+ VhdlLibrary *lib;
+ VhdlPrimaryUnit *primunit;
+ FOREACH_VHDL_LIBRARY(m1, lib)
+ FOREACH_VHDL_PRIMARY_UNIT(lib, m2, primunit) {
+ if (primunit->IsPackageDecl())
+ continue;
+ args.push_back(primunit->Name());
+ }
+ }
+ else
+ if (argidx == args.size())
+ log_cmd_error("No top module specified.\n");
+
+ for (; argidx < args.size(); 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());
+ }
+ }
+
+ while (!nl_todo.empty()) {
+ Netlist *nl = *nl_todo.begin();
+ if (nl_done.count(nl) == 0)
+ import_netlist(design, nl, nl_todo, mode_gates);
+ nl_todo.erase(nl);
+ nl_done.insert(nl);
+ }
+
+ Libset::Reset();
+ return;
+ }
+
+ log_cmd_error("Missing or unsupported mode parameter.\n");
+ }
+#else /* YOSYS_ENABLE_VERIFIC */
+ virtual void execute(std::vector<std::string>, RTLIL::Design *) {
+ log_cmd_error("This version of Yosys is built without Verific support.\n");
+ }
+#endif
+} VerificPass;
+
+YOSYS_NAMESPACE_END
+
diff --git a/frontends/verilog/.gitignore b/frontends/verilog/.gitignore
new file mode 100644
index 00000000..1d4ae9e5
--- /dev/null
+++ b/frontends/verilog/.gitignore
@@ -0,0 +1,4 @@
+verilog_lexer.cc
+verilog_parser.output
+verilog_parser.tab.cc
+verilog_parser.tab.h
diff --git a/frontends/verilog/Makefile.inc b/frontends/verilog/Makefile.inc
new file mode 100644
index 00000000..a06c1d5a
--- /dev/null
+++ b/frontends/verilog/Makefile.inc
@@ -0,0 +1,23 @@
+
+GENFILES += frontends/verilog/verilog_parser.tab.cc
+GENFILES += frontends/verilog/verilog_parser.tab.h
+GENFILES += frontends/verilog/verilog_parser.output
+GENFILES += frontends/verilog/verilog_lexer.cc
+
+frontends/verilog/verilog_parser.tab.cc: frontends/verilog/verilog_parser.y
+ $(Q) mkdir -p $(dir $@)
+ $(P) $(BISON) -d -r all -b frontends/verilog/verilog_parser $<
+ $(Q) mv frontends/verilog/verilog_parser.tab.c frontends/verilog/verilog_parser.tab.cc
+
+frontends/verilog/verilog_parser.tab.h: frontends/verilog/verilog_parser.tab.cc
+
+frontends/verilog/verilog_lexer.cc: frontends/verilog/verilog_lexer.l
+ $(Q) mkdir -p $(dir $@)
+ $(P) flex -o frontends/verilog/verilog_lexer.cc $<
+
+OBJS += frontends/verilog/verilog_parser.tab.o
+OBJS += frontends/verilog/verilog_lexer.o
+OBJS += frontends/verilog/preproc.o
+OBJS += frontends/verilog/verilog_frontend.o
+OBJS += frontends/verilog/const2ast.o
+
diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc
new file mode 100644
index 00000000..4a58357b
--- /dev/null
+++ b/frontends/verilog/const2ast.cc
@@ -0,0 +1,241 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * The Verilog frontend.
+ *
+ * This frontend is using the AST frontend library (see frontends/ast/).
+ * Thus this frontend does not generate RTLIL code directly but creates an
+ * AST directly from the Verilog parse tree and then passes this AST to
+ * the AST frontend library.
+ *
+ * ---
+ *
+ * This file contains an ad-hoc parser for Verilog constants. The Verilog
+ * lexer does only recognize a constant but does not actually split it to its
+ * components. I.e. it just passes the Verilog code for the constant to the
+ * bison parser. The parser then uses the function const2ast() from this file
+ * to create an AST node for the constant.
+ *
+ */
+
+#include "verilog_frontend.h"
+#include "kernel/log.h"
+#include <string.h>
+#include <math.h>
+
+YOSYS_NAMESPACE_BEGIN
+
+using namespace AST;
+
+// divide an arbitrary length decimal number by two and return the rest
+static int my_decimal_div_by_two(std::vector<uint8_t> &digits)
+{
+ int carry = 0;
+ for (size_t i = 0; i < digits.size(); i++) {
+ if (digits[i] >= 10)
+ log_error("Invalid use of [a-fxz?] in decimal constant at %s:%d.\n",
+ current_filename.c_str(), get_line_num());
+ digits[i] += carry * 10;
+ carry = digits[i] % 2;
+ digits[i] /= 2;
+ }
+ while (!digits.empty() && !digits.front())
+ digits.erase(digits.begin());
+ return carry;
+}
+
+// find the number of significant bits in a binary number (not including the sign bit)
+static int my_ilog2(int x)
+{
+ int ret = 0;
+ while (x != 0 && x != -1) {
+ x = x >> 1;
+ ret++;
+ }
+ return ret;
+}
+
+// parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?')
+static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int len_in_bits, int base, char case_type)
+{
+ // all digits in string (MSB at index 0)
+ std::vector<uint8_t> digits;
+
+ while (*str) {
+ if ('0' <= *str && *str <= '9')
+ digits.push_back(*str - '0');
+ else if ('a' <= *str && *str <= 'f')
+ digits.push_back(10 + *str - 'a');
+ else if ('A' <= *str && *str <= 'F')
+ digits.push_back(10 + *str - 'A');
+ else if (*str == 'x' || *str == 'X')
+ digits.push_back(0xf0);
+ else if (*str == 'z' || *str == 'Z')
+ digits.push_back(0xf1);
+ else if (*str == '?')
+ digits.push_back(0xf2);
+ str++;
+ }
+
+ if (base == 10 && GetSize(digits) == 1 && digits.front() >= 0xf0)
+ base = 2;
+
+ data.clear();
+
+ if (base == 10) {
+ while (!digits.empty())
+ data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0);
+ } else {
+ int bits_per_digit = my_ilog2(base-1);
+ for (auto it = digits.rbegin(), e = digits.rend(); it != e; it++) {
+ if (*it > (base-1) && *it < 0xf0)
+ log_error("Digit larger than %d used in in base-%d constant at %s:%d.\n",
+ base-1, base, current_filename.c_str(), get_line_num());
+ for (int i = 0; i < bits_per_digit; i++) {
+ int bitmask = 1 << i;
+ if (*it == 0xf0)
+ data.push_back(case_type == 'x' ? RTLIL::Sa : RTLIL::Sx);
+ else if (*it == 0xf1)
+ data.push_back(case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz);
+ else if (*it == 0xf2)
+ data.push_back(RTLIL::Sa);
+ else
+ data.push_back((*it & bitmask) ? RTLIL::S1 : RTLIL::S0);
+ }
+ }
+ }
+
+ int len = GetSize(data);
+ RTLIL::State msb = data.empty() ? RTLIL::S0 : data.back();
+
+ if (len_in_bits < 0) {
+ if (len < 32)
+ data.resize(32, msb == RTLIL::S0 || msb == RTLIL::S1 ? RTLIL::S0 : msb);
+ return;
+ }
+
+ for (len = len - 1; len >= 0; len--)
+ if (data[len] == RTLIL::S1)
+ break;
+ if (msb == RTLIL::S0 || msb == RTLIL::S1) {
+ len += 1;
+ data.resize(len_in_bits, RTLIL::S0);
+ } else {
+ len += 2;
+ data.resize(len_in_bits, msb);
+ }
+
+ if (len > len_in_bits)
+ log_warning("Literal has a width of %d bit, but value requires %d bit. (%s:%d)\n",
+ len_in_bits, len, current_filename.c_str(), get_line_num());
+}
+
+// convert the Verilog code for a constant to an AST node
+AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn_z)
+{
+ if (warn_z) {
+ AstNode *ret = const2ast(code, case_type);
+ if (std::find(ret->bits.begin(), ret->bits.end(), RTLIL::State::Sz) != ret->bits.end())
+ log_warning("Yosys has only limited support for tri-state logic at the moment. (%s:%d)\n",
+ current_filename.c_str(), get_line_num());
+ return ret;
+ }
+
+ const char *str = code.c_str();
+
+ // Strings
+ if (*str == '"') {
+ int len = strlen(str) - 2;
+ std::vector<RTLIL::State> data;
+ data.reserve(len * 8);
+ for (int i = 0; i < len; i++) {
+ unsigned char ch = str[len - i];
+ for (int j = 0; j < 8; j++) {
+ data.push_back((ch & 1) ? RTLIL::S1 : RTLIL::S0);
+ ch = ch >> 1;
+ }
+ }
+ AstNode *ast = AstNode::mkconst_bits(data, false);
+ ast->str = code;
+ return ast;
+ }
+
+ for (size_t i = 0; i < code.size(); i++)
+ if (code[i] == '_' || code[i] == ' ' || code[i] == '\t' || code[i] == '\r' || code[i] == '\n')
+ code.erase(code.begin()+(i--));
+ str = code.c_str();
+
+ char *endptr;
+ long len_in_bits = strtol(str, &endptr, 10);
+
+ // Simple base-10 integer
+ if (*endptr == 0) {
+ std::vector<RTLIL::State> data;
+ my_strtobin(data, str, -1, 10, case_type);
+ if (data.back() == RTLIL::S1)
+ data.push_back(RTLIL::S0);
+ return AstNode::mkconst_bits(data, true);
+ }
+
+ // unsized constant
+ if (str == endptr)
+ len_in_bits = -1;
+
+ // The "<bits>'s?[bodhBODH]<digits>" syntax
+ if (*endptr == '\'')
+ {
+ std::vector<RTLIL::State> data;
+ bool is_signed = false;
+ if (*(endptr+1) == 's') {
+ is_signed = true;
+ endptr++;
+ }
+ switch (*(endptr+1))
+ {
+ case 'b':
+ case 'B':
+ my_strtobin(data, endptr+2, len_in_bits, 2, case_type);
+ break;
+ case 'o':
+ case 'O':
+ my_strtobin(data, endptr+2, len_in_bits, 8, case_type);
+ break;
+ case 'd':
+ case 'D':
+ my_strtobin(data, endptr+2, len_in_bits, 10, case_type);
+ break;
+ case 'h':
+ case 'H':
+ my_strtobin(data, endptr+2, len_in_bits, 16, case_type);
+ break;
+ default:
+ return NULL;
+ }
+ if (len_in_bits < 0) {
+ if (is_signed && data.back() == RTLIL::S1)
+ data.push_back(RTLIL::S0);
+ }
+ return AstNode::mkconst_bits(data, is_signed);
+ }
+
+ return NULL;
+}
+
+YOSYS_NAMESPACE_END
+
diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc
new file mode 100644
index 00000000..997920b8
--- /dev/null
+++ b/frontends/verilog/preproc.cc
@@ -0,0 +1,452 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * The Verilog frontend.
+ *
+ * This frontend is using the AST frontend library (see frontends/ast/).
+ * Thus this frontend does not generate RTLIL code directly but creates an
+ * AST directly from the Verilog parse tree and then passes this AST to
+ * the AST frontend library.
+ *
+ * ---
+ *
+ * Ad-hoc implementation of a Verilog preprocessor. The directives `define,
+ * `include, `ifdef, `ifndef, `else and `endif are handled here. All other
+ * directives are handled by the lexer (see lexer.l).
+ *
+ */
+
+#include "verilog_frontend.h"
+#include "kernel/log.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+YOSYS_NAMESPACE_BEGIN
+using namespace VERILOG_FRONTEND;
+
+static std::list<std::string> output_code;
+static std::list<std::string> input_buffer;
+static size_t input_buffer_charp;
+
+static void return_char(char ch)
+{
+ if (input_buffer_charp == 0)
+ input_buffer.push_front(std::string() + ch);
+ else
+ input_buffer.front()[--input_buffer_charp] = ch;
+}
+
+static void insert_input(std::string str)
+{
+ if (input_buffer_charp != 0) {
+ input_buffer.front() = input_buffer.front().substr(input_buffer_charp);
+ input_buffer_charp = 0;
+ }
+ input_buffer.push_front(str);
+}
+
+static char next_char()
+{
+ if (input_buffer.empty())
+ return 0;
+
+ log_assert(input_buffer_charp <= input_buffer.front().size());
+ if (input_buffer_charp == input_buffer.front().size()) {
+ input_buffer_charp = 0;
+ input_buffer.pop_front();
+ return next_char();
+ }
+
+ char ch = input_buffer.front()[input_buffer_charp++];
+ return ch == '\r' ? next_char() : ch;
+}
+
+static std::string skip_spaces()
+{
+ std::string spaces;
+ while (1) {
+ char ch = next_char();
+ if (ch == 0)
+ break;
+ if (ch != ' ' && ch != '\t') {
+ return_char(ch);
+ break;
+ }
+ spaces += ch;
+ }
+ return spaces;
+}
+
+static std::string next_token(bool pass_newline = false)
+{
+ std::string token;
+
+ char ch = next_char();
+ if (ch == 0)
+ return token;
+
+ token += ch;
+ if (ch == '\n') {
+ if (pass_newline) {
+ output_code.push_back(token);
+ return "";
+ }
+ return token;
+ }
+
+ if (ch == ' ' || ch == '\t')
+ {
+ while ((ch = next_char()) != 0) {
+ if (ch != ' ' && ch != '\t') {
+ return_char(ch);
+ break;
+ }
+ token += ch;
+ }
+ }
+ else if (ch == '"')
+ {
+ while ((ch = next_char()) != 0) {
+ token += ch;
+ if (ch == '"')
+ break;
+ if (ch == '\\') {
+ if ((ch = next_char()) != 0)
+ token += ch;
+ }
+ }
+ if (token == "\"\"" && (ch = next_char()) != 0) {
+ if (ch == '"')
+ token += ch;
+ else
+ return_char(ch);
+ }
+ }
+ else if (ch == '/')
+ {
+ if ((ch = next_char()) != 0) {
+ if (ch == '/') {
+ token += '*';
+ char last_ch = 0;
+ while ((ch = next_char()) != 0) {
+ if (ch == '\n') {
+ return_char(ch);
+ break;
+ }
+ if (last_ch != '*' || ch != '/') {
+ token += ch;
+ last_ch = ch;
+ }
+ }
+ token += " */";
+ }
+ else if (ch == '*') {
+ token += '*';
+ int newline_count = 0;
+ char last_ch = 0;
+ while ((ch = next_char()) != 0) {
+ if (ch == '\n') {
+ newline_count++;
+ token += ' ';
+ } else
+ token += ch;
+ if (last_ch == '*' && ch == '/')
+ break;
+ last_ch = ch;
+ }
+ while (newline_count-- > 0)
+ return_char('\n');
+ }
+ else
+ return_char(ch);
+ }
+ }
+ else
+ {
+ const char *ok = "abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ$0123456789";
+ if (ch == '`' || strchr(ok, ch) != NULL)
+ while ((ch = next_char()) != 0) {
+ if (strchr(ok, ch) == NULL) {
+ return_char(ch);
+ break;
+ }
+ token += ch;
+ }
+ }
+
+ return token;
+}
+
+static void input_file(std::istream &f, std::string filename)
+{
+ char buffer[513];
+ int rc;
+
+ insert_input("");
+ auto it = input_buffer.begin();
+
+ input_buffer.insert(it, "`file_push \"" + filename + "\"\n");
+ while ((rc = readsome(f, buffer, sizeof(buffer)-1)) > 0) {
+ buffer[rc] = 0;
+ input_buffer.insert(it, buffer);
+ }
+ input_buffer.insert(it, "\n`file_pop\n");
+}
+
+std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map<std::string, std::string> pre_defines_map, const std::list<std::string> include_dirs)
+{
+ std::set<std::string> defines_with_args;
+ std::map<std::string, std::string> defines_map(pre_defines_map);
+ int ifdef_fail_level = 0;
+ bool in_elseif = false;
+
+ output_code.clear();
+ input_buffer.clear();
+ input_buffer_charp = 0;
+
+ input_file(f, filename);
+ defines_map["YOSYS"] = "1";
+ defines_map[formal_mode ? "FORMAL" : "SYNTHESIS"] = "1";
+
+ while (!input_buffer.empty())
+ {
+ std::string tok = next_token();
+ // printf("token: >>%s<<\n", tok != "\n" ? tok.c_str() : "NEWLINE");
+
+ if (tok == "`endif") {
+ if (ifdef_fail_level > 0)
+ ifdef_fail_level--;
+ if (ifdef_fail_level == 0)
+ in_elseif = false;
+ continue;
+ }
+
+ if (tok == "`else") {
+ if (ifdef_fail_level == 0)
+ ifdef_fail_level = 1;
+ else if (ifdef_fail_level == 1 && !in_elseif)
+ ifdef_fail_level = 0;
+ continue;
+ }
+
+ if (tok == "`elsif") {
+ skip_spaces();
+ std::string name = next_token(true);
+ if (ifdef_fail_level == 0)
+ ifdef_fail_level = 1, in_elseif = true;
+ else if (ifdef_fail_level == 1 && defines_map.count(name) != 0)
+ ifdef_fail_level = 0, in_elseif = true;
+ continue;
+ }
+
+ if (tok == "`ifdef") {
+ skip_spaces();
+ std::string name = next_token(true);
+ if (ifdef_fail_level > 0 || defines_map.count(name) == 0)
+ ifdef_fail_level++;
+ continue;
+ }
+
+ if (tok == "`ifndef") {
+ skip_spaces();
+ std::string name = next_token(true);
+ if (ifdef_fail_level > 0 || defines_map.count(name) != 0)
+ ifdef_fail_level++;
+ continue;
+ }
+
+ if (ifdef_fail_level > 0) {
+ if (tok == "\n")
+ output_code.push_back(tok);
+ continue;
+ }
+
+ if (tok == "`include") {
+ skip_spaces();
+ std::string fn = next_token(true);
+ while (1) {
+ size_t pos = fn.find('"');
+ if (pos == std::string::npos)
+ break;
+ if (pos == 0)
+ fn = fn.substr(1);
+ else
+ fn = fn.substr(0, pos) + fn.substr(pos+1);
+ }
+ std::ifstream ff;
+ ff.clear();
+ ff.open(fn.c_str());
+ if (ff.fail() && fn.size() > 0 && fn[0] != '/' && filename.find('/') != std::string::npos) {
+ // if the include file was not found, it is not given with an absolute path, and the
+ // currently read file is given with a path, then try again relative to its directory
+ ff.clear();
+ ff.open(filename.substr(0, filename.rfind('/')+1) + fn);
+ }
+ if (ff.fail() && fn.size() > 0 && fn[0] != '/') {
+ // if the include file was not found and it is not given with an absolute path, then
+ // search it in the include path
+ for (auto incdir : include_dirs) {
+ ff.clear();
+ ff.open(incdir + '/' + fn);
+ if (!ff.fail()) break;
+ }
+ }
+ if (ff.fail())
+ output_code.push_back("`file_notfound " + fn);
+ else
+ input_file(ff, fn);
+ continue;
+ }
+
+ if (tok == "`define") {
+ std::string name, value;
+ std::map<std::string, int> args;
+ skip_spaces();
+ name = next_token(true);
+ bool here_doc_mode = false;
+ int newline_count = 0;
+ int state = 0;
+ if (skip_spaces() != "")
+ state = 3;
+ while (!tok.empty()) {
+ tok = next_token();
+ if (tok == "\"\"\"") {
+ here_doc_mode = !here_doc_mode;
+ continue;
+ }
+ if (state == 0 && tok == "(") {
+ state = 1;
+ skip_spaces();
+ } else
+ if (state == 1) {
+ if (tok == ")")
+ state = 2;
+ else if (tok != ",") {
+ int arg_idx = args.size()+1;
+ args[tok] = arg_idx;
+ }
+ skip_spaces();
+ } else {
+ if (state != 2)
+ state = 3;
+ if (tok == "\n") {
+ if (here_doc_mode) {
+ value += " ";
+ newline_count++;
+ } else {
+ return_char('\n');
+ break;
+ }
+ } else
+ if (tok == "\\") {
+ char ch = next_char();
+ if (ch == '\n') {
+ value += " ";
+ newline_count++;
+ } else {
+ value += std::string("\\");
+ return_char(ch);
+ }
+ } else
+ if (args.count(tok) > 0)
+ value += stringf("`macro_%s_arg%d", name.c_str(), args.at(tok));
+ else
+ value += tok;
+ }
+ }
+ while (newline_count-- > 0)
+ return_char('\n');
+ // printf("define: >>%s<< -> >>%s<<\n", name.c_str(), value.c_str());
+ defines_map[name] = value;
+ if (state == 2)
+ defines_with_args.insert(name);
+ else
+ defines_with_args.erase(name);
+ continue;
+ }
+
+ if (tok == "`undef") {
+ std::string name;
+ skip_spaces();
+ name = next_token(true);
+ // printf("undef: >>%s<<\n", name.c_str());
+ defines_map.erase(name);
+ defines_with_args.erase(name);
+ continue;
+ }
+
+ if (tok == "`timescale") {
+ skip_spaces();
+ while (!tok.empty() && tok != "\n")
+ tok = next_token(true);
+ if (tok == "\n")
+ return_char('\n');
+ continue;
+ }
+
+ if (tok.size() > 1 && tok[0] == '`' && defines_map.count(tok.substr(1)) > 0) {
+ std::string name = tok.substr(1);
+ // printf("expand: >>%s<< -> >>%s<<\n", name.c_str(), defines_map[name].c_str());
+ std::string skipped_spaces = skip_spaces();
+ tok = next_token(false);
+ if (tok == "(" && defines_with_args.count(name) > 0) {
+ int level = 1;
+ std::vector<std::string> args;
+ args.push_back(std::string());
+ while (1)
+ {
+ tok = next_token(true);
+ if (tok == ")" || tok == "}" || tok == "]")
+ level--;
+ if (level == 0)
+ break;
+ if (level == 1 && tok == ",")
+ args.push_back(std::string());
+ else
+ args.back() += tok;
+ if (tok == "(" || tok == "{" || tok == "[")
+ level++;
+ }
+ for (int i = 0; i < GetSize(args); i++)
+ defines_map[stringf("macro_%s_arg%d", name.c_str(), i+1)] = args[i];
+ } else {
+ insert_input(tok);
+ insert_input(skipped_spaces);
+ }
+ insert_input(defines_map[name]);
+ continue;
+ }
+
+ output_code.push_back(tok);
+ }
+
+ std::string output;
+ for (auto &str : output_code)
+ output += str;
+
+ output_code.clear();
+ input_buffer.clear();
+ input_buffer_charp = 0;
+
+ return output;
+}
+
+YOSYS_NAMESPACE_END
+
diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc
new file mode 100644
index 00000000..894723c8
--- /dev/null
+++ b/frontends/verilog/verilog_frontend.cc
@@ -0,0 +1,456 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * The Verilog frontend.
+ *
+ * This frontend is using the AST frontend library (see frontends/ast/).
+ * Thus this frontend does not generate RTLIL code directly but creates an
+ * AST directly from the Verilog parse tree and then passes this AST to
+ * the AST frontend library.
+ *
+ */
+
+#include "verilog_frontend.h"
+#include "kernel/yosys.h"
+#include "libs/sha1/sha1.h"
+#include <stdarg.h>
+
+YOSYS_NAMESPACE_BEGIN
+using namespace VERILOG_FRONTEND;
+
+// use the Verilog bison/flex parser to generate an AST and use AST::process() to convert it to RTLIL
+
+static std::vector<std::string> verilog_defaults;
+static std::list<std::vector<std::string>> verilog_defaults_stack;
+
+static void error_on_dpi_function(AST::AstNode *node)
+{
+ if (node->type == AST::AST_DPI_FUNCTION)
+ log_error("Found DPI function %s at %s:%d.\n", node->str.c_str(), node->filename.c_str(), node->linenum);
+ for (auto child : node->children)
+ error_on_dpi_function(child);
+}
+
+struct VerilogFrontend : public Frontend {
+ VerilogFrontend() : Frontend("verilog", "read modules from Verilog file") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" read_verilog [options] [filename]\n");
+ log("\n");
+ log("Load modules from a Verilog file to the current design. A large subset of\n");
+ log("Verilog-2005 is supported.\n");
+ log("\n");
+ log(" -sv\n");
+ log(" enable support for SystemVerilog features. (only a small subset\n");
+ log(" of SystemVerilog is supported)\n");
+ log("\n");
+ log(" -formal\n");
+ log(" enable support for SystemVerilog assertions and some Yosys extensions\n");
+ log(" replace the implicit -D SYNTHESIS with -D FORMAL\n");
+ log("\n");
+ log(" -norestrict\n");
+ log(" ignore restrict() assertions\n");
+ log("\n");
+ log(" -assume-asserts\n");
+ log(" treat all assert() statements like assume() statements\n");
+ log("\n");
+ log(" -dump_ast1\n");
+ log(" dump abstract syntax tree (before simplification)\n");
+ log("\n");
+ log(" -dump_ast2\n");
+ log(" dump abstract syntax tree (after simplification)\n");
+ log("\n");
+ log(" -dump_vlog\n");
+ log(" dump ast as Verilog code (after simplification)\n");
+ log("\n");
+ log(" -dump_rtlil\n");
+ log(" dump generated RTLIL netlist\n");
+ log("\n");
+ log(" -yydebug\n");
+ log(" enable parser debug output\n");
+ log("\n");
+ log(" -nolatches\n");
+ log(" usually latches are synthesized into logic loops\n");
+ log(" this option prohibits this and sets the output to 'x'\n");
+ log(" in what would be the latches hold condition\n");
+ log("\n");
+ log(" this behavior can also be achieved by setting the\n");
+ log(" 'nolatches' attribute on the respective module or\n");
+ log(" always block.\n");
+ log("\n");
+ log(" -nomem2reg\n");
+ log(" under certain conditions memories are converted to registers\n");
+ log(" early during simplification to ensure correct handling of\n");
+ log(" complex corner cases. this option disables this behavior.\n");
+ log("\n");
+ log(" this can also be achieved by setting the 'nomem2reg'\n");
+ log(" attribute on the respective module or register.\n");
+ log("\n");
+ log(" This is potentially dangerous. Usually the front-end has good\n");
+ log(" reasons for converting an array to a list of registers.\n");
+ log(" Prohibiting this step will likely result in incorrect synthesis\n");
+ log(" results.\n");
+ log("\n");
+ log(" -mem2reg\n");
+ log(" always convert memories to registers. this can also be\n");
+ log(" achieved by setting the 'mem2reg' attribute on the respective\n");
+ log(" module or register.\n");
+ log("\n");
+ log(" -nomeminit\n");
+ log(" do not infer $meminit cells and instead convert initialized\n");
+ log(" memories to registers directly in the front-end.\n");
+ log("\n");
+ log(" -ppdump\n");
+ log(" dump Verilog code after pre-processor\n");
+ log("\n");
+ log(" -nopp\n");
+ log(" do not run the pre-processor\n");
+ log("\n");
+ log(" -nodpi\n");
+ log(" disable DPI-C support\n");
+ log("\n");
+ log(" -lib\n");
+ log(" only create empty blackbox modules. This implies -DBLACKBOX.\n");
+ log("\n");
+ log(" -noopt\n");
+ log(" don't perform basic optimizations (such as const folding) in the\n");
+ log(" high-level front-end.\n");
+ log("\n");
+ log(" -icells\n");
+ log(" interpret cell types starting with '$' as internal cell types\n");
+ log("\n");
+ log(" -ignore_redef\n");
+ log(" ignore re-definitions of modules. (the default behavior is to\n");
+ log(" create an error message.)\n");
+ log("\n");
+ log(" -defer\n");
+ log(" only read the abstract syntax tree and defer actual compilation\n");
+ log(" to a later 'hierarchy' command. Useful in cases where the default\n");
+ log(" parameters of modules yield invalid or not synthesizable code.\n");
+ log("\n");
+ log(" -noautowire\n");
+ log(" make the default of `default_nettype be \"none\" instead of \"wire\".\n");
+ log("\n");
+ log(" -setattr <attribute_name>\n");
+ log(" set the specified attribute (to the value 1) on all loaded modules\n");
+ log("\n");
+ log(" -Dname[=definition]\n");
+ log(" define the preprocessor symbol 'name' and set its optional value\n");
+ log(" 'definition'\n");
+ log("\n");
+ log(" -Idir\n");
+ log(" add 'dir' to the directories which are used when searching include\n");
+ log(" files\n");
+ log("\n");
+ log("The command 'verilog_defaults' can be used to register default options for\n");
+ log("subsequent calls to 'read_verilog'.\n");
+ log("\n");
+ log("Note that the Verilog frontend does a pretty good job of processing valid\n");
+ log("verilog input, but has not very good error reporting. It generally is\n");
+ log("recommended to use a simulator (for example Icarus Verilog) for checking\n");
+ log("the syntax of the code, rather than to rely on read_verilog for that.\n");
+ log("\n");
+ log("See the Yosys README file for a list of non-standard Verilog features\n");
+ log("supported by the Yosys Verilog front-end.\n");
+ log("\n");
+ }
+ virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool flag_dump_ast1 = false;
+ bool flag_dump_ast2 = false;
+ bool flag_dump_vlog = false;
+ bool flag_dump_rtlil = false;
+ bool flag_nolatches = false;
+ bool flag_nomeminit = false;
+ bool flag_nomem2reg = false;
+ bool flag_mem2reg = false;
+ bool flag_ppdump = false;
+ bool flag_nopp = false;
+ bool flag_nodpi = false;
+ bool flag_noopt = false;
+ bool flag_icells = false;
+ bool flag_ignore_redef = false;
+ bool flag_defer = false;
+ std::map<std::string, std::string> defines_map;
+ std::list<std::string> include_dirs;
+ std::list<std::string> attributes;
+
+ frontend_verilog_yydebug = false;
+ sv_mode = false;
+ formal_mode = false;
+ norestrict_mode = false;
+ assume_asserts_mode = false;
+ lib_mode = false;
+ default_nettype_wire = true;
+
+ log_header(design, "Executing Verilog-2005 frontend.\n");
+
+ args.insert(args.begin()+1, verilog_defaults.begin(), verilog_defaults.end());
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-sv") {
+ sv_mode = true;
+ continue;
+ }
+ if (arg == "-formal") {
+ formal_mode = true;
+ continue;
+ }
+ if (arg == "-norestrict") {
+ norestrict_mode = true;
+ continue;
+ }
+ if (arg == "-assume-asserts") {
+ assume_asserts_mode = true;
+ continue;
+ }
+ if (arg == "-dump_ast1") {
+ flag_dump_ast1 = true;
+ continue;
+ }
+ if (arg == "-dump_ast2") {
+ flag_dump_ast2 = true;
+ continue;
+ }
+ if (arg == "-dump_vlog") {
+ flag_dump_vlog = true;
+ continue;
+ }
+ if (arg == "-dump_rtlil") {
+ flag_dump_rtlil = true;
+ continue;
+ }
+ if (arg == "-yydebug") {
+ frontend_verilog_yydebug = true;
+ continue;
+ }
+ if (arg == "-nolatches") {
+ flag_nolatches = true;
+ continue;
+ }
+ if (arg == "-nomeminit") {
+ flag_nomeminit = true;
+ continue;
+ }
+ if (arg == "-nomem2reg") {
+ flag_nomem2reg = true;
+ continue;
+ }
+ if (arg == "-mem2reg") {
+ flag_mem2reg = true;
+ continue;
+ }
+ if (arg == "-ppdump") {
+ flag_ppdump = true;
+ continue;
+ }
+ if (arg == "-nopp") {
+ flag_nopp = true;
+ continue;
+ }
+ if (arg == "-nodpi") {
+ flag_nodpi = true;
+ continue;
+ }
+ if (arg == "-lib") {
+ lib_mode = true;
+ defines_map["BLACKBOX"] = string();
+ continue;
+ }
+ if (arg == "-noopt") {
+ flag_noopt = true;
+ continue;
+ }
+ if (arg == "-icells") {
+ flag_icells = true;
+ continue;
+ }
+ if (arg == "-ignore_redef") {
+ flag_ignore_redef = true;
+ continue;
+ }
+ if (arg == "-defer") {
+ flag_defer = true;
+ continue;
+ }
+ if (arg == "-noautowire") {
+ default_nettype_wire = false;
+ continue;
+ }
+ if (arg == "-setattr" && argidx+1 < args.size()) {
+ attributes.push_back(RTLIL::escape_id(args[++argidx]));
+ continue;
+ }
+ if (arg == "-D" && argidx+1 < args.size()) {
+ std::string name = args[++argidx], value;
+ size_t equal = name.find('=', 2);
+ if (equal != std::string::npos) {
+ value = arg.substr(equal+1);
+ name = arg.substr(0, equal);
+ }
+ defines_map[name] = value;
+ continue;
+ }
+ if (arg.compare(0, 2, "-D") == 0) {
+ size_t equal = arg.find('=', 2);
+ std::string name = arg.substr(2, equal-2);
+ std::string value;
+ if (equal != std::string::npos)
+ value = arg.substr(equal+1);
+ defines_map[name] = value;
+ continue;
+ }
+ if (arg == "-I" && argidx+1 < args.size()) {
+ include_dirs.push_back(args[++argidx]);
+ continue;
+ }
+ if (arg.compare(0, 2, "-I") == 0) {
+ include_dirs.push_back(arg.substr(2));
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ log("Parsing %s%s input from `%s' to AST representation.\n",
+ formal_mode ? "formal " : "", sv_mode ? "SystemVerilog" : "Verilog", filename.c_str());
+
+ AST::current_filename = filename;
+ AST::set_line_num = &frontend_verilog_yyset_lineno;
+ AST::get_line_num = &frontend_verilog_yyget_lineno;
+
+ current_ast = new AST::AstNode(AST::AST_DESIGN);
+
+ lexin = f;
+ std::string code_after_preproc;
+
+ if (!flag_nopp) {
+ code_after_preproc = frontend_verilog_preproc(*f, filename, defines_map, include_dirs);
+ if (flag_ppdump)
+ log("-- Verilog code after preprocessor --\n%s-- END OF DUMP --\n", code_after_preproc.c_str());
+ lexin = new std::istringstream(code_after_preproc);
+ }
+
+ frontend_verilog_yyset_lineno(1);
+ frontend_verilog_yyrestart(NULL);
+ frontend_verilog_yyparse();
+ frontend_verilog_yylex_destroy();
+
+ for (auto &child : current_ast->children) {
+ if (child->type == AST::AST_MODULE)
+ for (auto &attr : attributes)
+ if (child->attributes.count(attr) == 0)
+ child->attributes[attr] = AST::AstNode::mkconst_int(1, false);
+ }
+
+ if (flag_nodpi)
+ error_on_dpi_function(current_ast);
+
+ AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_dump_rtlil, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, lib_mode, flag_noopt, flag_icells, flag_ignore_redef, flag_defer, default_nettype_wire);
+
+ if (!flag_nopp)
+ delete lexin;
+
+ delete current_ast;
+ current_ast = NULL;
+
+ log("Successfully finished Verilog frontend.\n");
+ }
+} VerilogFrontend;
+
+struct VerilogDefaults : public Pass {
+ VerilogDefaults() : Pass("verilog_defaults", "set default options for read_verilog") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" verilog_defaults -add [options]\n");
+ log("\n");
+ log("Add the specified options to the list of default options to read_verilog.\n");
+ log("\n");
+ log("\n");
+ log(" verilog_defaults -clear\n");
+ log("\n");
+ log("Clear the list of Verilog default options.\n");
+ log("\n");
+ log("\n");
+ log(" verilog_defaults -push\n");
+ log(" verilog_defaults -pop\n");
+ log("\n");
+ log("Push or pop the list of default options to a stack. Note that -push does\n");
+ log("not imply -clear.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design*)
+ {
+ if (args.size() == 0)
+ cmd_error(args, 1, "Missing argument.");
+
+ if (args[1] == "-add") {
+ verilog_defaults.insert(verilog_defaults.end(), args.begin()+2, args.end());
+ return;
+ }
+
+ if (args.size() != 2)
+ cmd_error(args, 2, "Extra argument.");
+
+ if (args[1] == "-clear") {
+ verilog_defaults.clear();
+ return;
+ }
+
+ if (args[1] == "-push") {
+ verilog_defaults_stack.push_back(verilog_defaults);
+ return;
+ }
+
+ if (args[1] == "-pop") {
+ if (verilog_defaults_stack.empty()) {
+ verilog_defaults.clear();
+ } else {
+ verilog_defaults.swap(verilog_defaults_stack.back());
+ verilog_defaults_stack.pop_back();
+ }
+ return;
+ }
+ }
+} VerilogDefaults;
+
+YOSYS_NAMESPACE_END
+
+// the yyerror function used by bison to report parser errors
+void frontend_verilog_yyerror(char const *fmt, ...)
+{
+ va_list ap;
+ char buffer[1024];
+ char *p = buffer;
+ p += snprintf(p, buffer + sizeof(buffer) - p, "Parser error in line %s:%d: ",
+ YOSYS_NAMESPACE_PREFIX AST::current_filename.c_str(), frontend_verilog_yyget_lineno());
+ va_start(ap, fmt);
+ p += vsnprintf(p, buffer + sizeof(buffer) - p, fmt, ap);
+ va_end(ap);
+ p += snprintf(p, buffer + sizeof(buffer) - p, "\n");
+ YOSYS_NAMESPACE_PREFIX log_error("%s", buffer);
+ exit(1);
+}
+
diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h
new file mode 100644
index 00000000..606ec20a
--- /dev/null
+++ b/frontends/verilog/verilog_frontend.h
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * The Verilog frontend.
+ *
+ * This frontend is using the AST frontend library (see frontends/ast/).
+ * Thus this frontend does not generate RTLIL code directly but creates an
+ * AST directly from the Verilog parse tree and then passes this AST to
+ * the AST frontend library.
+ *
+ */
+
+#ifndef VERILOG_FRONTEND_H
+#define VERILOG_FRONTEND_H
+
+#include "kernel/yosys.h"
+#include "frontends/ast/ast.h"
+#include <stdio.h>
+#include <stdint.h>
+#include <list>
+
+YOSYS_NAMESPACE_BEGIN
+
+namespace VERILOG_FRONTEND
+{
+ // this variable is set to a new AST_DESIGN node and then filled with the AST by the bison parser
+ extern struct AST::AstNode *current_ast;
+
+ // this function converts a Verilog constant to an AST_CONSTANT node
+ AST::AstNode *const2ast(std::string code, char case_type = 0, bool warn_z = false);
+
+ // state of `default_nettype
+ extern bool default_nettype_wire;
+
+ // running in SystemVerilog mode
+ extern bool sv_mode;
+
+ // running in -formal mode
+ extern bool formal_mode;
+
+ // running in -norestrict mode
+ extern bool norestrict_mode;
+
+ // running in -assume-asserts mode
+ extern bool assume_asserts_mode;
+
+ // running in -lib mode
+ extern bool lib_mode;
+
+ // lexer input stream
+ extern std::istream *lexin;
+}
+
+// the pre-processor
+std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map<std::string, std::string> pre_defines_map, const std::list<std::string> include_dirs);
+
+YOSYS_NAMESPACE_END
+
+// the usual bison/flex stuff
+extern int frontend_verilog_yydebug;
+int frontend_verilog_yylex(void);
+void frontend_verilog_yyerror(char const *fmt, ...);
+void frontend_verilog_yyrestart(FILE *f);
+int frontend_verilog_yyparse(void);
+int frontend_verilog_yylex_destroy(void);
+int frontend_verilog_yyget_lineno(void);
+void frontend_verilog_yyset_lineno (int);
+
+#endif
diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l
new file mode 100644
index 00000000..405aeb97
--- /dev/null
+++ b/frontends/verilog/verilog_lexer.l
@@ -0,0 +1,387 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * The Verilog frontend.
+ *
+ * This frontend is using the AST frontend library (see frontends/ast/).
+ * Thus this frontend does not generate RTLIL code directly but creates an
+ * AST directly from the Verilog parse tree and then passes this AST to
+ * the AST frontend library.
+ *
+ * ---
+ *
+ * A simple lexer for Verilog code. Non-preprocessor compiler directives are
+ * handled here. The preprocessor stuff is handled in preproc.cc. Everything
+ * else is left to the bison parser (see parser.y).
+ *
+ */
+
+%{
+
+#ifdef __clang__
+// bison generates code using the 'register' storage class specifier
+#pragma clang diagnostic ignored "-Wdeprecated-register"
+#endif
+
+#include "kernel/log.h"
+#include "frontends/verilog/verilog_frontend.h"
+#include "frontends/ast/ast.h"
+#include "verilog_parser.tab.h"
+
+USING_YOSYS_NAMESPACE
+using namespace AST;
+using namespace VERILOG_FRONTEND;
+
+YOSYS_NAMESPACE_BEGIN
+namespace VERILOG_FRONTEND {
+ std::vector<std::string> fn_stack;
+ std::vector<int> ln_stack;
+}
+YOSYS_NAMESPACE_END
+
+#define SV_KEYWORD(_tok) \
+ if (sv_mode) return _tok; \
+ log("Lexer warning: The SystemVerilog keyword `%s' (at %s:%d) is not "\
+ "recognized unless read_verilog is called with -sv!\n", yytext, \
+ AST::current_filename.c_str(), frontend_verilog_yyget_lineno()); \
+ frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext); \
+ return TOK_ID;
+
+#define NON_KEYWORD() \
+ frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext); \
+ return TOK_ID;
+
+#define YY_INPUT(buf,result,max_size) \
+ result = readsome(*VERILOG_FRONTEND::lexin, buf, max_size)
+
+%}
+
+%option yylineno
+%option noyywrap
+%option nounput
+%option prefix="frontend_verilog_yy"
+
+%x COMMENT
+%x STRING
+%x SYNOPSYS_TRANSLATE_OFF
+%x SYNOPSYS_FLAGS
+%x IMPORT_DPI
+
+%%
+
+<INITIAL,SYNOPSYS_TRANSLATE_OFF>"`file_push "[^\n]* {
+ fn_stack.push_back(current_filename);
+ ln_stack.push_back(frontend_verilog_yyget_lineno());
+ current_filename = yytext+11;
+ if (!current_filename.empty() && current_filename.front() == '"')
+ current_filename = current_filename.substr(1);
+ if (!current_filename.empty() && current_filename.back() == '"')
+ current_filename = current_filename.substr(0, current_filename.size()-1);
+ frontend_verilog_yyset_lineno(0);
+}
+
+<INITIAL,SYNOPSYS_TRANSLATE_OFF>"`file_pop"[^\n]*\n {
+ current_filename = fn_stack.back();
+ fn_stack.pop_back();
+ frontend_verilog_yyset_lineno(ln_stack.back());
+ ln_stack.pop_back();
+}
+
+<INITIAL,SYNOPSYS_TRANSLATE_OFF>"`line"[ \t]+[^ \t\r\n]+[ \t]+\"[^ \r\n]+\"[^\r\n]*\n {
+ char *p = yytext + 5;
+ while (*p == ' ' || *p == '\t') p++;
+ frontend_verilog_yyset_lineno(atoi(p));
+ while (*p && *p != ' ' && *p != '\t') p++;
+ while (*p == ' ' || *p == '\t') p++;
+ char *q = *p ? p + 1 : p;
+ while (*q && *q != '"') q++;
+ current_filename = std::string(p).substr(1, q-p-1);
+}
+
+"`file_notfound "[^\n]* {
+ log_error("Can't open include file `%s'!\n", yytext + 15);
+}
+
+"`timescale"[ \t]+[^ \t\r\n/]+[ \t]*"/"[ \t]*[^ \t\r\n]* /* ignore timescale directive */
+
+"`celldefine"[^\n]* /* ignore `celldefine */
+"`endcelldefine"[^\n]* /* ignore `endcelldefine */
+
+"`default_nettype"[ \t]+[^ \t\r\n/]+ {
+ char *p = yytext;
+ while (*p != 0 && *p != ' ' && *p != '\t') p++;
+ while (*p == ' ' || *p == '\t') p++;
+ if (!strcmp(p, "none"))
+ VERILOG_FRONTEND::default_nettype_wire = false;
+ else if (!strcmp(p, "wire"))
+ VERILOG_FRONTEND::default_nettype_wire = true;
+ else
+ frontend_verilog_yyerror("Unsupported default nettype: %s", p);
+}
+
+"`"[a-zA-Z_$][a-zA-Z0-9_$]* {
+ frontend_verilog_yyerror("Unimplemented compiler directive or undefined macro %s.", yytext);
+}
+
+"module" { return TOK_MODULE; }
+"endmodule" { return TOK_ENDMODULE; }
+"function" { return TOK_FUNCTION; }
+"endfunction" { return TOK_ENDFUNCTION; }
+"task" { return TOK_TASK; }
+"endtask" { return TOK_ENDTASK; }
+"package" { SV_KEYWORD(TOK_PACKAGE); }
+"endpackage" { SV_KEYWORD(TOK_ENDPACKAGE); }
+"parameter" { return TOK_PARAMETER; }
+"localparam" { return TOK_LOCALPARAM; }
+"defparam" { return TOK_DEFPARAM; }
+"assign" { return TOK_ASSIGN; }
+"always" { return TOK_ALWAYS; }
+"initial" { return TOK_INITIAL; }
+"begin" { return TOK_BEGIN; }
+"end" { return TOK_END; }
+"if" { return TOK_IF; }
+"else" { return TOK_ELSE; }
+"for" { return TOK_FOR; }
+"posedge" { return TOK_POSEDGE; }
+"negedge" { return TOK_NEGEDGE; }
+"or" { return TOK_OR; }
+"case" { return TOK_CASE; }
+"casex" { return TOK_CASEX; }
+"casez" { return TOK_CASEZ; }
+"endcase" { return TOK_ENDCASE; }
+"default" { return TOK_DEFAULT; }
+"generate" { return TOK_GENERATE; }
+"endgenerate" { return TOK_ENDGENERATE; }
+"while" { return TOK_WHILE; }
+"repeat" { return TOK_REPEAT; }
+
+"always_comb" { SV_KEYWORD(TOK_ALWAYS); }
+"always_ff" { SV_KEYWORD(TOK_ALWAYS); }
+"always_latch" { SV_KEYWORD(TOK_ALWAYS); }
+
+"assert" { if (formal_mode) return TOK_ASSERT; SV_KEYWORD(TOK_ASSERT); }
+"assume" { if (formal_mode) return TOK_ASSUME; SV_KEYWORD(TOK_ASSUME); }
+"restrict" { if (formal_mode) return TOK_RESTRICT; SV_KEYWORD(TOK_RESTRICT); }
+"property" { if (formal_mode) return TOK_PROPERTY; SV_KEYWORD(TOK_PROPERTY); }
+"logic" { SV_KEYWORD(TOK_REG); }
+"bit" { SV_KEYWORD(TOK_REG); }
+
+"input" { return TOK_INPUT; }
+"output" { return TOK_OUTPUT; }
+"inout" { return TOK_INOUT; }
+"wire" { return TOK_WIRE; }
+"reg" { return TOK_REG; }
+"integer" { return TOK_INTEGER; }
+"signed" { return TOK_SIGNED; }
+"genvar" { return TOK_GENVAR; }
+"real" { return TOK_REAL; }
+
+[0-9][0-9_]* {
+ frontend_verilog_yylval.string = new std::string(yytext);
+ return TOK_CONST;
+}
+
+[0-9]*[ \t]*\'s?[bodhBODH][ \t\r\n]*[0-9a-fA-FzxZX?_]+ {
+ frontend_verilog_yylval.string = new std::string(yytext);
+ return TOK_CONST;
+}
+
+[0-9][0-9_]*\.[0-9][0-9_]*([eE][-+]?[0-9_]+)? {
+ frontend_verilog_yylval.string = new std::string(yytext);
+ return TOK_REALVAL;
+}
+
+[0-9][0-9_]*[eE][-+]?[0-9_]+ {
+ frontend_verilog_yylval.string = new std::string(yytext);
+ return TOK_REALVAL;
+}
+
+\" { BEGIN(STRING); }
+<STRING>\\. { yymore(); }
+<STRING>\" {
+ BEGIN(0);
+ char *yystr = strdup(yytext);
+ yystr[strlen(yytext) - 1] = 0;
+ int i = 0, j = 0;
+ while (yystr[i]) {
+ if (yystr[i] == '\\' && yystr[i + 1]) {
+ i++;
+ if (yystr[i] == 'n')
+ yystr[i] = '\n';
+ else if (yystr[i] == 't')
+ yystr[i] = '\t';
+ else if ('0' <= yystr[i] && yystr[i] <= '7') {
+ yystr[i] = yystr[i] - '0';
+ if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') {
+ yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0';
+ i++;
+ }
+ if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') {
+ yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0';
+ i++;
+ }
+ }
+ }
+ yystr[j++] = yystr[i++];
+ }
+ yystr[j] = 0;
+ frontend_verilog_yylval.string = new std::string(yystr);
+ free(yystr);
+ return TOK_STRING;
+}
+<STRING>. { yymore(); }
+
+and|nand|or|nor|xor|xnor|not|buf|bufif0|bufif1|notif0|notif1 {
+ frontend_verilog_yylval.string = new std::string(yytext);
+ return TOK_PRIMITIVE;
+}
+
+supply0 { return TOK_SUPPLY0; }
+supply1 { return TOK_SUPPLY1; }
+
+"$"(display|write|strobe|monitor|time|stop|finish|dumpfile|dumpvars|dumpon|dumpoff|dumpall) {
+ frontend_verilog_yylval.string = new std::string(yytext);
+ return TOK_ID;
+}
+
+"$signed" { return TOK_TO_SIGNED; }
+"$unsigned" { return TOK_TO_UNSIGNED; }
+
+[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) {
+ log_warning("Found one of those horrible `(synopsys|synthesis) translate_off' comments.\n"
+ "Yosys does support them but it is recommended to use `ifdef constructs instead!\n");
+ printed_warning = true;
+ }
+ BEGIN(SYNOPSYS_TRANSLATE_OFF);
+}
+<SYNOPSYS_TRANSLATE_OFF>. /* ignore synopsys translate_off body */
+<SYNOPSYS_TRANSLATE_OFF>\n /* ignore synopsys translate_off body */
+<SYNOPSYS_TRANSLATE_OFF>"/*"[ \t]*(synopsys|synthesis)[ \t]*"translate_on"[ \t]*"*/" { BEGIN(0); }
+
+"/*"[ \t]*(synopsys|synthesis)[ \t]+ {
+ BEGIN(SYNOPSYS_FLAGS);
+}
+<SYNOPSYS_FLAGS>full_case {
+ static bool printed_warning = false;
+ if (!printed_warning) {
+ log_warning("Found one of those horrible `(synopsys|synthesis) full_case' comments.\n"
+ "Yosys does support them but it is recommended to use Verilog `full_case' attributes instead!\n");
+ printed_warning = true;
+ }
+ return TOK_SYNOPSYS_FULL_CASE;
+}
+<SYNOPSYS_FLAGS>parallel_case {
+ static bool printed_warning = false;
+ if (!printed_warning) {
+ log_warning("Found one of those horrible `(synopsys|synthesis) parallel_case' comments.\n"
+ "Yosys does support them but it is recommended to use Verilog `parallel_case' attributes instead!\n");
+ printed_warning = true;
+ }
+ return TOK_SYNOPSYS_PARALLEL_CASE;
+}
+<SYNOPSYS_FLAGS>. /* ignore everything else */
+<SYNOPSYS_FLAGS>"*/" { BEGIN(0); }
+
+import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ {
+ BEGIN(IMPORT_DPI);
+ return TOK_DPI_FUNCTION;
+}
+
+<IMPORT_DPI>[a-zA-Z_$][a-zA-Z0-9_$]* {
+ frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext);
+ return TOK_ID;
+}
+
+<IMPORT_DPI>[ \t\r\n] /* ignore whitespaces */
+
+<IMPORT_DPI>";" {
+ BEGIN(0);
+ return *yytext;
+}
+
+<IMPORT_DPI>. {
+ return *yytext;
+}
+
+"\\"[^ \t\r\n]+ {
+ frontend_verilog_yylval.string = new std::string(yytext);
+ return TOK_ID;
+}
+
+"(*" { return ATTR_BEGIN; }
+"*)" { return ATTR_END; }
+
+"{*" { return DEFATTR_BEGIN; }
+"*}" { return DEFATTR_END; }
+
+"**" { return OP_POW; }
+"||" { return OP_LOR; }
+"&&" { return OP_LAND; }
+"==" { return OP_EQ; }
+"!=" { return OP_NE; }
+"<=" { return OP_LE; }
+">=" { return OP_GE; }
+
+"===" { return OP_EQX; }
+"!==" { return OP_NEX; }
+
+"~&" { return OP_NAND; }
+"~|" { return OP_NOR; }
+"~^" { return OP_XNOR; }
+"^~" { return OP_XNOR; }
+
+"<<" { return OP_SHL; }
+">>" { return OP_SHR; }
+"<<<" { return OP_SSHL; }
+">>>" { return OP_SSHR; }
+
+"::" { SV_KEYWORD(TOK_PACKAGESEP); }
+
+"+:" { return TOK_POS_INDEXED; }
+"-:" { return TOK_NEG_INDEXED; }
+
+"/*" { BEGIN(COMMENT); }
+<COMMENT>. /* ignore comment body */
+<COMMENT>\n /* ignore comment body */
+<COMMENT>"*/" { BEGIN(0); }
+
+[ \t\r\n] /* ignore whitespaces */
+\\[\r\n] /* ignore continuation sequence */
+"//"[^\r\n]* /* ignore one-line comments */
+
+"#"\ *[0-9][0-9_]* /* ignore simulation timings */
+"#"\ *[0-9][0-9_]*\.[0-9][0-9_]* /* ignore simulation timings */
+"#"\ *[$a-zA-Z_\.][$a-zA-Z_0-9\.]* /* ignore simulation timings */
+
+. { return *yytext; }
+
+%%
+
+// this is a hack to avoid the 'yyinput defined but not used' error msgs
+void *frontend_verilog_avoid_input_warnings() {
+ return (void*)&yyinput;
+}
+
diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y
new file mode 100644
index 00000000..5bbda535
--- /dev/null
+++ b/frontends/verilog/verilog_parser.y
@@ -0,0 +1,1583 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * The Verilog frontend.
+ *
+ * This frontend is using the AST frontend library (see frontends/ast/).
+ * Thus this frontend does not generate RTLIL code directly but creates an
+ * AST directly from the Verilog parse tree and then passes this AST to
+ * the AST frontend library.
+ *
+ * ---
+ *
+ * This is the actual bison parser for Verilog code. The AST ist created directly
+ * from the bison reduce functions here. Note that this code uses a few global
+ * variables to hold the state of the AST generator and therefore this parser is
+ * not reentrant.
+ *
+ */
+
+%{
+#include <list>
+#include <string.h>
+#include "frontends/verilog/verilog_frontend.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+using namespace AST;
+using namespace VERILOG_FRONTEND;
+
+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*> *albuf;
+ std::vector<AstNode*> ast_stack;
+ struct AstNode *astbuf1, *astbuf2, *astbuf3;
+ struct AstNode *current_function_or_task;
+ struct AstNode *current_ast, *current_ast_mod;
+ int current_function_or_task_port_id;
+ std::vector<char> case_type_stack;
+ bool do_not_require_port_stubs;
+ bool default_nettype_wire;
+ bool sv_mode, formal_mode, lib_mode;
+ bool norestrict_mode, assume_asserts_mode;
+ std::istream *lexin;
+}
+YOSYS_NAMESPACE_END
+
+static void append_attr(AstNode *ast, std::map<std::string, AstNode*> *al)
+{
+ for (auto &it : *al) {
+ if (ast->attributes.count(it.first) > 0)
+ delete ast->attributes[it.first];
+ ast->attributes[it.first] = it.second;
+ }
+ delete al;
+}
+
+static void append_attr_clone(AstNode *ast, std::map<std::string, AstNode*> *al)
+{
+ for (auto &it : *al) {
+ if (ast->attributes.count(it.first) > 0)
+ delete ast->attributes[it.first];
+ ast->attributes[it.first] = it.second->clone();
+ }
+}
+
+static void free_attr(std::map<std::string, AstNode*> *al)
+{
+ for (auto &it : *al)
+ delete it.second;
+ delete al;
+}
+
+%}
+
+%name-prefix "frontend_verilog_yy"
+
+%union {
+ std::string *string;
+ struct YOSYS_NAMESPACE_PREFIX AST::AstNode *ast;
+ std::map<std::string, YOSYS_NAMESPACE_PREFIX AST::AstNode*> *al;
+ bool boolean;
+}
+
+%token <string> TOK_STRING TOK_ID TOK_CONST TOK_REALVAL TOK_PRIMITIVE
+%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_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG
+%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL
+%token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT
+%token TOK_DPI_FUNCTION TOK_POSEDGE TOK_NEGEDGE TOK_OR
+%token TOK_CASE TOK_CASEX TOK_CASEZ TOK_ENDCASE TOK_DEFAULT
+%token TOK_FUNCTION TOK_ENDFUNCTION TOK_TASK TOK_ENDTASK
+%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_PROPERTY
+
+%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
+%type <al> attr
+
+// operator precedence from low to high
+%left OP_LOR
+%left OP_LAND
+%left '|' OP_NOR
+%left '^' OP_XNOR
+%left '&' OP_NAND
+%left OP_EQ OP_NE OP_EQX OP_NEX
+%left '<' OP_LE OP_GE '>'
+%left OP_SHL OP_SHR OP_SSHL OP_SSHR
+%left '+' '-'
+%left '*' '/' '%'
+%left OP_POW
+%right UNARY_OPS
+
+%define parse.error verbose
+%define parse.lac full
+
+%expect 2
+%debug
+
+%%
+
+input: {
+ ast_stack.clear();
+ ast_stack.push_back(current_ast);
+} design {
+ ast_stack.pop_back();
+ log_assert(GetSize(ast_stack) == 0);
+ for (auto &it : default_attr_list)
+ delete it.second;
+ default_attr_list.clear();
+};
+
+design:
+ module design |
+ defattr design |
+ task_func_decl design |
+ param_decl design |
+ localparam_decl design |
+ package design |
+ /* empty */;
+
+attr:
+ {
+ for (auto &it : attr_list)
+ delete it.second;
+ attr_list.clear();
+ for (auto &it : default_attr_list)
+ 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_opt:
+ attr_opt ATTR_BEGIN opt_attr_list ATTR_END |
+ /* empty */;
+
+defattr:
+ DEFATTR_BEGIN {
+ 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();
+ } DEFATTR_END;
+
+opt_attr_list:
+ attr_list | /* empty */;
+
+attr_list:
+ attr_assign |
+ attr_list ',' attr_assign;
+
+attr_assign:
+ hierarchical_id {
+ 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;
+ delete $1;
+ };
+
+hierarchical_id:
+ TOK_ID {
+ $$ = $1;
+ } |
+ hierarchical_id TOK_PACKAGESEP TOK_ID {
+ if ($3->substr(0, 1) == "\\")
+ *$1 += "::" + $3->substr(1);
+ else
+ *$1 += "::" + *$3;
+ delete $3;
+ $$ = $1;
+ } |
+ hierarchical_id '.' TOK_ID {
+ if ($3->substr(0, 1) == "\\")
+ *$1 += "." + $3->substr(1);
+ else
+ *$1 += "." + *$3;
+ delete $3;
+ $$ = $1;
+ };
+
+module:
+ attr TOK_MODULE TOK_ID {
+ do_not_require_port_stubs = false;
+ AstNode *mod = new AstNode(AST_MODULE);
+ ast_stack.back()->children.push_back(mod);
+ ast_stack.push_back(mod);
+ current_ast_mod = mod;
+ port_stubs.clear();
+ port_counter = 0;
+ mod->str = *$3;
+ append_attr(mod, $1);
+ delete $3;
+ } module_para_opt module_args_opt ';' module_body TOK_ENDMODULE {
+ 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;
+ };
+
+module_para_opt:
+ '#' '(' { astbuf1 = nullptr; } module_para_list { if (astbuf1) delete astbuf1; } ')' | /* empty */;
+
+module_para_list:
+ single_module_para | module_para_list ',' single_module_para;
+
+single_module_para:
+ /* empty */ |
+ TOK_PARAMETER {
+ if (astbuf1) delete astbuf1;
+ astbuf1 = new AstNode(AST_PARAMETER);
+ astbuf1->children.push_back(AstNode::mkconst_int(0, true));
+ } param_signed param_integer param_range single_param_decl | single_param_decl;
+
+module_args_opt:
+ '(' ')' | /* empty */ | '(' module_args optional_comma ')';
+
+module_args:
+ module_arg | module_args ',' module_arg;
+
+optional_comma:
+ ',' | /* empty */;
+
+module_arg_opt_assignment:
+ '=' expr {
+ if (ast_stack.back()->children.size() > 0 && ast_stack.back()->children.back()->type == AST_WIRE) {
+ AstNode *wire = new AstNode(AST_IDENTIFIER);
+ wire->str = ast_stack.back()->children.back()->str;
+ if (ast_stack.back()->children.back()->is_reg)
+ ast_stack.back()->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, wire, $2))));
+ else
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $2));
+ } else
+ frontend_verilog_yyerror("Syntax error.");
+ } |
+ /* empty */;
+
+module_arg:
+ TOK_ID {
+ if (ast_stack.back()->children.size() > 0 && ast_stack.back()->children.back()->type == AST_WIRE) {
+ AstNode *node = ast_stack.back()->children.back()->clone();
+ node->str = *$1;
+ node->port_id = ++port_counter;
+ ast_stack.back()->children.push_back(node);
+ } else {
+ if (port_stubs.count(*$1) != 0)
+ frontend_verilog_yyerror("Duplicate module port `%s'.", $1->c_str());
+ port_stubs[*$1] = ++port_counter;
+ }
+ delete $1;
+ } module_arg_opt_assignment |
+ attr wire_type range TOK_ID {
+ AstNode *node = $2;
+ node->str = *$4;
+ node->port_id = ++port_counter;
+ if ($3 != NULL)
+ node->children.push_back($3);
+ if (!node->is_input && !node->is_output)
+ frontend_verilog_yyerror("Module port `%s' is neither input nor output.", $4->c_str());
+ if (node->is_reg && node->is_input && !node->is_output && !sv_mode)
+ frontend_verilog_yyerror("Input port `%s' is declared as register.", $4->c_str());
+ ast_stack.back()->children.push_back(node);
+ append_attr(node, $1);
+ delete $4;
+ } module_arg_opt_assignment |
+ '.' '.' '.' {
+ do_not_require_port_stubs = true;
+ };
+
+package:
+ attr TOK_PACKAGE TOK_ID {
+ AstNode *mod = new AstNode(AST_PACKAGE);
+ ast_stack.back()->children.push_back(mod);
+ ast_stack.push_back(mod);
+ current_ast_mod = mod;
+ mod->str = *$3;
+ append_attr(mod, $1);
+ } ';' package_body TOK_ENDPACKAGE {
+ ast_stack.pop_back();
+ current_ast_mod = NULL;
+ };
+
+package_body:
+ package_body package_body_stmt |;
+
+package_body_stmt:
+ localparam_decl;
+
+non_opt_delay:
+ '#' '(' expr ')' { delete $3; } |
+ '#' '(' expr ':' expr ':' expr ')' { delete $3; delete $5; delete $7; };
+
+delay:
+ non_opt_delay | /* empty */;
+
+wire_type:
+ {
+ astbuf3 = new AstNode(AST_WIRE);
+ } wire_type_token_list delay {
+ $$ = astbuf3;
+ };
+
+wire_type_token_list:
+ wire_type_token | wire_type_token_list wire_type_token;
+
+wire_type_token:
+ TOK_INPUT {
+ astbuf3->is_input = true;
+ } |
+ TOK_OUTPUT {
+ astbuf3->is_output = true;
+ } |
+ TOK_INOUT {
+ astbuf3->is_input = true;
+ astbuf3->is_output = true;
+ } |
+ TOK_WIRE {
+ } |
+ TOK_REG {
+ astbuf3->is_reg = true;
+ } |
+ TOK_INTEGER {
+ astbuf3->is_reg = true;
+ astbuf3->range_left = 31;
+ astbuf3->range_right = 0;
+ astbuf3->is_signed = true;
+ } |
+ TOK_GENVAR {
+ astbuf3->type = AST_GENVAR;
+ astbuf3->is_reg = true;
+ astbuf3->range_left = 31;
+ astbuf3->range_right = 0;
+ } |
+ TOK_SIGNED {
+ astbuf3->is_signed = true;
+ };
+
+non_opt_range:
+ '[' expr ':' expr ']' {
+ $$ = new AstNode(AST_RANGE);
+ $$->children.push_back($2);
+ $$->children.push_back($4);
+ } |
+ '[' expr TOK_POS_INDEXED expr ']' {
+ $$ = new AstNode(AST_RANGE);
+ $$->children.push_back(new AstNode(AST_SUB, new AstNode(AST_ADD, $2->clone(), $4), AstNode::mkconst_int(1, true)));
+ $$->children.push_back(new AstNode(AST_ADD, $2, AstNode::mkconst_int(0, true)));
+ } |
+ '[' expr TOK_NEG_INDEXED expr ']' {
+ $$ = new AstNode(AST_RANGE);
+ $$->children.push_back(new AstNode(AST_ADD, $2, AstNode::mkconst_int(0, true)));
+ $$->children.push_back(new AstNode(AST_SUB, new AstNode(AST_ADD, $2->clone(), AstNode::mkconst_int(1, true)), $4));
+ } |
+ '[' expr ']' {
+ $$ = new AstNode(AST_RANGE);
+ $$->children.push_back($2);
+ };
+
+non_opt_multirange:
+ non_opt_range non_opt_range {
+ $$ = new AstNode(AST_MULTIRANGE, $1, $2);
+ } |
+ non_opt_multirange non_opt_range {
+ $$ = $1;
+ $$->children.push_back($2);
+ };
+
+range:
+ non_opt_range {
+ $$ = $1;
+ } |
+ /* empty */ {
+ $$ = NULL;
+ };
+
+range_or_multirange:
+ range { $$ = $1; } |
+ non_opt_multirange { $$ = $1; };
+
+range_or_signed_int:
+ range {
+ $$ = $1;
+ } |
+ TOK_INTEGER {
+ $$ = new AstNode(AST_RANGE);
+ $$->children.push_back(AstNode::mkconst_int(31, true));
+ $$->children.push_back(AstNode::mkconst_int(0, true));
+ $$->is_signed = true;
+ };
+
+module_body:
+ module_body module_body_stmt |
+ /* the following line makes the generate..endgenrate keywords optional */
+ module_body gen_stmt |
+ /* empty */;
+
+module_body_stmt:
+ task_func_decl | param_decl | localparam_decl | defparam_decl | wire_decl | assign_stmt | cell_stmt |
+ always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property;
+
+task_func_decl:
+ attr TOK_DPI_FUNCTION TOK_ID TOK_ID {
+ current_function_or_task = new AstNode(AST_DPI_FUNCTION, AstNode::mkconst_str(*$3), AstNode::mkconst_str(*$4));
+ current_function_or_task->str = *$4;
+ append_attr(current_function_or_task, $1);
+ ast_stack.back()->children.push_back(current_function_or_task);
+ delete $3;
+ delete $4;
+ } opt_dpi_function_args ';' {
+ current_function_or_task = NULL;
+ } |
+ attr TOK_DPI_FUNCTION TOK_ID '=' TOK_ID TOK_ID {
+ current_function_or_task = new AstNode(AST_DPI_FUNCTION, AstNode::mkconst_str(*$5), AstNode::mkconst_str(*$3));
+ current_function_or_task->str = *$6;
+ append_attr(current_function_or_task, $1);
+ ast_stack.back()->children.push_back(current_function_or_task);
+ delete $3;
+ delete $5;
+ delete $6;
+ } opt_dpi_function_args ';' {
+ current_function_or_task = NULL;
+ } |
+ attr TOK_DPI_FUNCTION TOK_ID ':' TOK_ID '=' TOK_ID TOK_ID {
+ current_function_or_task = new AstNode(AST_DPI_FUNCTION, AstNode::mkconst_str(*$7), AstNode::mkconst_str(*$3 + ":" + RTLIL::unescape_id(*$5)));
+ current_function_or_task->str = *$8;
+ append_attr(current_function_or_task, $1);
+ ast_stack.back()->children.push_back(current_function_or_task);
+ delete $3;
+ delete $5;
+ delete $7;
+ delete $8;
+ } opt_dpi_function_args ';' {
+ current_function_or_task = NULL;
+ } |
+ attr TOK_TASK TOK_ID {
+ current_function_or_task = new AstNode(AST_TASK);
+ current_function_or_task->str = *$3;
+ append_attr(current_function_or_task, $1);
+ ast_stack.back()->children.push_back(current_function_or_task);
+ ast_stack.push_back(current_function_or_task);
+ current_function_or_task_port_id = 1;
+ delete $3;
+ } task_func_args_opt ';' task_func_body TOK_ENDTASK {
+ current_function_or_task = NULL;
+ ast_stack.pop_back();
+ } |
+ attr TOK_FUNCTION opt_signed range_or_signed_int TOK_ID {
+ current_function_or_task = new AstNode(AST_FUNCTION);
+ current_function_or_task->str = *$5;
+ append_attr(current_function_or_task, $1);
+ ast_stack.back()->children.push_back(current_function_or_task);
+ ast_stack.push_back(current_function_or_task);
+ AstNode *outreg = new AstNode(AST_WIRE);
+ outreg->str = *$5;
+ outreg->is_signed = $3;
+ if ($4 != NULL) {
+ outreg->children.push_back($4);
+ outreg->is_signed = $3 || $4->is_signed;
+ $4->is_signed = false;
+ }
+ current_function_or_task->children.push_back(outreg);
+ current_function_or_task_port_id = 1;
+ delete $5;
+ } task_func_args_opt ';' task_func_body TOK_ENDFUNCTION {
+ current_function_or_task = NULL;
+ ast_stack.pop_back();
+ };
+
+dpi_function_arg:
+ TOK_ID TOK_ID {
+ current_function_or_task->children.push_back(AstNode::mkconst_str(*$1));
+ delete $1;
+ delete $2;
+ } |
+ TOK_ID {
+ current_function_or_task->children.push_back(AstNode::mkconst_str(*$1));
+ delete $1;
+ };
+
+opt_dpi_function_args:
+ '(' dpi_function_args ')' |
+ /* empty */;
+
+dpi_function_args:
+ dpi_function_args ',' dpi_function_arg |
+ dpi_function_args ',' |
+ dpi_function_arg |
+ /* empty */;
+
+opt_signed:
+ TOK_SIGNED {
+ $$ = true;
+ } |
+ /* empty */ {
+ $$ = false;
+ };
+
+task_func_args_opt:
+ '(' ')' | /* empty */ | '(' {
+ albuf = nullptr;
+ astbuf1 = nullptr;
+ astbuf2 = nullptr;
+ } task_func_args optional_comma {
+ delete astbuf1;
+ if (astbuf2 != NULL)
+ delete astbuf2;
+ free_attr(albuf);
+ } ')';
+
+task_func_args:
+ task_func_port | task_func_args ',' task_func_port;
+
+task_func_port:
+ attr wire_type range {
+ if (albuf) {
+ delete astbuf1;
+ if (astbuf2 != NULL)
+ delete astbuf2;
+ free_attr(albuf);
+ }
+ albuf = $1;
+ astbuf1 = $2;
+ astbuf2 = $3;
+ if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) {
+ if (astbuf2) {
+ frontend_verilog_yyerror("Syntax error.");
+ } else {
+ astbuf2 = new AstNode(AST_RANGE);
+ astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true));
+ astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_right, true));
+ }
+ }
+ if (astbuf2 && astbuf2->children.size() != 2)
+ frontend_verilog_yyerror("Syntax error.");
+ } wire_name | wire_name;
+
+task_func_body:
+ task_func_body behavioral_stmt |
+ /* empty */;
+
+param_signed:
+ TOK_SIGNED {
+ astbuf1->is_signed = true;
+ } | /* empty */;
+
+param_integer:
+ TOK_INTEGER {
+ if (astbuf1->children.size() != 1)
+ frontend_verilog_yyerror("Syntax error.");
+ 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));
+ astbuf1->is_signed = true;
+ } | /* empty */;
+
+param_real:
+ TOK_REAL {
+ if (astbuf1->children.size() != 1)
+ frontend_verilog_yyerror("Syntax error.");
+ astbuf1->children.push_back(new AstNode(AST_REALVALUE));
+ } | /* empty */;
+
+param_range:
+ range {
+ if ($1 != NULL) {
+ if (astbuf1->children.size() != 1)
+ frontend_verilog_yyerror("Syntax error.");
+ astbuf1->children.push_back($1);
+ }
+ };
+
+param_decl:
+ TOK_PARAMETER {
+ astbuf1 = new AstNode(AST_PARAMETER);
+ astbuf1->children.push_back(AstNode::mkconst_int(0, true));
+ } param_signed param_integer param_real param_range param_decl_list ';' {
+ delete astbuf1;
+ };
+
+localparam_decl:
+ TOK_LOCALPARAM {
+ astbuf1 = new AstNode(AST_LOCALPARAM);
+ astbuf1->children.push_back(AstNode::mkconst_int(0, true));
+ } param_signed param_integer param_real param_range param_decl_list ';' {
+ delete astbuf1;
+ };
+
+param_decl_list:
+ single_param_decl | param_decl_list ',' single_param_decl;
+
+single_param_decl:
+ TOK_ID '=' expr {
+ if (astbuf1 == nullptr)
+ frontend_verilog_yyerror("syntax error");
+ AstNode *node = astbuf1->clone();
+ node->str = *$1;
+ delete node->children[0];
+ node->children[0] = $3;
+ ast_stack.back()->children.push_back(node);
+ delete $1;
+ };
+
+defparam_decl:
+ TOK_DEFPARAM defparam_decl_list ';';
+
+defparam_decl_list:
+ single_defparam_decl | defparam_decl_list ',' single_defparam_decl;
+
+single_defparam_decl:
+ range hierarchical_id '=' expr {
+ AstNode *node = new AstNode(AST_DEFPARAM);
+ node->str = *$2;
+ node->children.push_back($4);
+ if ($1 != NULL)
+ node->children.push_back($1);
+ ast_stack.back()->children.push_back(node);
+ delete $2;
+ };
+
+wire_decl:
+ attr wire_type range {
+ albuf = $1;
+ astbuf1 = $2;
+ astbuf2 = $3;
+ if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) {
+ if (astbuf2) {
+ frontend_verilog_yyerror("Syntax error.");
+ } else {
+ astbuf2 = new AstNode(AST_RANGE);
+ astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true));
+ astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_right, true));
+ }
+ }
+ if (astbuf2 && astbuf2->children.size() != 2)
+ frontend_verilog_yyerror("Syntax error.");
+ } wire_name_list {
+ delete astbuf1;
+ if (astbuf2 != NULL)
+ delete astbuf2;
+ free_attr(albuf);
+ } ';' |
+ attr TOK_SUPPLY0 TOK_ID {
+ ast_stack.back()->children.push_back(new AstNode(AST_WIRE));
+ ast_stack.back()->children.back()->str = *$3;
+ append_attr(ast_stack.back()->children.back(), $1);
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(0, false, 1)));
+ ast_stack.back()->children.back()->children[0]->str = *$3;
+ delete $3;
+ } opt_supply_wires ';' |
+ attr TOK_SUPPLY1 TOK_ID {
+ ast_stack.back()->children.push_back(new AstNode(AST_WIRE));
+ ast_stack.back()->children.back()->str = *$3;
+ append_attr(ast_stack.back()->children.back(), $1);
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(1, false, 1)));
+ ast_stack.back()->children.back()->children[0]->str = *$3;
+ delete $3;
+ } opt_supply_wires ';';
+
+opt_supply_wires:
+ /* empty */ |
+ opt_supply_wires ',' TOK_ID {
+ AstNode *wire_node = ast_stack.back()->children.at(GetSize(ast_stack.back()->children)-2)->clone();
+ AstNode *assign_node = ast_stack.back()->children.at(GetSize(ast_stack.back()->children)-1)->clone();
+ wire_node->str = *$3;
+ assign_node->children[0]->str = *$3;
+ ast_stack.back()->children.push_back(wire_node);
+ ast_stack.back()->children.push_back(assign_node);
+ delete $3;
+ };
+
+wire_name_list:
+ wire_name_and_opt_assign | wire_name_list ',' wire_name_and_opt_assign;
+
+wire_name_and_opt_assign:
+ wire_name |
+ wire_name '=' expr {
+ AstNode *wire = new AstNode(AST_IDENTIFIER);
+ wire->str = ast_stack.back()->children.back()->str;
+ if (astbuf1->is_reg)
+ ast_stack.back()->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, wire, $3))));
+ else
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $3));
+ };
+
+wire_name:
+ TOK_ID range_or_multirange {
+ if (astbuf1 == nullptr)
+ frontend_verilog_yyerror("Syntax error.");
+ AstNode *node = astbuf1->clone();
+ node->str = *$1;
+ append_attr_clone(node, albuf);
+ if (astbuf2 != NULL)
+ node->children.push_back(astbuf2->clone());
+ if ($2 != NULL) {
+ if (node->is_input || node->is_output)
+ frontend_verilog_yyerror("Syntax error.");
+ if (!astbuf2) {
+ AstNode *rng = new AstNode(AST_RANGE);
+ rng->children.push_back(AstNode::mkconst_int(0, true));
+ rng->children.push_back(AstNode::mkconst_int(0, true));
+ node->children.push_back(rng);
+ }
+ node->type = AST_MEMORY;
+ node->children.push_back($2);
+ }
+ if (current_function_or_task == NULL) {
+ if (do_not_require_port_stubs && (node->is_input || node->is_output) && port_stubs.count(*$1) == 0) {
+ port_stubs[*$1] = ++port_counter;
+ }
+ if (port_stubs.count(*$1) != 0) {
+ if (!node->is_input && !node->is_output)
+ frontend_verilog_yyerror("Module port `%s' is neither input nor output.", $1->c_str());
+ if (node->is_reg && node->is_input && !node->is_output && !sv_mode)
+ frontend_verilog_yyerror("Input port `%s' is declared as register.", $1->c_str());
+ node->port_id = port_stubs[*$1];
+ port_stubs.erase(*$1);
+ } else {
+ if (node->is_input || node->is_output)
+ frontend_verilog_yyerror("Module port `%s' is not declared in module header.", $1->c_str());
+ }
+ } else {
+ if (node->is_input || node->is_output)
+ node->port_id = current_function_or_task_port_id++;
+ }
+ ast_stack.back()->children.push_back(node);
+ delete $1;
+ };
+
+assign_stmt:
+ TOK_ASSIGN delay assign_expr_list ';';
+
+assign_expr_list:
+ assign_expr | assign_expr_list ',' assign_expr;
+
+assign_expr:
+ lvalue '=' expr {
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, $1, $3));
+ };
+
+cell_stmt:
+ attr TOK_ID {
+ astbuf1 = new AstNode(AST_CELL);
+ append_attr(astbuf1, $1);
+ astbuf1->children.push_back(new AstNode(AST_CELLTYPE));
+ astbuf1->children[0]->str = *$2;
+ delete $2;
+ } cell_parameter_list_opt cell_list ';' {
+ delete astbuf1;
+ } |
+ attr tok_prim_wrapper delay {
+ astbuf1 = new AstNode(AST_PRIMITIVE);
+ astbuf1->str = *$2;
+ append_attr(astbuf1, $1);
+ delete $2;
+ } prim_list ';' {
+ delete astbuf1;
+ };
+
+tok_prim_wrapper:
+ TOK_PRIMITIVE {
+ $$ = $1;
+ } |
+ TOK_OR {
+ $$ = new std::string("or");
+ };
+
+cell_list:
+ single_cell |
+ cell_list ',' single_cell;
+
+single_cell:
+ TOK_ID {
+ astbuf2 = astbuf1->clone();
+ if (astbuf2->type != AST_PRIMITIVE)
+ astbuf2->str = *$1;
+ delete $1;
+ ast_stack.back()->children.push_back(astbuf2);
+ } '(' cell_port_list ')' |
+ TOK_ID non_opt_range {
+ astbuf2 = astbuf1->clone();
+ if (astbuf2->type != AST_PRIMITIVE)
+ astbuf2->str = *$1;
+ delete $1;
+ ast_stack.back()->children.push_back(new AstNode(AST_CELLARRAY, $2, astbuf2));
+ } '(' cell_port_list ')';
+
+prim_list:
+ single_prim |
+ prim_list ',' single_prim;
+
+single_prim:
+ single_cell |
+ /* no name */ {
+ astbuf2 = astbuf1->clone();
+ ast_stack.back()->children.push_back(astbuf2);
+ } '(' cell_port_list ')';
+
+cell_parameter_list_opt:
+ '#' '(' cell_parameter_list ')' | /* empty */;
+
+cell_parameter_list:
+ cell_parameter | cell_parameter_list ',' cell_parameter;
+
+cell_parameter:
+ /* empty */ |
+ expr {
+ AstNode *node = new AstNode(AST_PARASET);
+ astbuf1->children.push_back(node);
+ node->children.push_back($1);
+ } |
+ '.' TOK_ID '(' expr ')' {
+ AstNode *node = new AstNode(AST_PARASET);
+ node->str = *$2;
+ astbuf1->children.push_back(node);
+ node->children.push_back($4);
+ delete $2;
+ };
+
+cell_port_list:
+ cell_port_list_rules {
+ // remove empty args from end of list
+ while (!astbuf2->children.empty()) {
+ AstNode *node = astbuf2->children.back();
+ if (node->type != AST_ARGUMENT) break;
+ if (!node->children.empty()) break;
+ if (!node->str.empty()) break;
+ astbuf2->children.pop_back();
+ delete node;
+ }
+
+ // check port types
+ bool has_positional_args = false;
+ bool has_named_args = false;
+ for (auto node : astbuf2->children) {
+ if (node->type != AST_ARGUMENT) continue;
+ if (node->str.empty())
+ has_positional_args = true;
+ else
+ has_named_args = true;
+ }
+
+ if (has_positional_args && has_named_args)
+ frontend_verilog_yyerror("Mix of positional and named cell ports.");
+ };
+
+cell_port_list_rules:
+ cell_port | cell_port_list_rules ',' cell_port;
+
+cell_port:
+ /* empty */ {
+ AstNode *node = new AstNode(AST_ARGUMENT);
+ astbuf2->children.push_back(node);
+ } |
+ expr {
+ AstNode *node = new AstNode(AST_ARGUMENT);
+ astbuf2->children.push_back(node);
+ node->children.push_back($1);
+ } |
+ '.' TOK_ID '(' expr ')' {
+ AstNode *node = new AstNode(AST_ARGUMENT);
+ node->str = *$2;
+ astbuf2->children.push_back(node);
+ node->children.push_back($4);
+ delete $2;
+ } |
+ '.' TOK_ID '(' ')' {
+ AstNode *node = new AstNode(AST_ARGUMENT);
+ node->str = *$2;
+ astbuf2->children.push_back(node);
+ delete $2;
+ };
+
+always_stmt:
+ attr TOK_ALWAYS {
+ AstNode *node = new AstNode(AST_ALWAYS);
+ append_attr(node, $1);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ } always_cond {
+ AstNode *block = new AstNode(AST_BLOCK);
+ ast_stack.back()->children.push_back(block);
+ ast_stack.push_back(block);
+ } behavioral_stmt {
+ ast_stack.pop_back();
+ ast_stack.pop_back();
+ } |
+ attr TOK_INITIAL {
+ AstNode *node = new AstNode(AST_INITIAL);
+ append_attr(node, $1);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ AstNode *block = new AstNode(AST_BLOCK);
+ ast_stack.back()->children.push_back(block);
+ ast_stack.push_back(block);
+ } behavioral_stmt {
+ ast_stack.pop_back();
+ ast_stack.pop_back();
+ };
+
+always_cond:
+ '@' '(' always_events ')' |
+ '@' '(' '*' ')' |
+ '@' ATTR_BEGIN ')' |
+ '@' '(' ATTR_END |
+ '@' '*' |
+ /* empty */;
+
+always_events:
+ always_event |
+ always_events TOK_OR always_event |
+ always_events ',' always_event;
+
+always_event:
+ TOK_POSEDGE expr {
+ AstNode *node = new AstNode(AST_POSEDGE);
+ ast_stack.back()->children.push_back(node);
+ node->children.push_back($2);
+ } |
+ TOK_NEGEDGE expr {
+ AstNode *node = new AstNode(AST_NEGEDGE);
+ ast_stack.back()->children.push_back(node);
+ node->children.push_back($2);
+ } |
+ expr {
+ AstNode *node = new AstNode(AST_EDGE);
+ ast_stack.back()->children.push_back(node);
+ node->children.push_back($1);
+ };
+
+opt_label:
+ ':' TOK_ID {
+ $$ = $2;
+ } |
+ /* empty */ {
+ $$ = NULL;
+ };
+
+assert:
+ TOK_ASSERT '(' expr ')' ';' {
+ ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $3));
+ } |
+ TOK_ASSUME '(' expr ')' ';' {
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $3));
+ } |
+ TOK_RESTRICT '(' expr ')' ';' {
+ if (norestrict_mode)
+ delete $3;
+ else
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $3));
+ };
+
+assert_property:
+ TOK_ASSERT TOK_PROPERTY '(' expr ')' ';' {
+ ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $4));
+ } |
+ TOK_ASSUME TOK_PROPERTY '(' expr ')' ';' {
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $4));
+ } |
+ TOK_RESTRICT TOK_PROPERTY '(' expr ')' ';' {
+ if (norestrict_mode)
+ delete $4;
+ else
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $4));
+ };
+
+simple_behavioral_stmt:
+ lvalue '=' delay expr {
+ AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, $4);
+ ast_stack.back()->children.push_back(node);
+ } |
+ lvalue OP_LE delay expr {
+ AstNode *node = new AstNode(AST_ASSIGN_LE, $1, $4);
+ ast_stack.back()->children.push_back(node);
+ };
+
+// this production creates the obligatory if-else shift/reduce conflict
+behavioral_stmt:
+ defattr | assert | wire_decl | param_decl | localparam_decl |
+ non_opt_delay behavioral_stmt |
+ simple_behavioral_stmt ';' | ';' |
+ hierarchical_id attr {
+ AstNode *node = new AstNode(AST_TCALL);
+ node->str = *$1;
+ delete $1;
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ append_attr(node, $2);
+ } opt_arg_list ';'{
+ ast_stack.pop_back();
+ } |
+ attr TOK_BEGIN opt_label {
+ AstNode *node = new AstNode(AST_BLOCK);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ append_attr(node, $1);
+ if ($3 != NULL)
+ node->str = *$3;
+ } behavioral_stmt_list TOK_END opt_label {
+ if ($3 != NULL && $7 != NULL && *$3 != *$7)
+ frontend_verilog_yyerror("Syntax error.");
+ if ($3 != NULL)
+ delete $3;
+ if ($7 != NULL)
+ delete $7;
+ ast_stack.pop_back();
+ } |
+ attr TOK_FOR '(' {
+ AstNode *node = new AstNode(AST_FOR);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ append_attr(node, $1);
+ } simple_behavioral_stmt ';' expr {
+ ast_stack.back()->children.push_back($7);
+ } ';' simple_behavioral_stmt ')' {
+ AstNode *block = new AstNode(AST_BLOCK);
+ ast_stack.back()->children.push_back(block);
+ ast_stack.push_back(block);
+ } behavioral_stmt {
+ ast_stack.pop_back();
+ ast_stack.pop_back();
+ } |
+ attr TOK_WHILE '(' expr ')' {
+ AstNode *node = new AstNode(AST_WHILE);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ append_attr(node, $1);
+ AstNode *block = new AstNode(AST_BLOCK);
+ ast_stack.back()->children.push_back($4);
+ ast_stack.back()->children.push_back(block);
+ ast_stack.push_back(block);
+ } behavioral_stmt {
+ ast_stack.pop_back();
+ ast_stack.pop_back();
+ } |
+ attr TOK_REPEAT '(' expr ')' {
+ AstNode *node = new AstNode(AST_REPEAT);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ append_attr(node, $1);
+ AstNode *block = new AstNode(AST_BLOCK);
+ ast_stack.back()->children.push_back($4);
+ ast_stack.back()->children.push_back(block);
+ ast_stack.push_back(block);
+ } behavioral_stmt {
+ ast_stack.pop_back();
+ ast_stack.pop_back();
+ } |
+ attr TOK_IF '(' expr ')' {
+ AstNode *node = new AstNode(AST_CASE);
+ AstNode *block = new AstNode(AST_BLOCK);
+ AstNode *cond = new AstNode(AST_COND, AstNode::mkconst_int(1, false, 1), block);
+ ast_stack.back()->children.push_back(node);
+ node->children.push_back(new AstNode(AST_REDUCE_BOOL, $4));
+ node->children.push_back(cond);
+ ast_stack.push_back(node);
+ ast_stack.push_back(block);
+ append_attr(node, $1);
+ } behavioral_stmt optional_else {
+ ast_stack.pop_back();
+ ast_stack.pop_back();
+ } |
+ attr case_type '(' expr ')' {
+ AstNode *node = new AstNode(AST_CASE, $4);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ append_attr(node, $1);
+ } opt_synopsys_attr case_body TOK_ENDCASE {
+ case_type_stack.pop_back();
+ ast_stack.pop_back();
+ };
+
+case_type:
+ TOK_CASE {
+ case_type_stack.push_back(0);
+ } |
+ TOK_CASEX {
+ case_type_stack.push_back('x');
+ } |
+ TOK_CASEZ {
+ case_type_stack.push_back('z');
+ };
+
+opt_synopsys_attr:
+ opt_synopsys_attr TOK_SYNOPSYS_FULL_CASE {
+ if (ast_stack.back()->attributes.count("\\full_case") == 0)
+ ast_stack.back()->attributes["\\full_case"] = AstNode::mkconst_int(1, false);
+ } |
+ opt_synopsys_attr TOK_SYNOPSYS_PARALLEL_CASE {
+ if (ast_stack.back()->attributes.count("\\parallel_case") == 0)
+ ast_stack.back()->attributes["\\parallel_case"] = AstNode::mkconst_int(1, false);
+ } |
+ /* empty */;
+
+behavioral_stmt_list:
+ behavioral_stmt_list behavioral_stmt |
+ /* empty */;
+
+optional_else:
+ TOK_ELSE {
+ AstNode *block = new AstNode(AST_BLOCK);
+ AstNode *cond = new AstNode(AST_COND, new AstNode(AST_DEFAULT), block);
+ ast_stack.pop_back();
+ ast_stack.back()->children.push_back(cond);
+ ast_stack.push_back(block);
+ } behavioral_stmt |
+ /* empty */;
+
+case_body:
+ case_body case_item |
+ /* empty */;
+
+case_item:
+ {
+ AstNode *node = new AstNode(
+ case_type_stack.size() && case_type_stack.back() == 'x' ? AST_CONDX :
+ case_type_stack.size() && case_type_stack.back() == 'z' ? AST_CONDZ : AST_COND);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ } case_select {
+ AstNode *block = new AstNode(AST_BLOCK);
+ ast_stack.back()->children.push_back(block);
+ ast_stack.push_back(block);
+ case_type_stack.push_back(0);
+ } behavioral_stmt {
+ case_type_stack.pop_back();
+ ast_stack.pop_back();
+ ast_stack.pop_back();
+ };
+
+gen_case_body:
+ gen_case_body gen_case_item |
+ /* empty */;
+
+gen_case_item:
+ {
+ AstNode *node = new AstNode(
+ case_type_stack.size() && case_type_stack.back() == 'x' ? AST_CONDX :
+ case_type_stack.size() && case_type_stack.back() == 'z' ? AST_CONDZ : AST_COND);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ } case_select {
+ case_type_stack.push_back(0);
+ } gen_stmt_or_null {
+ case_type_stack.pop_back();
+ ast_stack.pop_back();
+ };
+
+case_select:
+ case_expr_list ':' |
+ TOK_DEFAULT;
+
+case_expr_list:
+ TOK_DEFAULT {
+ ast_stack.back()->children.push_back(new AstNode(AST_DEFAULT));
+ } |
+ expr {
+ ast_stack.back()->children.push_back($1);
+ } |
+ case_expr_list ',' expr {
+ ast_stack.back()->children.push_back($3);
+ };
+
+rvalue:
+ hierarchical_id '[' expr ']' '.' rvalue {
+ $$ = new AstNode(AST_PREFIX, $3, $6);
+ $$->str = *$1;
+ delete $1;
+ } |
+ hierarchical_id range {
+ $$ = new AstNode(AST_IDENTIFIER, $2);
+ $$->str = *$1;
+ delete $1;
+ if ($2 == nullptr && formal_mode && ($$->str == "\\$initstate" || $$->str == "\\$anyconst" || $$->str == "\\$anyseq"))
+ $$->type = AST_FCALL;
+ } |
+ hierarchical_id non_opt_multirange {
+ $$ = new AstNode(AST_IDENTIFIER, $2);
+ $$->str = *$1;
+ delete $1;
+ };
+
+lvalue:
+ rvalue {
+ $$ = $1;
+ } |
+ '{' lvalue_concat_list '}' {
+ $$ = $2;
+ };
+
+lvalue_concat_list:
+ expr {
+ $$ = new AstNode(AST_CONCAT);
+ $$->children.push_back($1);
+ } |
+ expr ',' lvalue_concat_list {
+ $$ = $3;
+ $$->children.push_back($1);
+ };
+
+opt_arg_list:
+ '(' arg_list optional_comma ')' |
+ /* empty */;
+
+arg_list:
+ arg_list2 |
+ /* empty */;
+
+arg_list2:
+ single_arg |
+ arg_list ',' single_arg;
+
+single_arg:
+ expr {
+ ast_stack.back()->children.push_back($1);
+ };
+
+module_gen_body:
+ module_gen_body gen_stmt_or_module_body_stmt |
+ /* empty */;
+
+gen_stmt_or_module_body_stmt:
+ gen_stmt | module_body_stmt;
+
+// this production creates the obligatory if-else shift/reduce conflict
+gen_stmt:
+ TOK_FOR '(' {
+ AstNode *node = new AstNode(AST_GENFOR);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ } simple_behavioral_stmt ';' expr {
+ ast_stack.back()->children.push_back($6);
+ } ';' simple_behavioral_stmt ')' gen_stmt_block {
+ ast_stack.pop_back();
+ } |
+ TOK_IF '(' expr ')' {
+ AstNode *node = new AstNode(AST_GENIF);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ ast_stack.back()->children.push_back($3);
+ } gen_stmt_block opt_gen_else {
+ ast_stack.pop_back();
+ } |
+ case_type '(' expr ')' {
+ AstNode *node = new AstNode(AST_GENCASE, $3);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ } gen_case_body TOK_ENDCASE {
+ case_type_stack.pop_back();
+ ast_stack.pop_back();
+ } |
+ TOK_BEGIN opt_label {
+ AstNode *node = new AstNode(AST_GENBLOCK);
+ node->str = $2 ? *$2 : std::string();
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ } module_gen_body TOK_END opt_label {
+ if ($2 != NULL)
+ delete $2;
+ if ($6 != NULL)
+ delete $6;
+ ast_stack.pop_back();
+ };
+
+gen_stmt_block:
+ {
+ AstNode *node = new AstNode(AST_GENBLOCK);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ } gen_stmt_or_module_body_stmt {
+ ast_stack.pop_back();
+ };
+
+gen_stmt_or_null:
+ gen_stmt_block | ';';
+
+opt_gen_else:
+ TOK_ELSE gen_stmt_or_null | /* empty */;
+
+expr:
+ basic_expr {
+ $$ = $1;
+ } |
+ basic_expr '?' attr expr ':' expr {
+ $$ = new AstNode(AST_TERNARY);
+ $$->children.push_back($1);
+ $$->children.push_back($4);
+ $$->children.push_back($6);
+ append_attr($$, $3);
+ };
+
+basic_expr:
+ rvalue {
+ $$ = $1;
+ } |
+ '(' expr ')' TOK_CONST {
+ if ($4->substr(0, 1) != "'")
+ frontend_verilog_yyerror("Syntax error.");
+ AstNode *bits = $2;
+ AstNode *val = const2ast(*$4, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode);
+ if (val == NULL)
+ log_error("Value conversion failed: `%s'\n", $4->c_str());
+ $$ = new AstNode(AST_TO_BITS, bits, val);
+ delete $4;
+ } |
+ hierarchical_id TOK_CONST {
+ if ($2->substr(0, 1) != "'")
+ frontend_verilog_yyerror("Syntax error.");
+ AstNode *bits = new AstNode(AST_IDENTIFIER);
+ bits->str = *$1;
+ AstNode *val = const2ast(*$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode);
+ if (val == NULL)
+ log_error("Value conversion failed: `%s'\n", $2->c_str());
+ $$ = new AstNode(AST_TO_BITS, bits, val);
+ delete $1;
+ delete $2;
+ } |
+ TOK_CONST TOK_CONST {
+ $$ = const2ast(*$1 + *$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode);
+ if ($$ == NULL || (*$2)[0] != '\'')
+ log_error("Value conversion failed: `%s%s'\n", $1->c_str(), $2->c_str());
+ delete $1;
+ delete $2;
+ } |
+ TOK_CONST {
+ $$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode);
+ if ($$ == NULL)
+ log_error("Value conversion failed: `%s'\n", $1->c_str());
+ delete $1;
+ } |
+ TOK_REALVAL {
+ $$ = new AstNode(AST_REALVALUE);
+ char *p = (char*)malloc(GetSize(*$1) + 1), *q;
+ for (int i = 0, j = 0; j < GetSize(*$1); j++)
+ if ((*$1)[j] != '_')
+ p[i++] = (*$1)[j], p[i] = 0;
+ $$->realvalue = strtod(p, &q);
+ log_assert(*q == 0);
+ delete $1;
+ free(p);
+ } |
+ TOK_STRING {
+ $$ = AstNode::mkconst_str(*$1);
+ delete $1;
+ } |
+ hierarchical_id attr {
+ AstNode *node = new AstNode(AST_FCALL);
+ node->str = *$1;
+ delete $1;
+ ast_stack.push_back(node);
+ append_attr(node, $2);
+ } '(' arg_list optional_comma ')' {
+ $$ = ast_stack.back();
+ ast_stack.pop_back();
+ } |
+ TOK_TO_SIGNED attr '(' expr ')' {
+ $$ = new AstNode(AST_TO_SIGNED, $4);
+ append_attr($$, $2);
+ } |
+ TOK_TO_UNSIGNED attr '(' expr ')' {
+ $$ = new AstNode(AST_TO_UNSIGNED, $4);
+ append_attr($$, $2);
+ } |
+ '(' expr ')' {
+ $$ = $2;
+ } |
+ '(' expr ':' expr ':' expr ')' {
+ delete $2;
+ $$ = $4;
+ delete $6;
+ } |
+ '{' concat_list '}' {
+ $$ = $2;
+ } |
+ '{' expr '{' concat_list '}' '}' {
+ $$ = new AstNode(AST_REPLICATE, $2, $4);
+ } |
+ '~' attr basic_expr %prec UNARY_OPS {
+ $$ = new AstNode(AST_BIT_NOT, $3);
+ append_attr($$, $2);
+ } |
+ basic_expr '&' attr basic_expr {
+ $$ = new AstNode(AST_BIT_AND, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr '|' attr basic_expr {
+ $$ = new AstNode(AST_BIT_OR, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr '^' attr basic_expr {
+ $$ = new AstNode(AST_BIT_XOR, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_XNOR attr basic_expr {
+ $$ = new AstNode(AST_BIT_XNOR, $1, $4);
+ append_attr($$, $3);
+ } |
+ '&' attr basic_expr %prec UNARY_OPS {
+ $$ = new AstNode(AST_REDUCE_AND, $3);
+ append_attr($$, $2);
+ } |
+ OP_NAND attr basic_expr %prec UNARY_OPS {
+ $$ = new AstNode(AST_REDUCE_AND, $3);
+ append_attr($$, $2);
+ $$ = new AstNode(AST_LOGIC_NOT, $$);
+ } |
+ '|' attr basic_expr %prec UNARY_OPS {
+ $$ = new AstNode(AST_REDUCE_OR, $3);
+ append_attr($$, $2);
+ } |
+ OP_NOR attr basic_expr %prec UNARY_OPS {
+ $$ = new AstNode(AST_REDUCE_OR, $3);
+ append_attr($$, $2);
+ $$ = new AstNode(AST_LOGIC_NOT, $$);
+ } |
+ '^' attr basic_expr %prec UNARY_OPS {
+ $$ = new AstNode(AST_REDUCE_XOR, $3);
+ append_attr($$, $2);
+ } |
+ OP_XNOR attr basic_expr %prec UNARY_OPS {
+ $$ = new AstNode(AST_REDUCE_XNOR, $3);
+ append_attr($$, $2);
+ } |
+ basic_expr OP_SHL attr basic_expr {
+ $$ = new AstNode(AST_SHIFT_LEFT, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_SHR attr basic_expr {
+ $$ = new AstNode(AST_SHIFT_RIGHT, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_SSHL attr basic_expr {
+ $$ = new AstNode(AST_SHIFT_SLEFT, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_SSHR attr basic_expr {
+ $$ = new AstNode(AST_SHIFT_SRIGHT, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr '<' attr basic_expr {
+ $$ = new AstNode(AST_LT, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_LE attr basic_expr {
+ $$ = new AstNode(AST_LE, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_EQ attr basic_expr {
+ $$ = new AstNode(AST_EQ, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_NE attr basic_expr {
+ $$ = new AstNode(AST_NE, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_EQX attr basic_expr {
+ $$ = new AstNode(AST_EQX, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_NEX attr basic_expr {
+ $$ = new AstNode(AST_NEX, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_GE attr basic_expr {
+ $$ = new AstNode(AST_GE, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr '>' attr basic_expr {
+ $$ = new AstNode(AST_GT, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr '+' attr basic_expr {
+ $$ = new AstNode(AST_ADD, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr '-' attr basic_expr {
+ $$ = new AstNode(AST_SUB, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr '*' attr basic_expr {
+ $$ = new AstNode(AST_MUL, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr '/' attr basic_expr {
+ $$ = new AstNode(AST_DIV, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr '%' attr basic_expr {
+ $$ = new AstNode(AST_MOD, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_POW attr basic_expr {
+ $$ = new AstNode(AST_POW, $1, $4);
+ append_attr($$, $3);
+ } |
+ '+' attr basic_expr %prec UNARY_OPS {
+ $$ = new AstNode(AST_POS, $3);
+ append_attr($$, $2);
+ } |
+ '-' attr basic_expr %prec UNARY_OPS {
+ $$ = new AstNode(AST_NEG, $3);
+ append_attr($$, $2);
+ } |
+ basic_expr OP_LAND attr basic_expr {
+ $$ = new AstNode(AST_LOGIC_AND, $1, $4);
+ append_attr($$, $3);
+ } |
+ basic_expr OP_LOR attr basic_expr {
+ $$ = new AstNode(AST_LOGIC_OR, $1, $4);
+ append_attr($$, $3);
+ } |
+ '!' attr basic_expr %prec UNARY_OPS {
+ $$ = new AstNode(AST_LOGIC_NOT, $3);
+ append_attr($$, $2);
+ };
+
+concat_list:
+ expr {
+ $$ = new AstNode(AST_CONCAT, $1);
+ } |
+ expr ',' concat_list {
+ $$ = $3;
+ $$->children.push_back($1);
+ };
+
diff --git a/frontends/vhdl2verilog/Makefile.inc b/frontends/vhdl2verilog/Makefile.inc
new file mode 100644
index 00000000..003d89c4
--- /dev/null
+++ b/frontends/vhdl2verilog/Makefile.inc
@@ -0,0 +1 @@
+OBJS += frontends/vhdl2verilog/vhdl2verilog.o
diff --git a/frontends/vhdl2verilog/vhdl2verilog.cc b/frontends/vhdl2verilog/vhdl2verilog.cc
new file mode 100644
index 00000000..6f9c0e3f
--- /dev/null
+++ b/frontends/vhdl2verilog/vhdl2verilog.cc
@@ -0,0 +1,183 @@
+/*
+ * 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/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#ifndef _WIN32
+# include <unistd.h>
+# include <dirent.h>
+#endif
+
+YOSYS_NAMESPACE_BEGIN
+
+struct Vhdl2verilogPass : public Pass {
+ Vhdl2verilogPass() : Pass("vhdl2verilog", "importing VHDL designs using vhdl2verilog") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" vhdl2verilog [options] <vhdl-file>..\n");
+ log("\n");
+ log("This command reads VHDL source files using the 'vhdl2verilog' tool and the\n");
+ log("Yosys Verilog frontend.\n");
+ log("\n");
+ log(" -out <out_file>\n");
+ log(" do not import the vhdl2verilog output. instead write it to the\n");
+ log(" specified file.\n");
+ log("\n");
+ log(" -vhdl2verilog_dir <directory>\n");
+ log(" do use the specified vhdl2verilog installation. this is the directory\n");
+ log(" that contains the setup_env.sh file. when this option is not present,\n");
+ log(" it is assumed that vhdl2verilog is in the PATH environment variable.\n");
+ log("\n");
+ log(" -top <top-entity-name>\n");
+ log(" The name of the top entity. This option is mandatory.\n");
+ log("\n");
+ log("The following options are passed as-is to vhdl2verilog:\n");
+ log("\n");
+ log(" -arch <architecture_name>\n");
+ log(" -unroll_generate\n");
+ log(" -nogenericeval\n");
+ log(" -nouniquify\n");
+ log(" -oldparser\n");
+ log(" -suppress <list>\n");
+ log(" -quiet\n");
+ log(" -nobanner\n");
+ log(" -mapfile <file>\n");
+ log("\n");
+ log("vhdl2verilog can be obtained from:\n");
+ log("http://www.edautils.com/vhdl2verilog.html\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing VHDL2VERILOG (importing VHDL designs using vhdl2verilog).\n");
+ log_push();
+
+ std::string out_file, top_entity;
+ std::string vhdl2verilog_dir;
+ std::string extra_opts;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-out" && argidx+1 < args.size()) {
+ out_file = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-top" && argidx+1 < args.size()) {
+ top_entity = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-vhdl2verilog_dir" && argidx+1 < args.size()) {
+ vhdl2verilog_dir = args[++argidx];
+ continue;
+ }
+ if ((args[argidx] == "-arch" || args[argidx] == "-suppress" || args[argidx] == "-mapfile") && argidx+1 < args.size()) {
+ if (args[argidx] == "-mapfile" && !args[argidx+1].empty() && args[argidx+1][0] != '/') {
+ char pwd[PATH_MAX];
+ if (!getcwd(pwd, sizeof(pwd))) {
+ log_cmd_error("getcwd failed: %s", strerror(errno));
+ log_abort();
+ }
+ args[argidx+1] = pwd + ("/" + args[argidx+1]);
+ }
+ extra_opts += std::string(" ") + args[argidx];
+ extra_opts += std::string(" '") + args[++argidx] + std::string("'");
+ continue;
+ }
+ if (args[argidx] == "-unroll_generate" || args[argidx] == "-nogenericeval" || args[argidx] == "-nouniquify" ||
+ args[argidx] == "-oldparser" || args[argidx] == "-quiet" || args[argidx] == "-nobanner") {
+ extra_opts += std::string(" ") + args[argidx];
+ continue;
+ }
+ break;
+ }
+
+ if (argidx == args.size())
+ cmd_error(args, argidx, "Missing filenames.");
+ if (args[argidx].substr(0, 1) == "-")
+ cmd_error(args, argidx, "Unknown option.");
+ if (top_entity.empty())
+ log_cmd_error("Missing -top option.\n");
+
+ std::string tempdir_name = make_temp_dir("/tmp/yosys-vhdl2verilog-XXXXXX");
+ log("Using temp directory %s.\n", tempdir_name.c_str());
+
+ if (!out_file.empty() && out_file[0] != '/') {
+ char pwd[PATH_MAX];
+ if (!getcwd(pwd, sizeof(pwd))) {
+ log_cmd_error("getcwd failed: %s", strerror(errno));
+ log_abort();
+ }
+ out_file = pwd + ("/" + out_file);
+ }
+
+ FILE *f = fopen(stringf("%s/files.list", tempdir_name.c_str()).c_str(), "wt");
+ while (argidx < args.size()) {
+ std::string file = args[argidx++];
+ if (file.empty())
+ continue;
+ if (file[0] != '/') {
+ char pwd[PATH_MAX];
+ if (!getcwd(pwd, sizeof(pwd))) {
+ log_cmd_error("getcwd failed: %s", strerror(errno));
+ log_abort();
+ }
+ file = pwd + ("/" + file);
+ }
+ fprintf(f, "%s\n", file.c_str());
+ log("Adding '%s' to the file list.\n", file.c_str());
+ }
+ fclose(f);
+
+ std::string command = "exec 2>&1; ";
+ if (!vhdl2verilog_dir.empty())
+ command += stringf("cd '%s'; . ./setup_env.sh; ", vhdl2verilog_dir.c_str());
+ command += stringf("cd '%s'; vhdl2verilog -out '%s' -filelist files.list -top '%s'%s", tempdir_name.c_str(),
+ out_file.empty() ? "vhdl2verilog_output.v" : out_file.c_str(), top_entity.c_str(), extra_opts.c_str());
+
+ log("Running '%s'..\n", command.c_str());
+
+ int ret = run_command(command, [](const std::string &line) { log("%s", line.c_str()); });
+ if (ret != 0)
+ log_error("Execution of command \"%s\" failed: return code %d.\n", command.c_str(), ret);
+
+ if (out_file.empty()) {
+ std::ifstream ff;
+ ff.open(stringf("%s/vhdl2verilog_output.v", tempdir_name.c_str()).c_str());
+ if (ff.fail())
+ log_error("Can't open vhdl2verilog output file `vhdl2verilog_output.v'.\n");
+ Frontend::frontend_call(design, &ff, stringf("%s/vhdl2verilog_output.v", tempdir_name.c_str()), "verilog");
+ }
+
+ log_header(design, "Removing temp directory `%s':\n", tempdir_name.c_str());
+ remove_directory(tempdir_name);
+ log_pop();
+ }
+} Vhdl2verilogPass;
+
+YOSYS_NAMESPACE_END
+
diff --git a/kernel/bitpattern.h b/kernel/bitpattern.h
new file mode 100644
index 00000000..894a95ed
--- /dev/null
+++ b/kernel/bitpattern.h
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef BITPATTERN_H
+#define BITPATTERN_H
+
+#include "kernel/log.h"
+#include "kernel/rtlil.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+struct BitPatternPool
+{
+ int width;
+ struct bits_t {
+ std::vector<RTLIL::State> bitdata;
+ mutable unsigned int cached_hash;
+ bits_t(int width = 0) : bitdata(width), cached_hash(0) { }
+ RTLIL::State &operator[](int index) {
+ return bitdata[index];
+ }
+ const RTLIL::State &operator[](int index) const {
+ return bitdata[index];
+ }
+ bool operator==(const bits_t &other) const {
+ if (hash() != other.hash())
+ return false;
+ return bitdata == other.bitdata;
+ }
+ unsigned int hash() const {
+ if (!cached_hash)
+ cached_hash = hash_ops<std::vector<RTLIL::State>>::hash(bitdata);
+ return cached_hash;
+ }
+ };
+ pool<bits_t> database;
+
+ BitPatternPool(RTLIL::SigSpec sig)
+ {
+ width = sig.size();
+ if (width > 0) {
+ bits_t pattern(width);
+ for (int i = 0; i < width; i++) {
+ if (sig[i].wire == NULL && sig[i].data <= RTLIL::State::S1)
+ pattern[i] = sig[i].data;
+ else
+ pattern[i] = RTLIL::State::Sa;
+ }
+ database.insert(pattern);
+ }
+ }
+
+ BitPatternPool(int width)
+ {
+ this->width = width;
+ if (width > 0) {
+ bits_t pattern(width);
+ for (int i = 0; i < width; i++)
+ pattern[i] = RTLIL::State::Sa;
+ database.insert(pattern);
+ }
+ }
+
+ bits_t sig2bits(RTLIL::SigSpec sig)
+ {
+ bits_t bits;
+ bits.bitdata = sig.as_const().bits;
+ for (auto &b : bits.bitdata)
+ if (b > RTLIL::State::S1)
+ b = RTLIL::State::Sa;
+ return bits;
+ }
+
+ bool match(bits_t a, bits_t b)
+ {
+ log_assert(int(a.bitdata.size()) == width);
+ log_assert(int(b.bitdata.size()) == width);
+ for (int i = 0; i < width; i++)
+ if (a[i] <= RTLIL::State::S1 && b[i] <= RTLIL::State::S1 && a[i] != b[i])
+ return false;
+ return true;
+ }
+
+ bool has_any(RTLIL::SigSpec sig)
+ {
+ bits_t bits = sig2bits(sig);
+ for (auto &it : database)
+ if (match(it, bits))
+ return true;
+ return false;
+ }
+
+ bool has_all(RTLIL::SigSpec sig)
+ {
+ bits_t bits = sig2bits(sig);
+ for (auto &it : database)
+ if (match(it, bits)) {
+ for (int i = 0; i < width; i++)
+ if (bits[i] > RTLIL::State::S1 && it[i] <= RTLIL::State::S1)
+ goto next_database_entry;
+ return true;
+ next_database_entry:;
+ }
+ return false;
+ }
+
+ bool take(RTLIL::SigSpec sig)
+ {
+ bool status = false;
+ bits_t bits = sig2bits(sig);
+ for (auto it = database.begin(); it != database.end();)
+ if (match(*it, bits)) {
+ for (int i = 0; i < width; i++) {
+ if ((*it)[i] != RTLIL::State::Sa || bits[i] == RTLIL::State::Sa)
+ continue;
+ bits_t new_pattern;
+ new_pattern.bitdata = it->bitdata;
+ new_pattern[i] = bits[i] == RTLIL::State::S1 ? RTLIL::State::S0 : RTLIL::State::S1;
+ database.insert(new_pattern);
+ }
+ it = database.erase(it);
+ status = true;
+ continue;
+ } else
+ ++it;
+ return status;
+ }
+
+ bool take_all()
+ {
+ if (database.empty())
+ return false;
+ database.clear();
+ return true;
+ }
+
+ bool empty()
+ {
+ return database.empty();
+ }
+};
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/kernel/calc.cc b/kernel/calc.cc
new file mode 100644
index 00000000..a24fa2ab
--- /dev/null
+++ b/kernel/calc.cc
@@ -0,0 +1,586 @@
+/*
+ * 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.
+ *
+ */
+
+// [[CITE]] Power-Modulus Algorithm
+// Schneier, Bruce (1996). Applied Cryptography: Protocols, Algorithms, and Source Code in C,
+// Second Edition (2nd ed.). Wiley. ISBN 978-0-471-11709-4, page 244
+
+#include "kernel/yosys.h"
+#include "libs/bigint/BigIntegerLibrary.hh"
+
+YOSYS_NAMESPACE_BEGIN
+
+static void extend_u0(RTLIL::Const &arg, int width, bool is_signed)
+{
+ RTLIL::State padding = RTLIL::State::S0;
+
+ if (arg.bits.size() > 0 && is_signed)
+ padding = arg.bits.back();
+
+ while (int(arg.bits.size()) < width)
+ arg.bits.push_back(padding);
+
+ arg.bits.resize(width);
+}
+
+static BigInteger const2big(const RTLIL::Const &val, bool as_signed, int &undef_bit_pos)
+{
+ BigUnsigned mag;
+
+ BigInteger::Sign sign = BigInteger::positive;
+ State inv_sign_bit = RTLIL::State::S1;
+ size_t num_bits = val.bits.size();
+
+ if (as_signed && num_bits && val.bits[num_bits-1] == RTLIL::State::S1) {
+ inv_sign_bit = RTLIL::State::S0;
+ sign = BigInteger::negative;
+ num_bits--;
+ }
+
+ for (size_t i = 0; i < num_bits; i++)
+ if (val.bits[i] == RTLIL::State::S0 || val.bits[i] == RTLIL::State::S1)
+ mag.setBit(i, val.bits[i] == inv_sign_bit);
+ else if (undef_bit_pos < 0)
+ undef_bit_pos = i;
+
+ if (sign == BigInteger::negative)
+ mag += 1;
+
+ return BigInteger(mag, sign);
+}
+
+static RTLIL::Const big2const(const BigInteger &val, int result_len, int undef_bit_pos)
+{
+ if (undef_bit_pos >= 0)
+ return RTLIL::Const(RTLIL::State::Sx, result_len);
+
+ BigUnsigned mag = val.getMagnitude();
+ RTLIL::Const result(0, result_len);
+
+ if (!mag.isZero())
+ {
+ if (val.getSign() < 0)
+ {
+ mag--;
+ for (int i = 0; i < result_len; i++)
+ result.bits[i] = mag.getBit(i) ? RTLIL::State::S0 : RTLIL::State::S1;
+ }
+ else
+ {
+ for (int i = 0; i < result_len; i++)
+ result.bits[i] = mag.getBit(i) ? RTLIL::State::S1 : RTLIL::State::S0;
+ }
+ }
+
+#if 0
+ if (undef_bit_pos >= 0)
+ for (int i = undef_bit_pos; i < result_len; i++)
+ result.bits[i] = RTLIL::State::Sx;
+#endif
+
+ return result;
+}
+
+static RTLIL::State logic_and(RTLIL::State a, RTLIL::State b)
+{
+ if (a == RTLIL::State::S0) return RTLIL::State::S0;
+ if (b == RTLIL::State::S0) return RTLIL::State::S0;
+ if (a != RTLIL::State::S1) return RTLIL::State::Sx;
+ if (b != RTLIL::State::S1) return RTLIL::State::Sx;
+ return RTLIL::State::S1;
+}
+
+static RTLIL::State logic_or(RTLIL::State a, RTLIL::State b)
+{
+ if (a == RTLIL::State::S1) return RTLIL::State::S1;
+ if (b == RTLIL::State::S1) return RTLIL::State::S1;
+ if (a != RTLIL::State::S0) return RTLIL::State::Sx;
+ if (b != RTLIL::State::S0) return RTLIL::State::Sx;
+ return RTLIL::State::S0;
+}
+
+static RTLIL::State logic_xor(RTLIL::State a, RTLIL::State b)
+{
+ if (a != RTLIL::State::S0 && a != RTLIL::State::S1) return RTLIL::State::Sx;
+ if (b != RTLIL::State::S0 && b != RTLIL::State::S1) return RTLIL::State::Sx;
+ return a != b ? RTLIL::State::S1 : RTLIL::State::S0;
+}
+
+static RTLIL::State logic_xnor(RTLIL::State a, RTLIL::State b)
+{
+ if (a != RTLIL::State::S0 && a != RTLIL::State::S1) return RTLIL::State::Sx;
+ if (b != RTLIL::State::S0 && b != RTLIL::State::S1) return RTLIL::State::Sx;
+ return a == b ? RTLIL::State::S1 : RTLIL::State::S0;
+}
+
+RTLIL::Const RTLIL::const_not(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len)
+{
+ if (result_len < 0)
+ result_len = arg1.bits.size();
+
+ RTLIL::Const arg1_ext = arg1;
+ extend_u0(arg1_ext, result_len, signed1);
+
+ RTLIL::Const result(RTLIL::State::Sx, result_len);
+ for (size_t i = 0; i < size_t(result_len); i++) {
+ if (i >= arg1_ext.bits.size())
+ result.bits[i] = RTLIL::State::S0;
+ else if (arg1_ext.bits[i] == RTLIL::State::S0)
+ result.bits[i] = RTLIL::State::S1;
+ else if (arg1_ext.bits[i] == RTLIL::State::S1)
+ result.bits[i] = RTLIL::State::S0;
+ }
+
+ return result;
+}
+
+static RTLIL::Const logic_wrapper(RTLIL::State(*logic_func)(RTLIL::State, RTLIL::State),
+ RTLIL::Const arg1, RTLIL::Const arg2, bool signed1, bool signed2, int result_len = -1)
+{
+ if (result_len < 0)
+ result_len = max(arg1.bits.size(), arg2.bits.size());
+
+ extend_u0(arg1, result_len, signed1);
+ extend_u0(arg2, result_len, signed2);
+
+ RTLIL::Const result(RTLIL::State::Sx, result_len);
+ for (size_t i = 0; i < size_t(result_len); i++) {
+ RTLIL::State a = i < arg1.bits.size() ? arg1.bits[i] : RTLIL::State::S0;
+ RTLIL::State b = i < arg2.bits.size() ? arg2.bits[i] : RTLIL::State::S0;
+ result.bits[i] = logic_func(a, b);
+ }
+
+ return result;
+}
+
+RTLIL::Const RTLIL::const_and(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ return logic_wrapper(logic_and, arg1, arg2, signed1, signed2, result_len);
+}
+
+RTLIL::Const RTLIL::const_or(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ return logic_wrapper(logic_or, arg1, arg2, signed1, signed2, result_len);
+}
+
+RTLIL::Const RTLIL::const_xor(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ return logic_wrapper(logic_xor, arg1, arg2, signed1, signed2, result_len);
+}
+
+RTLIL::Const RTLIL::const_xnor(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ return logic_wrapper(logic_xnor, arg1, arg2, signed1, signed2, result_len);
+}
+
+static RTLIL::Const logic_reduce_wrapper(RTLIL::State initial, RTLIL::State(*logic_func)(RTLIL::State, RTLIL::State), const RTLIL::Const &arg1, int result_len)
+{
+ RTLIL::State temp = initial;
+
+ for (size_t i = 0; i < arg1.bits.size(); i++)
+ temp = logic_func(temp, arg1.bits[i]);
+
+ RTLIL::Const result(temp);
+ while (int(result.bits.size()) < result_len)
+ result.bits.push_back(RTLIL::State::S0);
+ return result;
+}
+
+RTLIL::Const RTLIL::const_reduce_and(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int result_len)
+{
+ return logic_reduce_wrapper(RTLIL::State::S1, logic_and, arg1, result_len);
+}
+
+RTLIL::Const RTLIL::const_reduce_or(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int result_len)
+{
+ return logic_reduce_wrapper(RTLIL::State::S0, logic_or, arg1, result_len);
+}
+
+RTLIL::Const RTLIL::const_reduce_xor(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int result_len)
+{
+ return logic_reduce_wrapper(RTLIL::State::S0, logic_xor, arg1, result_len);
+}
+
+RTLIL::Const RTLIL::const_reduce_xnor(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int result_len)
+{
+ RTLIL::Const buffer = logic_reduce_wrapper(RTLIL::State::S0, logic_xor, arg1, result_len);
+ if (!buffer.bits.empty()) {
+ if (buffer.bits.front() == RTLIL::State::S0)
+ buffer.bits.front() = RTLIL::State::S1;
+ else if (buffer.bits.front() == RTLIL::State::S1)
+ buffer.bits.front() = RTLIL::State::S0;
+ }
+ return buffer;
+}
+
+RTLIL::Const RTLIL::const_reduce_bool(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int result_len)
+{
+ return logic_reduce_wrapper(RTLIL::State::S0, logic_or, arg1, result_len);
+}
+
+RTLIL::Const RTLIL::const_logic_not(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len)
+{
+ int undef_bit_pos_a = -1;
+ BigInteger a = const2big(arg1, signed1, undef_bit_pos_a);
+ RTLIL::Const result(a.isZero() ? undef_bit_pos_a >= 0 ? RTLIL::State::Sx : RTLIL::State::S1 : RTLIL::State::S0);
+
+ while (int(result.bits.size()) < result_len)
+ result.bits.push_back(RTLIL::State::S0);
+ return result;
+}
+
+RTLIL::Const RTLIL::const_logic_and(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ int undef_bit_pos_a = -1, undef_bit_pos_b = -1;
+ BigInteger a = const2big(arg1, signed1, undef_bit_pos_a);
+ BigInteger b = const2big(arg2, signed2, undef_bit_pos_b);
+
+ RTLIL::State bit_a = a.isZero() ? undef_bit_pos_a >= 0 ? RTLIL::State::Sx : RTLIL::State::S0 : RTLIL::State::S1;
+ RTLIL::State bit_b = b.isZero() ? undef_bit_pos_b >= 0 ? RTLIL::State::Sx : RTLIL::State::S0 : RTLIL::State::S1;
+ RTLIL::Const result(logic_and(bit_a, bit_b));
+
+ while (int(result.bits.size()) < result_len)
+ result.bits.push_back(RTLIL::State::S0);
+ return result;
+}
+
+RTLIL::Const RTLIL::const_logic_or(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ int undef_bit_pos_a = -1, undef_bit_pos_b = -1;
+ BigInteger a = const2big(arg1, signed1, undef_bit_pos_a);
+ BigInteger b = const2big(arg2, signed2, undef_bit_pos_b);
+
+ RTLIL::State bit_a = a.isZero() ? undef_bit_pos_a >= 0 ? RTLIL::State::Sx : RTLIL::State::S0 : RTLIL::State::S1;
+ RTLIL::State bit_b = b.isZero() ? undef_bit_pos_b >= 0 ? RTLIL::State::Sx : RTLIL::State::S0 : RTLIL::State::S1;
+ RTLIL::Const result(logic_or(bit_a, bit_b));
+
+ while (int(result.bits.size()) < result_len)
+ result.bits.push_back(RTLIL::State::S0);
+ return result;
+}
+
+static RTLIL::Const const_shift_worker(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool sign_ext, int direction, int result_len)
+{
+ int undef_bit_pos = -1;
+ BigInteger offset = const2big(arg2, false, undef_bit_pos) * direction;
+
+ if (result_len < 0)
+ result_len = arg1.bits.size();
+
+ RTLIL::Const result(RTLIL::State::Sx, result_len);
+ if (undef_bit_pos >= 0)
+ return result;
+
+ for (int i = 0; i < result_len; i++) {
+ BigInteger pos = BigInteger(i) + offset;
+ if (pos < 0)
+ result.bits[i] = RTLIL::State::S0;
+ else if (pos >= arg1.bits.size())
+ result.bits[i] = sign_ext ? arg1.bits.back() : RTLIL::State::S0;
+ else
+ result.bits[i] = arg1.bits[pos.toInt()];
+ }
+
+ return result;
+}
+
+RTLIL::Const RTLIL::const_shl(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool, int result_len)
+{
+ RTLIL::Const arg1_ext = arg1;
+ extend_u0(arg1_ext, result_len, signed1);
+ return const_shift_worker(arg1_ext, arg2, false, -1, result_len);
+}
+
+RTLIL::Const RTLIL::const_shr(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool, int result_len)
+{
+ RTLIL::Const arg1_ext = arg1;
+ extend_u0(arg1_ext, max(result_len, GetSize(arg1)), signed1);
+ return const_shift_worker(arg1_ext, arg2, false, +1, result_len);
+}
+
+RTLIL::Const RTLIL::const_sshl(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ if (!signed1)
+ return const_shl(arg1, arg2, signed1, signed2, result_len);
+ return const_shift_worker(arg1, arg2, true, -1, result_len);
+}
+
+RTLIL::Const RTLIL::const_sshr(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ if (!signed1)
+ return const_shr(arg1, arg2, signed1, signed2, result_len);
+ return const_shift_worker(arg1, arg2, true, +1, result_len);
+}
+
+static RTLIL::Const const_shift_shiftx(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool signed2, int result_len, RTLIL::State other_bits)
+{
+ int undef_bit_pos = -1;
+ BigInteger offset = const2big(arg2, signed2, undef_bit_pos);
+
+ if (result_len < 0)
+ result_len = arg1.bits.size();
+
+ RTLIL::Const result(RTLIL::State::Sx, result_len);
+ if (undef_bit_pos >= 0)
+ return result;
+
+ for (int i = 0; i < result_len; i++) {
+ BigInteger pos = BigInteger(i) + offset;
+ if (pos < 0 || pos >= arg1.bits.size())
+ result.bits[i] = other_bits;
+ else
+ result.bits[i] = arg1.bits[pos.toInt()];
+ }
+
+ return result;
+}
+
+RTLIL::Const RTLIL::const_shift(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ return const_shift_shiftx(arg1, arg2, signed1, signed2, result_len, RTLIL::State::S0);
+}
+
+RTLIL::Const RTLIL::const_shiftx(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ return const_shift_shiftx(arg1, arg2, signed1, signed2, result_len, RTLIL::State::Sx);
+}
+
+RTLIL::Const RTLIL::const_lt(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ int undef_bit_pos = -1;
+ bool y = const2big(arg1, signed1, undef_bit_pos) < const2big(arg2, signed2, undef_bit_pos);
+ RTLIL::Const result(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0);
+
+ while (int(result.bits.size()) < result_len)
+ result.bits.push_back(RTLIL::State::S0);
+ return result;
+}
+
+RTLIL::Const RTLIL::const_le(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ int undef_bit_pos = -1;
+ bool y = const2big(arg1, signed1, undef_bit_pos) <= const2big(arg2, signed2, undef_bit_pos);
+ RTLIL::Const result(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0);
+
+ while (int(result.bits.size()) < result_len)
+ result.bits.push_back(RTLIL::State::S0);
+ return result;
+}
+
+RTLIL::Const RTLIL::const_eq(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ RTLIL::Const arg1_ext = arg1;
+ RTLIL::Const arg2_ext = arg2;
+ RTLIL::Const result(RTLIL::State::S0, result_len);
+
+ int width = max(arg1_ext.bits.size(), arg2_ext.bits.size());
+ extend_u0(arg1_ext, width, signed1 && signed2);
+ extend_u0(arg2_ext, width, signed1 && signed2);
+
+ RTLIL::State matched_status = RTLIL::State::S1;
+ for (size_t i = 0; i < arg1_ext.bits.size(); i++) {
+ if (arg1_ext.bits.at(i) == RTLIL::State::S0 && arg2_ext.bits.at(i) == RTLIL::State::S1)
+ return result;
+ if (arg1_ext.bits.at(i) == RTLIL::State::S1 && arg2_ext.bits.at(i) == RTLIL::State::S0)
+ return result;
+ if (arg1_ext.bits.at(i) > RTLIL::State::S1 || arg2_ext.bits.at(i) > RTLIL::State::S1)
+ matched_status = RTLIL::State::Sx;
+ }
+
+ result.bits.front() = matched_status;
+ return result;
+}
+
+RTLIL::Const RTLIL::const_ne(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ RTLIL::Const result = RTLIL::const_eq(arg1, arg2, signed1, signed2, result_len);
+ if (result.bits.front() == RTLIL::State::S0)
+ result.bits.front() = RTLIL::State::S1;
+ else if (result.bits.front() == RTLIL::State::S1)
+ result.bits.front() = RTLIL::State::S0;
+ return result;
+}
+
+RTLIL::Const RTLIL::const_eqx(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ RTLIL::Const arg1_ext = arg1;
+ RTLIL::Const arg2_ext = arg2;
+ RTLIL::Const result(RTLIL::State::S0, result_len);
+
+ int width = max(arg1_ext.bits.size(), arg2_ext.bits.size());
+ extend_u0(arg1_ext, width, signed1 && signed2);
+ extend_u0(arg2_ext, width, signed1 && signed2);
+
+ for (size_t i = 0; i < arg1_ext.bits.size(); i++) {
+ if (arg1_ext.bits.at(i) != arg2_ext.bits.at(i))
+ return result;
+ }
+
+ result.bits.front() = RTLIL::State::S1;
+ return result;
+}
+
+RTLIL::Const RTLIL::const_nex(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ RTLIL::Const result = RTLIL::const_eqx(arg1, arg2, signed1, signed2, result_len);
+ if (result.bits.front() == RTLIL::State::S0)
+ result.bits.front() = RTLIL::State::S1;
+ else if (result.bits.front() == RTLIL::State::S1)
+ result.bits.front() = RTLIL::State::S0;
+ return result;
+}
+
+RTLIL::Const RTLIL::const_ge(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ int undef_bit_pos = -1;
+ bool y = const2big(arg1, signed1, undef_bit_pos) >= const2big(arg2, signed2, undef_bit_pos);
+ RTLIL::Const result(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0);
+
+ while (int(result.bits.size()) < result_len)
+ result.bits.push_back(RTLIL::State::S0);
+ return result;
+}
+
+RTLIL::Const RTLIL::const_gt(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ int undef_bit_pos = -1;
+ bool y = const2big(arg1, signed1, undef_bit_pos) > const2big(arg2, signed2, undef_bit_pos);
+ RTLIL::Const result(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0);
+
+ while (int(result.bits.size()) < result_len)
+ result.bits.push_back(RTLIL::State::S0);
+ return result;
+}
+
+RTLIL::Const RTLIL::const_add(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ int undef_bit_pos = -1;
+ BigInteger y = const2big(arg1, signed1, undef_bit_pos) + const2big(arg2, signed2, undef_bit_pos);
+ return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), undef_bit_pos);
+}
+
+RTLIL::Const RTLIL::const_sub(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ int undef_bit_pos = -1;
+ BigInteger y = const2big(arg1, signed1, undef_bit_pos) - const2big(arg2, signed2, undef_bit_pos);
+ return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), undef_bit_pos);
+}
+
+RTLIL::Const RTLIL::const_mul(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ int undef_bit_pos = -1;
+ BigInteger y = const2big(arg1, signed1, undef_bit_pos) * const2big(arg2, signed2, undef_bit_pos);
+ return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
+}
+
+RTLIL::Const RTLIL::const_div(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ int undef_bit_pos = -1;
+ BigInteger a = const2big(arg1, signed1, undef_bit_pos);
+ BigInteger b = const2big(arg2, signed2, undef_bit_pos);
+ if (b.isZero())
+ return RTLIL::Const(RTLIL::State::Sx, result_len);
+ bool result_neg = (a.getSign() == BigInteger::negative) != (b.getSign() == BigInteger::negative);
+ a = a.getSign() == BigInteger::negative ? -a : a;
+ b = b.getSign() == BigInteger::negative ? -b : b;
+ return big2const(result_neg ? -(a / b) : (a / b), result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
+}
+
+RTLIL::Const RTLIL::const_mod(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ int undef_bit_pos = -1;
+ BigInteger a = const2big(arg1, signed1, undef_bit_pos);
+ BigInteger b = const2big(arg2, signed2, undef_bit_pos);
+ if (b.isZero())
+ return RTLIL::Const(RTLIL::State::Sx, result_len);
+ bool result_neg = a.getSign() == BigInteger::negative;
+ a = a.getSign() == BigInteger::negative ? -a : a;
+ b = b.getSign() == BigInteger::negative ? -b : b;
+ return big2const(result_neg ? -(a % b) : (a % b), result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
+}
+
+RTLIL::Const RTLIL::const_pow(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ int undef_bit_pos = -1;
+
+ BigInteger a = const2big(arg1, signed1, undef_bit_pos);
+ BigInteger b = const2big(arg2, signed2, undef_bit_pos);
+ BigInteger y = 1;
+
+ if (a == 0 && b < 0)
+ return RTLIL::Const(RTLIL::State::Sx, result_len);
+
+ if (a == 0 && b > 0)
+ return RTLIL::Const(RTLIL::State::S0, result_len);
+
+ if (b < 0)
+ {
+ if (a < -1 || a > 1)
+ y = 0;
+ if (a == -1)
+ y = (-b % 2) == 0 ? 1 : -1;
+ }
+
+ if (b > 0)
+ {
+ // Power-modulo with 2^result_len as modulus
+ BigInteger modulus = 1;
+ int modulus_bits = (result_len >= 0 ? result_len : 1024);
+ for (int i = 0; i < modulus_bits; i++)
+ modulus *= 2;
+
+ bool flip_result_sign = false;
+ if (a < 0) {
+ a *= -1;
+ if (b % 2 == 1)
+ flip_result_sign = true;
+ }
+
+ while (b > 0) {
+ if (b % 2 == 1)
+ y = (y * a) % modulus;
+ b = b / 2;
+ a = (a * a) % modulus;
+ }
+
+ if (flip_result_sign)
+ y *= -1;
+ }
+
+ return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
+}
+
+RTLIL::Const RTLIL::const_pos(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len)
+{
+ RTLIL::Const arg1_ext = arg1;
+ extend_u0(arg1_ext, result_len, signed1);
+
+ return arg1_ext;
+}
+
+RTLIL::Const RTLIL::const_neg(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len)
+{
+ RTLIL::Const arg1_ext = arg1;
+ RTLIL::Const zero(RTLIL::State::S0, 1);
+
+ return RTLIL::const_sub(zero, arg1_ext, true, signed1, result_len);
+}
+
+YOSYS_NAMESPACE_END
+
diff --git a/kernel/cellaigs.cc b/kernel/cellaigs.cc
new file mode 100644
index 00000000..41f81355
--- /dev/null
+++ b/kernel/cellaigs.cc
@@ -0,0 +1,481 @@
+/*
+ * 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/cellaigs.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+AigNode::AigNode()
+{
+ portbit = -1;
+ inverter = false;
+ left_parent = -1;
+ right_parent = -1;
+}
+
+bool AigNode::operator==(const AigNode &other) const
+{
+ if (portname != other.portname) return false;
+ if (portbit != other.portbit) return false;
+ if (inverter != other.inverter) return false;
+ if (left_parent != other.left_parent) return false;
+ if (right_parent != other.right_parent) return false;
+ return true;
+}
+
+unsigned int AigNode::hash() const
+{
+ unsigned int h = mkhash_init;
+ h = mkhash(portname.hash(), portbit);
+ h = mkhash(h, inverter);
+ h = mkhash(h, left_parent);
+ h = mkhash(h, right_parent);
+ return h;
+}
+
+bool Aig::operator==(const Aig &other) const
+{
+ return name == other.name;
+}
+
+unsigned int Aig::hash() const
+{
+ return hash_ops<std::string>::hash(name);
+}
+
+struct AigMaker
+{
+ Aig *aig;
+ Cell *cell;
+ idict<AigNode> aig_indices;
+
+ int the_true_node;
+ int the_false_node;
+
+ AigMaker(Aig *aig, Cell *cell) : aig(aig), cell(cell)
+ {
+ the_true_node = -1;
+ the_false_node = -1;
+ }
+
+ int node2index(const AigNode &node)
+ {
+ if (node.left_parent > node.right_parent) {
+ AigNode n(node);
+ std::swap(n.left_parent, n.right_parent);
+ return node2index(n);
+ }
+
+ if (!aig_indices.count(node)) {
+ aig_indices.expect(node, GetSize(aig->nodes));
+ aig->nodes.push_back(node);
+ }
+
+ return aig_indices.at(node);
+ }
+
+ int bool_node(bool value)
+ {
+ AigNode node;
+ node.inverter = value;
+ return node2index(node);
+ }
+
+ int inport(IdString portname, int portbit = 0, bool inverter = false)
+ {
+ if (portbit >= GetSize(cell->getPort(portname))) {
+ if (cell->parameters.count(portname.str() + "_SIGNED") && cell->getParam(portname.str() + "_SIGNED").as_bool())
+ return inport(portname, GetSize(cell->getPort(portname))-1, inverter);
+ return bool_node(inverter);
+ }
+
+ AigNode node;
+ node.portname = portname;
+ node.portbit = portbit;
+ node.inverter = inverter;
+ return node2index(node);
+ }
+
+ vector<int> inport_vec(IdString portname, int width)
+ {
+ vector<int> vec;
+ for (int i = 0; i < width; i++)
+ vec.push_back(inport(portname, i));
+ return vec;
+ }
+
+ int not_inport(IdString portname, int portbit = 0)
+ {
+ return inport(portname, portbit, true);
+ }
+
+ int not_gate(int A)
+ {
+ AigNode node(aig_indices[A]);
+ node.outports.clear();
+ node.inverter = !node.inverter;
+ return node2index(node);
+ }
+
+ int and_gate(int A, int B, bool inverter = false)
+ {
+ if (A == B)
+ return inverter ? not_gate(A) : A;
+
+ const AigNode &nA = aig_indices[A];
+ const AigNode &nB = aig_indices[B];
+
+ AigNode nB_inv(nB);
+ nB_inv.inverter = !nB_inv.inverter;
+
+ if (nA == nB_inv)
+ return bool_node(inverter);
+
+ bool nA_bool = nA.portbit < 0 && nA.left_parent < 0 && nA.right_parent < 0;
+ bool nB_bool = nB.portbit < 0 && nB.left_parent < 0 && nB.right_parent < 0;
+
+ if (nA_bool && nB_bool) {
+ bool bA = nA.inverter;
+ bool bB = nB.inverter;
+ return bool_node(inverter != (bA && bB));
+ }
+
+ if (nA_bool) {
+ bool bA = nA.inverter;
+ if (inverter)
+ return bA ? not_gate(B) : bool_node(true);
+ return bA ? B : bool_node(false);
+ }
+
+ if (nB_bool) {
+ bool bB = nB.inverter;
+ if (inverter)
+ return bB ? not_gate(A) : bool_node(true);
+ return bB ? A : bool_node(false);
+ }
+
+ AigNode node;
+ node.inverter = inverter;
+ node.left_parent = A;
+ node.right_parent = B;
+ return node2index(node);
+ }
+
+ int nand_gate(int A, int B)
+ {
+ return and_gate(A, B, true);
+ }
+
+ int or_gate(int A, int B)
+ {
+ return nand_gate(not_gate(A), not_gate(B));
+ }
+
+ int nor_gate(int A, int B)
+ {
+ return and_gate(not_gate(A), not_gate(B));
+ }
+
+ int xor_gate(int A, int B)
+ {
+ return nor_gate(and_gate(A, B), nor_gate(A, B));
+ }
+
+ int xnor_gate(int A, int B)
+ {
+ return or_gate(and_gate(A, B), nor_gate(A, B));
+ }
+
+ int mux_gate(int A, int B, int S)
+ {
+ return or_gate(and_gate(A, not_gate(S)), and_gate(B, S));
+ }
+
+ vector<int> adder(const vector<int> &A, const vector<int> &B, int carry, vector<int> *X = nullptr, vector<int> *CO = nullptr)
+ {
+ vector<int> Y(GetSize(A));
+ log_assert(GetSize(A) == GetSize(B));
+ for (int i = 0; i < GetSize(A); i++) {
+ Y[i] = xor_gate(xor_gate(A[i], B[i]), carry);
+ carry = or_gate(and_gate(A[i], B[i]), and_gate(or_gate(A[i], B[i]), carry));
+ if (X != nullptr)
+ X->at(i) = xor_gate(A[i], B[i]);
+ if (CO != nullptr)
+ CO->at(i) = carry;
+ }
+ return Y;
+ }
+
+ void outport(int node, IdString portname, int portbit = 0)
+ {
+ if (portbit < GetSize(cell->getPort(portname)))
+ aig->nodes.at(node).outports.push_back(pair<IdString, int>(portname, portbit));
+ }
+
+ void outport_bool(int node, IdString portname)
+ {
+ outport(node, portname);
+ for (int i = 1; i < GetSize(cell->getPort(portname)); i++)
+ outport(bool_node(false), portname, i);
+ }
+
+ void outport_vec(const vector<int> &vec, IdString portname)
+ {
+ for (int i = 0; i < GetSize(vec); i++)
+ outport(vec.at(i), portname, i);
+ }
+};
+
+Aig::Aig(Cell *cell)
+{
+ if (cell->type[0] != '$')
+ return;
+
+ AigMaker mk(this, cell);
+ name = cell->type.str();
+
+ string mkname_last;
+ bool mkname_a_signed = false;
+ bool mkname_b_signed = false;
+ bool mkname_is_signed = false;
+
+ cell->parameters.sort();
+ for (auto p : cell->parameters)
+ {
+ if (p.first == "\\A_WIDTH" && mkname_a_signed) {
+ name = mkname_last + stringf(":%d%c", p.second.as_int(), mkname_is_signed ? 'S' : 'U');
+ } else if (p.first == "\\B_WIDTH" && mkname_b_signed) {
+ name = mkname_last + stringf(":%d%c", p.second.as_int(), mkname_is_signed ? 'S' : 'U');
+ } else {
+ mkname_last = name;
+ name += stringf(":%d", p.second.as_int());
+ }
+
+ mkname_a_signed = false;
+ mkname_b_signed = false;
+ mkname_is_signed = false;
+ if (p.first == "\\A_SIGNED") {
+ mkname_a_signed = true;
+ mkname_is_signed = p.second.as_bool();
+ }
+ if (p.first == "\\B_SIGNED") {
+ mkname_b_signed = true;
+ mkname_is_signed = p.second.as_bool();
+ }
+ }
+
+ if (cell->type.in("$not", "$_NOT_", "$pos", "$_BUF_"))
+ {
+ for (int i = 0; i < GetSize(cell->getPort("\\Y")); i++) {
+ int A = mk.inport("\\A", i);
+ int Y = cell->type.in("$not", "$_NOT_") ? mk.not_gate(A) : A;
+ mk.outport(Y, "\\Y", i);
+ }
+ goto optimize;
+ }
+
+ if (cell->type.in("$and", "$_AND_", "$_NAND_", "$or", "$_OR_", "$_NOR_", "$xor", "$xnor", "$_XOR_", "$_XNOR_"))
+ {
+ for (int i = 0; i < GetSize(cell->getPort("\\Y")); i++) {
+ int A = mk.inport("\\A", i);
+ int B = mk.inport("\\B", i);
+ int Y = cell->type.in("$and", "$_AND_") ? mk.and_gate(A, B) :
+ cell->type.in("$_NAND_") ? mk.nand_gate(A, B) :
+ cell->type.in("$or", "$_OR_") ? mk.or_gate(A, B) :
+ cell->type.in("$_NOR_") ? mk.nor_gate(A, B) :
+ cell->type.in("$xor", "$_XOR_") ? mk.xor_gate(A, B) :
+ cell->type.in("$xnor", "$_XNOR_") ? mk.xnor_gate(A, B) : -1;
+ mk.outport(Y, "\\Y", i);
+ }
+ goto optimize;
+ }
+
+ if (cell->type.in("$mux", "$_MUX_"))
+ {
+ int S = mk.inport("\\S");
+ for (int i = 0; i < GetSize(cell->getPort("\\Y")); i++) {
+ int A = mk.inport("\\A", i);
+ int B = mk.inport("\\B", i);
+ int Y = mk.mux_gate(A, B, S);
+ mk.outport(Y, "\\Y", i);
+ }
+ goto optimize;
+ }
+
+ if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool"))
+ {
+ int Y = mk.inport("\\A", 0);
+ for (int i = 1; i < GetSize(cell->getPort("\\A")); i++) {
+ int A = mk.inport("\\A", i);
+ if (cell->type == "$reduce_and") Y = mk.and_gate(A, Y);
+ if (cell->type == "$reduce_or") Y = mk.or_gate(A, Y);
+ if (cell->type == "$reduce_bool") Y = mk.or_gate(A, Y);
+ if (cell->type == "$reduce_xor") Y = mk.xor_gate(A, Y);
+ if (cell->type == "$reduce_xnor") Y = mk.xor_gate(A, Y);
+ }
+ if (cell->type == "$reduce_xnor")
+ Y = mk.not_gate(Y);
+ mk.outport(Y, "\\Y", 0);
+ for (int i = 1; i < GetSize(cell->getPort("\\Y")); i++)
+ mk.outport(mk.bool_node(false), "\\Y", i);
+ goto optimize;
+ }
+
+ if (cell->type.in("$logic_not", "$logic_and", "$logic_or"))
+ {
+ int A = mk.inport("\\A", 0), Y = -1;
+ for (int i = 1; i < GetSize(cell->getPort("\\A")); i++)
+ A = mk.or_gate(mk.inport("\\A", i), A);
+ if (cell->type.in("$logic_and", "$logic_or")) {
+ int B = mk.inport("\\B", 0);
+ for (int i = 1; i < GetSize(cell->getPort("\\B")); i++)
+ B = mk.or_gate(mk.inport("\\B", i), B);
+ if (cell->type == "$logic_and") Y = mk.and_gate(A, B);
+ if (cell->type == "$logic_or") Y = mk.or_gate(A, B);
+ } else {
+ if (cell->type == "$logic_not") Y = mk.not_gate(A);
+ }
+ mk.outport_bool(Y, "\\Y");
+ goto optimize;
+ }
+
+ if (cell->type.in("$add", "$sub"))
+ {
+ int width = GetSize(cell->getPort("\\Y"));
+ vector<int> A = mk.inport_vec("\\A", width);
+ vector<int> B = mk.inport_vec("\\B", width);
+ int carry = mk.bool_node(false);
+ if (cell->type == "$sub") {
+ for (auto &n : B)
+ n = mk.not_gate(n);
+ carry = mk.not_gate(carry);
+ }
+ vector<int> Y = mk.adder(A, B, carry);
+ mk.outport_vec(Y, "\\Y");
+ goto optimize;
+ }
+
+ if (cell->type == "$alu")
+ {
+ int width = GetSize(cell->getPort("\\Y"));
+ vector<int> A = mk.inport_vec("\\A", width);
+ vector<int> B = mk.inport_vec("\\B", width);
+ int carry = mk.inport("\\CI");
+ int binv = mk.inport("\\BI");
+ for (auto &n : B)
+ n = mk.xor_gate(n, binv);
+ vector<int> X(width), CO(width);
+ vector<int> Y = mk.adder(A, B, carry, &X, &CO);
+ for (int i = 0; i < width; i++)
+ X[i] = mk.xor_gate(A[i], B[i]);
+ mk.outport_vec(Y, "\\Y");
+ mk.outport_vec(X, "\\X");
+ mk.outport_vec(CO, "\\CO");
+ goto optimize;
+ }
+
+ if (cell->type.in("$eq", "$ne"))
+ {
+ int width = max(GetSize(cell->getPort("\\A")), GetSize(cell->getPort("\\B")));
+ vector<int> A = mk.inport_vec("\\A", width);
+ vector<int> B = mk.inport_vec("\\B", width);
+ int Y = mk.bool_node(false);
+ for (int i = 0; i < width; i++)
+ Y = mk.or_gate(Y, mk.xor_gate(A[i], B[i]));
+ if (cell->type == "$eq")
+ Y = mk.not_gate(Y);
+ mk.outport_bool(Y, "\\Y");
+ goto optimize;
+ }
+
+ if (cell->type == "$_AOI3_")
+ {
+ int A = mk.inport("\\A");
+ int B = mk.inport("\\B");
+ int C = mk.inport("\\C");
+ int Y = mk.nor_gate(mk.and_gate(A, B), C);
+ mk.outport(Y, "\\Y");
+ goto optimize;
+ }
+
+ if (cell->type == "$_OAI3_")
+ {
+ int A = mk.inport("\\A");
+ int B = mk.inport("\\B");
+ int C = mk.inport("\\C");
+ int Y = mk.nand_gate(mk.or_gate(A, B), C);
+ mk.outport(Y, "\\Y");
+ goto optimize;
+ }
+
+ if (cell->type == "$_AOI4_")
+ {
+ int A = mk.inport("\\A");
+ int B = mk.inport("\\B");
+ int C = mk.inport("\\C");
+ int D = mk.inport("\\D");
+ int Y = mk.nor_gate(mk.and_gate(A, B), mk.and_gate(C, D));
+ mk.outport(Y, "\\Y");
+ goto optimize;
+ }
+
+ if (cell->type == "$_OAI4_")
+ {
+ int A = mk.inport("\\A");
+ int B = mk.inport("\\B");
+ int C = mk.inport("\\C");
+ int D = mk.inport("\\D");
+ int Y = mk.nand_gate(mk.nor_gate(A, B), mk.nor_gate(C, D));
+ mk.outport(Y, "\\Y");
+ goto optimize;
+ }
+
+ name.clear();
+ return;
+
+optimize:;
+ pool<int> used_old_ids;
+ vector<AigNode> new_nodes;
+ dict<int, int> old_to_new_ids;
+ old_to_new_ids[-1] = -1;
+
+ for (int i = GetSize(nodes)-1; i >= 0; i--) {
+ if (!nodes[i].outports.empty())
+ used_old_ids.insert(i);
+ if (!used_old_ids.count(i))
+ continue;
+ if (nodes[i].left_parent >= 0)
+ used_old_ids.insert(nodes[i].left_parent);
+ if (nodes[i].right_parent >= 0)
+ used_old_ids.insert(nodes[i].right_parent);
+ }
+
+ for (int i = 0; i < GetSize(nodes); i++) {
+ if (!used_old_ids.count(i))
+ continue;
+ nodes[i].left_parent = old_to_new_ids.at(nodes[i].left_parent);
+ nodes[i].right_parent = old_to_new_ids.at(nodes[i].right_parent);
+ old_to_new_ids[i] = GetSize(new_nodes);
+ new_nodes.push_back(nodes[i]);
+ }
+
+ new_nodes.swap(nodes);
+}
+
+YOSYS_NAMESPACE_END
diff --git a/kernel/cellaigs.h b/kernel/cellaigs.h
new file mode 100644
index 00000000..1417a614
--- /dev/null
+++ b/kernel/cellaigs.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef CELLAIGS_H
+#define CELLAIGS_H
+
+#include "kernel/yosys.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+struct AigNode
+{
+ IdString portname;
+ int portbit;
+ bool inverter;
+ int left_parent, right_parent;
+ vector<pair<IdString, int>> outports;
+
+ AigNode();
+ bool operator==(const AigNode &other) const;
+ unsigned int hash() const;
+};
+
+struct Aig
+{
+ string name;
+ vector<AigNode> nodes;
+ Aig(Cell *cell);
+
+ bool operator==(const Aig &other) const;
+ unsigned int hash() const;
+};
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/kernel/celledges.cc b/kernel/celledges.cc
new file mode 100644
index 00000000..556e8b82
--- /dev/null
+++ b/kernel/celledges.cc
@@ -0,0 +1,209 @@
+/*
+ * 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/celledges.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void bitwise_unary_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
+{
+ IdString A = "\\A", Y = "\\Y";
+
+ bool is_signed = cell->getParam("\\A_SIGNED").as_bool();
+ int a_width = GetSize(cell->getPort(A));
+ int y_width = GetSize(cell->getPort(Y));
+
+ for (int i = 0; i < y_width; i++)
+ {
+ if (i < a_width)
+ db->add_edge(cell, A, i, Y, i, -1);
+ else if (is_signed && a_width > 0)
+ db->add_edge(cell, A, a_width-1, Y, i, -1);
+ }
+}
+
+void bitwise_binary_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
+{
+ IdString A = "\\A", B = "\\B", Y = "\\Y";
+
+ bool is_signed = cell->getParam("\\A_SIGNED").as_bool();
+ int a_width = GetSize(cell->getPort(A));
+ int b_width = GetSize(cell->getPort(B));
+ int y_width = GetSize(cell->getPort(Y));
+
+ if (cell->type == "$and" && !is_signed) {
+ if (a_width > b_width)
+ a_width = b_width;
+ else
+ b_width = a_width;
+ }
+
+ for (int i = 0; i < y_width; i++)
+ {
+ if (i < a_width)
+ db->add_edge(cell, A, i, Y, i, -1);
+ else if (is_signed && a_width > 0)
+ db->add_edge(cell, A, a_width-1, Y, i, -1);
+
+ if (i < b_width)
+ db->add_edge(cell, B, i, Y, i, -1);
+ else if (is_signed && b_width > 0)
+ db->add_edge(cell, B, b_width-1, Y, i, -1);
+ }
+}
+
+void arith_neg_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
+{
+ IdString A = "\\A", Y = "\\Y";
+
+ bool is_signed = cell->getParam("\\A_SIGNED").as_bool();
+ int a_width = GetSize(cell->getPort(A));
+ int y_width = GetSize(cell->getPort(Y));
+
+ if (is_signed && a_width == 1)
+ y_width = std::min(y_width, 1);
+
+ for (int i = 0; i < y_width; i++)
+ for (int k = 0; k <= i && k < a_width; k++)
+ db->add_edge(cell, A, k, Y, i, -1);
+}
+
+void arith_binary_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
+{
+ IdString A = "\\A", B = "\\B", Y = "\\Y";
+
+ bool is_signed = cell->getParam("\\A_SIGNED").as_bool();
+ int a_width = GetSize(cell->getPort(A));
+ int b_width = GetSize(cell->getPort(B));
+ int y_width = GetSize(cell->getPort(Y));
+
+ if (!is_signed && cell->type != "$sub") {
+ int ab_width = std::max(a_width, b_width);
+ y_width = std::min(y_width, ab_width+1);
+ }
+
+ for (int i = 0; i < y_width; i++)
+ {
+ for (int k = 0; k <= i; k++)
+ {
+ if (k < a_width)
+ db->add_edge(cell, A, k, Y, i, -1);
+
+ if (k < b_width)
+ db->add_edge(cell, B, k, Y, i, -1);
+ }
+ }
+}
+
+void reduce_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
+{
+ IdString A = "\\A", Y = "\\Y";
+
+ int a_width = GetSize(cell->getPort(A));
+
+ for (int i = 0; i < a_width; i++)
+ db->add_edge(cell, A, i, Y, 0, -1);
+}
+
+void compare_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
+{
+ IdString A = "\\A", B = "\\B", Y = "\\Y";
+
+ int a_width = GetSize(cell->getPort(A));
+ int b_width = GetSize(cell->getPort(B));
+
+ for (int i = 0; i < a_width; i++)
+ db->add_edge(cell, A, i, Y, 0, -1);
+
+ for (int i = 0; i < b_width; i++)
+ db->add_edge(cell, B, i, Y, 0, -1);
+}
+
+void mux_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
+{
+ IdString A = "\\A", B = "\\B", S = "\\S", Y = "\\Y";
+
+ int a_width = GetSize(cell->getPort(A));
+ int b_width = GetSize(cell->getPort(B));
+ int s_width = GetSize(cell->getPort(S));
+
+ for (int i = 0; i < a_width; i++)
+ {
+ db->add_edge(cell, A, i, Y, i, -1);
+
+ for (int k = i; k < b_width; k += a_width)
+ db->add_edge(cell, B, k, Y, i, -1);
+
+ for (int k = 0; k < s_width; k++)
+ db->add_edge(cell, S, k, Y, i, -1);
+ }
+}
+
+PRIVATE_NAMESPACE_END
+
+bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL::Cell *cell)
+{
+ if (cell->type.in("$not", "$pos")) {
+ bitwise_unary_op(this, cell);
+ return true;
+ }
+
+ if (cell->type.in("$and", "$or", "$xor", "$xnor")) {
+ bitwise_binary_op(this, cell);
+ return true;
+ }
+
+ if (cell->type == "$neg") {
+ arith_neg_op(this, cell);
+ return true;
+ }
+
+ if (cell->type.in("$add", "$sub")) {
+ arith_binary_op(this, cell);
+ return true;
+ }
+
+ if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool", "$logic_not")) {
+ reduce_op(this, cell);
+ return true;
+ }
+
+ // FIXME:
+ // if (cell->type.in("$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx")) {
+ // shift_op(this, cell);
+ // return true;
+ // }
+
+ if (cell->type.in("$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt")) {
+ compare_op(this, cell);
+ return true;
+ }
+
+ if (cell->type.in("$mux", "$pmux")) {
+ mux_op(this, cell);
+ return true;
+ }
+
+ // FIXME: $mul $div $mod $slice $concat
+ // FIXME: $lut $sop $alu $lcu $macc $fa
+
+ return false;
+}
+
diff --git a/kernel/celledges.h b/kernel/celledges.h
new file mode 100644
index 00000000..6aab9ed4
--- /dev/null
+++ b/kernel/celledges.h
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef CELLEDGES_H
+#define CELLEDGES_H
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+struct AbstractCellEdgesDatabase
+{
+ virtual ~AbstractCellEdgesDatabase() { }
+ virtual void add_edge(RTLIL::Cell *cell, RTLIL::IdString from_port, int from_bit, RTLIL::IdString to_port, int to_bit, int delay) = 0;
+ bool add_edges_from_cell(RTLIL::Cell *cell);
+};
+
+struct FwdCellEdgesDatabase : AbstractCellEdgesDatabase
+{
+ SigMap &sigmap;
+ dict<SigBit, pool<SigBit>> db;
+ FwdCellEdgesDatabase(SigMap &sigmap) : sigmap(sigmap) { }
+
+ virtual void add_edge(RTLIL::Cell *cell, RTLIL::IdString from_port, int from_bit, RTLIL::IdString to_port, int to_bit, int) override {
+ SigBit from_sigbit = sigmap(cell->getPort(from_port)[from_bit]);
+ SigBit to_sigbit = sigmap(cell->getPort(to_port)[to_bit]);
+ db[from_sigbit].insert(to_sigbit);
+ }
+};
+
+struct RevCellEdgesDatabase : AbstractCellEdgesDatabase
+{
+ SigMap &sigmap;
+ dict<SigBit, pool<SigBit>> db;
+ RevCellEdgesDatabase(SigMap &sigmap) : sigmap(sigmap) { }
+
+ virtual void add_edge(RTLIL::Cell *cell, RTLIL::IdString from_port, int from_bit, RTLIL::IdString to_port, int to_bit, int) override {
+ SigBit from_sigbit = sigmap(cell->getPort(from_port)[from_bit]);
+ SigBit to_sigbit = sigmap(cell->getPort(to_port)[to_bit]);
+ db[to_sigbit].insert(from_sigbit);
+ }
+};
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/kernel/celltypes.h b/kernel/celltypes.h
new file mode 100644
index 00000000..f0ead1e8
--- /dev/null
+++ b/kernel/celltypes.h
@@ -0,0 +1,449 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef CELLTYPES_H
+#define CELLTYPES_H
+
+#include <kernel/yosys.h>
+
+YOSYS_NAMESPACE_BEGIN
+
+struct CellType
+{
+ RTLIL::IdString type;
+ pool<RTLIL::IdString> inputs, outputs;
+ bool is_evaluable;
+};
+
+struct CellTypes
+{
+ dict<RTLIL::IdString, CellType> cell_types;
+
+ CellTypes()
+ {
+ }
+
+ CellTypes(RTLIL::Design *design)
+ {
+ setup(design);
+ }
+
+ void setup(RTLIL::Design *design = NULL)
+ {
+ if (design)
+ setup_design(design);
+
+ setup_internals();
+ setup_internals_mem();
+ setup_stdcells();
+ setup_stdcells_mem();
+ }
+
+ void setup_type(RTLIL::IdString type, const pool<RTLIL::IdString> &inputs, const pool<RTLIL::IdString> &outputs, bool is_evaluable = false)
+ {
+ CellType ct = {type, inputs, outputs, is_evaluable};
+ cell_types[ct.type] = ct;
+ }
+
+ void setup_module(RTLIL::Module *module)
+ {
+ pool<RTLIL::IdString> inputs, outputs;
+ for (RTLIL::IdString wire_name : module->ports) {
+ RTLIL::Wire *wire = module->wire(wire_name);
+ if (wire->port_input)
+ inputs.insert(wire->name);
+ if (wire->port_output)
+ outputs.insert(wire->name);
+ }
+ setup_type(module->name, inputs, outputs);
+ }
+
+ void setup_design(RTLIL::Design *design)
+ {
+ for (auto module : design->modules())
+ setup_module(module);
+ }
+
+ void setup_internals()
+ {
+ std::vector<RTLIL::IdString> unary_ops = {
+ "$not", "$pos", "$neg",
+ "$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool",
+ "$logic_not", "$slice", "$lut", "$sop"
+ };
+
+ std::vector<RTLIL::IdString> binary_ops = {
+ "$and", "$or", "$xor", "$xnor",
+ "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
+ "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
+ "$add", "$sub", "$mul", "$div", "$mod", "$pow",
+ "$logic_and", "$logic_or", "$concat", "$macc"
+ };
+ IdString A = "\\A", B = "\\B", S = "\\S", Y = "\\Y";
+ IdString P = "\\P", G = "\\G", C = "\\C", X = "\\X";
+ IdString BI = "\\BI", CI = "\\CI", CO = "\\CO", EN = "\\EN";
+
+ for (auto type : unary_ops)
+ setup_type(type, {A}, {Y}, true);
+
+ for (auto type : binary_ops)
+ setup_type(type, {A, B}, {Y}, true);
+
+ for (auto type : std::vector<RTLIL::IdString>({"$mux", "$pmux"}))
+ setup_type(type, {A, B, S}, {Y}, true);
+
+ setup_type("$lcu", {P, G, CI}, {CO}, true);
+ setup_type("$alu", {A, B, CI, BI}, {X, Y, CO}, true);
+ setup_type("$fa", {A, B, C}, {X, Y}, true);
+
+ setup_type("$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("$initstate", pool<RTLIL::IdString>(), {Y}, true);
+ setup_type("$anyconst", pool<RTLIL::IdString>(), {Y}, true);
+ setup_type("$anyseq", pool<RTLIL::IdString>(), {Y}, true);
+ setup_type("$equiv", {A, B}, {Y}, true);
+ }
+
+ void setup_internals_mem()
+ {
+ IdString SET = "\\SET", CLR = "\\CLR", CLK = "\\CLK", ARST = "\\ARST", EN = "\\EN";
+ IdString Q = "\\Q", D = "\\D", ADDR = "\\ADDR", DATA = "\\DATA", RD_EN = "\\RD_EN";
+ IdString RD_CLK = "\\RD_CLK", RD_ADDR = "\\RD_ADDR", WR_CLK = "\\WR_CLK", WR_EN = "\\WR_EN";
+ IdString WR_ADDR = "\\WR_ADDR", WR_DATA = "\\WR_DATA", RD_DATA = "\\RD_DATA";
+ IdString CTRL_IN = "\\CTRL_IN", CTRL_OUT = "\\CTRL_OUT";
+
+ setup_type("$sr", {SET, CLR}, {Q});
+ setup_type("$ff", {D}, {Q});
+ setup_type("$dff", {CLK, D}, {Q});
+ setup_type("$dffe", {CLK, EN, D}, {Q});
+ setup_type("$dffsr", {CLK, SET, CLR, D}, {Q});
+ setup_type("$adff", {CLK, ARST, D}, {Q});
+ setup_type("$dlatch", {EN, D}, {Q});
+ setup_type("$dlatchsr", {EN, SET, CLR, D}, {Q});
+
+ setup_type("$memrd", {CLK, EN, ADDR}, {DATA});
+ setup_type("$memwr", {CLK, EN, ADDR, DATA}, pool<RTLIL::IdString>());
+ setup_type("$meminit", {ADDR, DATA}, pool<RTLIL::IdString>());
+ setup_type("$mem", {RD_CLK, RD_EN, RD_ADDR, WR_CLK, WR_EN, WR_ADDR, WR_DATA}, {RD_DATA});
+
+ setup_type("$fsm", {CLK, ARST, CTRL_IN}, {CTRL_OUT});
+ }
+
+ void setup_stdcells()
+ {
+ 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 S = "\\S", T = "\\T", U = "\\U", V = "\\V";
+ IdString Y = "\\Y";
+
+ setup_type("$_BUF_", {A}, {Y}, true);
+ setup_type("$_NOT_", {A}, {Y}, true);
+ setup_type("$_AND_", {A, B}, {Y}, true);
+ setup_type("$_NAND_", {A, B}, {Y}, true);
+ setup_type("$_OR_", {A, B}, {Y}, true);
+ setup_type("$_NOR_", {A, B}, {Y}, true);
+ setup_type("$_XOR_", {A, B}, {Y}, true);
+ setup_type("$_XNOR_", {A, B}, {Y}, true);
+ setup_type("$_MUX_", {A, B, S}, {Y}, true);
+ setup_type("$_MUX4_", {A, B, C, D, S, T}, {Y}, true);
+ setup_type("$_MUX8_", {A, B, C, D, E, F, G, H, S, T, U}, {Y}, true);
+ setup_type("$_MUX16_", {A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V}, {Y}, true);
+ setup_type("$_AOI3_", {A, B, C}, {Y}, true);
+ setup_type("$_OAI3_", {A, B, C}, {Y}, true);
+ setup_type("$_AOI4_", {A, B, C, D}, {Y}, true);
+ setup_type("$_OAI4_", {A, B, C, D}, {Y}, true);
+ setup_type("$_TBUF_", {A, E}, {Y}, true);
+ }
+
+ void setup_stdcells_mem()
+ {
+ IdString S = "\\S", R = "\\R", C = "\\C";
+ IdString D = "\\D", Q = "\\Q", E = "\\E";
+
+ std::vector<char> list_np = {'N', 'P'}, list_01 = {'0', '1'};
+
+ for (auto c1 : list_np)
+ for (auto c2 : list_np)
+ setup_type(stringf("$_SR_%c%c_", c1, c2), {S, R}, {Q});
+
+ setup_type("$_FF_", {D}, {Q});
+
+ for (auto c1 : list_np)
+ setup_type(stringf("$_DFF_%c_", c1), {C, D}, {Q});
+
+ for (auto c1 : list_np)
+ for (auto c2 : list_np)
+ setup_type(stringf("$_DFFE_%c%c_", c1, c2), {C, D, E}, {Q});
+
+ for (auto c1 : list_np)
+ for (auto c2 : list_np)
+ for (auto c3 : list_01)
+ setup_type(stringf("$_DFF_%c%c%c_", c1, c2, c3), {C, R, D}, {Q});
+
+ for (auto c1 : list_np)
+ for (auto c2 : list_np)
+ for (auto c3 : list_np)
+ setup_type(stringf("$_DFFSR_%c%c%c_", c1, c2, c3), {C, S, R, D}, {Q});
+
+ for (auto c1 : list_np)
+ setup_type(stringf("$_DLATCH_%c_", c1), {E, D}, {Q});
+
+ for (auto c1 : list_np)
+ for (auto c2 : list_np)
+ for (auto c3 : list_np)
+ setup_type(stringf("$_DLATCHSR_%c%c%c_", c1, c2, c3), {E, S, R, D}, {Q});
+ }
+
+ void clear()
+ {
+ cell_types.clear();
+ }
+
+ bool cell_known(RTLIL::IdString type)
+ {
+ return cell_types.count(type) != 0;
+ }
+
+ bool cell_output(RTLIL::IdString type, RTLIL::IdString port)
+ {
+ auto it = cell_types.find(type);
+ return it != cell_types.end() && it->second.outputs.count(port) != 0;
+ }
+
+ bool cell_input(RTLIL::IdString type, RTLIL::IdString port)
+ {
+ auto it = cell_types.find(type);
+ return it != cell_types.end() && it->second.inputs.count(port) != 0;
+ }
+
+ bool cell_evaluable(RTLIL::IdString type)
+ {
+ auto it = cell_types.find(type);
+ return it != cell_types.end() && it->second.is_evaluable;
+ }
+
+ static RTLIL::Const eval_not(RTLIL::Const v)
+ {
+ for (auto &bit : v.bits)
+ if (bit == RTLIL::S0) bit = RTLIL::S1;
+ else if (bit == RTLIL::S1) bit = RTLIL::S0;
+ return v;
+ }
+
+ static RTLIL::Const eval(RTLIL::IdString type, const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+ {
+ if (type == "$sshr" && !signed1)
+ type = "$shr";
+ if (type == "$sshl" && !signed1)
+ type = "$shl";
+
+ if (type != "$sshr" && type != "$sshl" && type != "$shr" && type != "$shl" && type != "$shift" && type != "$shiftx" &&
+ type != "$pos" && type != "$neg" && type != "$not") {
+ if (!signed1 || !signed2)
+ signed1 = false, signed2 = false;
+ }
+
+#define HANDLE_CELL_TYPE(_t) if (type == "$" #_t) return const_ ## _t(arg1, arg2, signed1, signed2, result_len);
+ HANDLE_CELL_TYPE(not)
+ HANDLE_CELL_TYPE(and)
+ HANDLE_CELL_TYPE(or)
+ HANDLE_CELL_TYPE(xor)
+ HANDLE_CELL_TYPE(xnor)
+ HANDLE_CELL_TYPE(reduce_and)
+ HANDLE_CELL_TYPE(reduce_or)
+ HANDLE_CELL_TYPE(reduce_xor)
+ HANDLE_CELL_TYPE(reduce_xnor)
+ HANDLE_CELL_TYPE(reduce_bool)
+ HANDLE_CELL_TYPE(logic_not)
+ HANDLE_CELL_TYPE(logic_and)
+ HANDLE_CELL_TYPE(logic_or)
+ HANDLE_CELL_TYPE(shl)
+ HANDLE_CELL_TYPE(shr)
+ HANDLE_CELL_TYPE(sshl)
+ HANDLE_CELL_TYPE(sshr)
+ HANDLE_CELL_TYPE(shift)
+ HANDLE_CELL_TYPE(shiftx)
+ HANDLE_CELL_TYPE(lt)
+ HANDLE_CELL_TYPE(le)
+ HANDLE_CELL_TYPE(eq)
+ HANDLE_CELL_TYPE(ne)
+ HANDLE_CELL_TYPE(eqx)
+ HANDLE_CELL_TYPE(nex)
+ HANDLE_CELL_TYPE(ge)
+ HANDLE_CELL_TYPE(gt)
+ HANDLE_CELL_TYPE(add)
+ HANDLE_CELL_TYPE(sub)
+ HANDLE_CELL_TYPE(mul)
+ HANDLE_CELL_TYPE(div)
+ HANDLE_CELL_TYPE(mod)
+ HANDLE_CELL_TYPE(pow)
+ HANDLE_CELL_TYPE(pos)
+ HANDLE_CELL_TYPE(neg)
+#undef HANDLE_CELL_TYPE
+
+ if (type == "$_BUF_")
+ return arg1;
+ if (type == "$_NOT_")
+ return eval_not(arg1);
+ if (type == "$_AND_")
+ return const_and(arg1, arg2, false, false, 1);
+ if (type == "$_NAND_")
+ return eval_not(const_and(arg1, arg2, false, false, 1));
+ if (type == "$_OR_")
+ return const_or(arg1, arg2, false, false, 1);
+ if (type == "$_NOR_")
+ return eval_not(const_and(arg1, arg2, false, false, 1));
+ if (type == "$_XOR_")
+ return const_xor(arg1, arg2, false, false, 1);
+ if (type == "$_XNOR_")
+ return const_xnor(arg1, arg2, false, false, 1);
+
+ log_abort();
+ }
+
+ static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2)
+ {
+ if (cell->type == "$slice") {
+ RTLIL::Const ret;
+ int width = cell->parameters.at("\\Y_WIDTH").as_int();
+ int offset = cell->parameters.at("\\OFFSET").as_int();
+ ret.bits.insert(ret.bits.end(), arg1.bits.begin()+offset, arg1.bits.begin()+offset+width);
+ return ret;
+ }
+
+ if (cell->type == "$concat") {
+ RTLIL::Const ret = arg1;
+ ret.bits.insert(ret.bits.end(), arg2.bits.begin(), arg2.bits.end());
+ return ret;
+ }
+
+ if (cell->type == "$lut")
+ {
+ int width = cell->parameters.at("\\WIDTH").as_int();
+
+ std::vector<RTLIL::State> t = cell->parameters.at("\\LUT").bits;
+ while (GetSize(t) < (1 << width))
+ t.push_back(RTLIL::S0);
+ t.resize(1 << width);
+
+ for (int i = width-1; i >= 0; i--) {
+ RTLIL::State sel = arg1.bits.at(i);
+ std::vector<RTLIL::State> new_t;
+ if (sel == RTLIL::S0)
+ new_t = std::vector<RTLIL::State>(t.begin(), t.begin() + GetSize(t)/2);
+ else if (sel == RTLIL::S1)
+ new_t = std::vector<RTLIL::State>(t.begin() + GetSize(t)/2, t.end());
+ else
+ for (int j = 0; j < GetSize(t)/2; j++)
+ new_t.push_back(t[j] == t[j + GetSize(t)/2] ? t[j] : RTLIL::Sx);
+ t.swap(new_t);
+ }
+
+ log_assert(GetSize(t) == 1);
+ return t;
+ }
+
+ if (cell->type == "$sop")
+ {
+ int width = cell->parameters.at("\\WIDTH").as_int();
+ int depth = cell->parameters.at("\\DEPTH").as_int();
+ std::vector<RTLIL::State> t = cell->parameters.at("\\TABLE").bits;
+
+ while (GetSize(t) < width*depth*2)
+ t.push_back(RTLIL::S0);
+
+ RTLIL::State default_ret = State::S0;
+
+ for (int i = 0; i < depth; i++)
+ {
+ bool match = true;
+ bool match_x = true;
+
+ for (int j = 0; j < width; j++) {
+ RTLIL::State a = arg1.bits.at(j);
+ if (t.at(2*width*i + 2*j + 0) == State::S1) {
+ if (a == State::S1) match_x = false;
+ if (a != State::S0) match = false;
+ }
+ if (t.at(2*width*i + 2*j + 1) == State::S1) {
+ if (a == State::S0) match_x = false;
+ if (a != State::S1) match = false;
+ }
+ }
+
+ if (match)
+ return State::S1;
+
+ if (match_x)
+ default_ret = State::Sx;
+ }
+
+ return default_ret;
+ }
+
+ 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);
+ }
+
+ static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3)
+ {
+ if (cell->type.in("$mux", "$pmux", "$_MUX_")) {
+ RTLIL::Const ret = arg1;
+ for (size_t i = 0; i < arg3.bits.size(); i++)
+ if (arg3.bits[i] == RTLIL::State::S1) {
+ std::vector<RTLIL::State> bits(arg2.bits.begin() + i*arg1.bits.size(), arg2.bits.begin() + (i+1)*arg1.bits.size());
+ ret = RTLIL::Const(bits);
+ }
+ return ret;
+ }
+
+ if (cell->type == "$_AOI3_")
+ return eval_not(const_or(const_and(arg1, arg2, false, false, 1), arg3, false, false, 1));
+ if (cell->type == "$_OAI3_")
+ 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);
+ }
+
+ static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3, const RTLIL::Const &arg4)
+ {
+ if (cell->type == "$_AOI4_")
+ return eval_not(const_or(const_and(arg1, arg2, false, false, 1), const_and(arg3, arg4, false, false, 1), false, false, 1));
+ if (cell->type == "$_OAI4_")
+ return eval_not(const_and(const_or(arg1, arg2, false, false, 1), const_and(arg3, arg4, false, false, 1), false, false, 1));
+
+ log_assert(arg4.bits.size() == 0);
+ return eval(cell, arg1, arg2, arg3);
+ }
+};
+
+// initialized by yosys_setup()
+extern CellTypes yosys_celltypes;
+
+YOSYS_NAMESPACE_END
+
+#endif
+
diff --git a/kernel/consteval.h b/kernel/consteval.h
new file mode 100644
index 00000000..4d48b45e
--- /dev/null
+++ b/kernel/consteval.h
@@ -0,0 +1,383 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef CONSTEVAL_H
+#define CONSTEVAL_H
+
+#include "kernel/rtlil.h"
+#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
+#include "kernel/macc.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+struct ConstEval
+{
+ RTLIL::Module *module;
+ SigMap assign_map;
+ SigMap values_map;
+ SigPool stop_signals;
+ SigSet<RTLIL::Cell*> sig2driver;
+ std::set<RTLIL::Cell*> busy;
+ std::vector<SigMap> stack;
+
+ ConstEval(RTLIL::Module *module) : module(module), assign_map(module)
+ {
+ CellTypes ct;
+ ct.setup_internals();
+ ct.setup_stdcells();
+
+ for (auto &it : module->cells_) {
+ if (!ct.cell_known(it.second->type))
+ continue;
+ for (auto &it2 : it.second->connections())
+ if (ct.cell_output(it.second->type, it2.first))
+ sig2driver.insert(assign_map(it2.second), it.second);
+ }
+ }
+
+ void clear()
+ {
+ values_map.clear();
+ stop_signals.clear();
+ }
+
+ void push()
+ {
+ stack.push_back(values_map);
+ }
+
+ void pop()
+ {
+ values_map.swap(stack.back());
+ stack.pop_back();
+ }
+
+ void set(RTLIL::SigSpec sig, RTLIL::Const value)
+ {
+ assign_map.apply(sig);
+#ifndef NDEBUG
+ RTLIL::SigSpec current_val = values_map(sig);
+ for (int i = 0; i < GetSize(current_val); i++)
+ log_assert(current_val[i].wire != NULL || current_val[i] == value.bits[i]);
+#endif
+ values_map.add(sig, RTLIL::SigSpec(value));
+ }
+
+ void stop(RTLIL::SigSpec sig)
+ {
+ assign_map.apply(sig);
+ stop_signals.add(sig);
+ }
+
+ bool eval(RTLIL::Cell *cell, RTLIL::SigSpec &undef)
+ {
+ if (cell->type == "$lcu")
+ {
+ RTLIL::SigSpec sig_p = cell->getPort("\\P");
+ RTLIL::SigSpec sig_g = cell->getPort("\\G");
+ RTLIL::SigSpec sig_ci = cell->getPort("\\CI");
+ RTLIL::SigSpec sig_co = values_map(assign_map(cell->getPort("\\CO")));
+
+ if (sig_co.is_fully_const())
+ return true;
+
+ if (!eval(sig_p, undef, cell))
+ return false;
+
+ if (!eval(sig_g, undef, cell))
+ return false;
+
+ if (!eval(sig_ci, undef, cell))
+ return false;
+
+ if (sig_p.is_fully_def() && sig_g.is_fully_def() && sig_ci.is_fully_def())
+ {
+ RTLIL::Const coval(RTLIL::Sx, GetSize(sig_co));
+ bool carry = sig_ci.as_bool();
+
+ for (int i = 0; i < GetSize(coval); i++) {
+ carry = (sig_g[i] == RTLIL::S1) || (sig_p[i] == RTLIL::S1 && carry);
+ coval.bits[i] = carry ? RTLIL::S1 : RTLIL::S0;
+ }
+
+ set(sig_co, coval);
+ }
+ else
+ set(sig_co, RTLIL::Const(RTLIL::Sx, GetSize(sig_co)));
+
+ return true;
+ }
+
+ RTLIL::SigSpec sig_a, sig_b, sig_s, sig_y;
+
+ log_assert(cell->hasPort("\\Y"));
+ sig_y = values_map(assign_map(cell->getPort("\\Y")));
+ if (sig_y.is_fully_const())
+ return true;
+
+ if (cell->hasPort("\\S")) {
+ sig_s = cell->getPort("\\S");
+ if (!eval(sig_s, undef, cell))
+ return false;
+ }
+
+ if (cell->hasPort("\\A"))
+ sig_a = cell->getPort("\\A");
+
+ if (cell->hasPort("\\B"))
+ sig_b = cell->getPort("\\B");
+
+ if (cell->type == "$mux" || cell->type == "$pmux" || cell->type == "$_MUX_")
+ {
+ std::vector<RTLIL::SigSpec> y_candidates;
+ int count_maybe_set_s_bits = 0;
+ int count_set_s_bits = 0;
+
+ for (int i = 0; i < sig_s.size(); i++)
+ {
+ RTLIL::State s_bit = sig_s.extract(i, 1).as_const().bits.at(0);
+ RTLIL::SigSpec b_slice = sig_b.extract(sig_y.size()*i, sig_y.size());
+
+ if (s_bit == RTLIL::State::Sx || s_bit == RTLIL::State::S1)
+ y_candidates.push_back(b_slice);
+
+ if (s_bit == RTLIL::State::S1 || s_bit == RTLIL::State::Sx)
+ count_maybe_set_s_bits++;
+
+ if (s_bit == RTLIL::State::S1)
+ count_set_s_bits++;
+ }
+
+ if (count_set_s_bits == 0)
+ y_candidates.push_back(sig_a);
+
+ std::vector<RTLIL::Const> y_values;
+
+ log_assert(y_candidates.size() > 0);
+ for (auto &yc : y_candidates) {
+ if (!eval(yc, undef, cell))
+ return false;
+ y_values.push_back(yc.as_const());
+ }
+
+ if (y_values.size() > 1)
+ {
+ std::vector<RTLIL::State> master_bits = y_values.at(0).bits;
+
+ for (size_t i = 1; i < y_values.size(); i++) {
+ std::vector<RTLIL::State> &slave_bits = y_values.at(i).bits;
+ log_assert(master_bits.size() == slave_bits.size());
+ for (size_t j = 0; j < master_bits.size(); j++)
+ if (master_bits[j] != slave_bits[j])
+ master_bits[j] = RTLIL::State::Sx;
+ }
+
+ set(sig_y, RTLIL::Const(master_bits));
+ }
+ else
+ set(sig_y, y_values.front());
+ }
+ else if (cell->type == "$fa")
+ {
+ RTLIL::SigSpec sig_c = cell->getPort("\\C");
+ RTLIL::SigSpec sig_x = cell->getPort("\\X");
+ int width = GetSize(sig_c);
+
+ if (!eval(sig_a, undef, cell))
+ return false;
+
+ if (!eval(sig_b, undef, cell))
+ return false;
+
+ if (!eval(sig_c, undef, cell))
+ return false;
+
+ RTLIL::Const t1 = const_xor(sig_a.as_const(), sig_b.as_const(), false, false, width);
+ RTLIL::Const val_y = const_xor(t1, sig_c.as_const(), false, false, width);
+
+ RTLIL::Const t2 = const_and(sig_a.as_const(), sig_b.as_const(), false, false, width);
+ RTLIL::Const t3 = const_and(sig_c.as_const(), t1, false, false, width);
+ RTLIL::Const val_x = const_or(t2, t3, false, false, width);
+
+ for (int i = 0; i < GetSize(val_y); i++)
+ if (val_y.bits[i] == RTLIL::Sx)
+ val_x.bits[i] = RTLIL::Sx;
+
+ set(sig_y, val_y);
+ set(sig_x, val_x);
+ }
+ else if (cell->type == "$alu")
+ {
+ 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();
+
+ RTLIL::SigSpec sig_ci = cell->getPort("\\CI");
+ RTLIL::SigSpec sig_bi = cell->getPort("\\BI");
+
+ if (!eval(sig_a, undef, cell))
+ return false;
+
+ if (!eval(sig_b, undef, cell))
+ return false;
+
+ if (!eval(sig_ci, undef, cell))
+ return false;
+
+ if (!eval(sig_bi, undef, cell))
+ return false;
+
+ RTLIL::SigSpec sig_x = cell->getPort("\\X");
+ RTLIL::SigSpec sig_co = cell->getPort("\\CO");
+
+ bool any_input_undef = !(sig_a.is_fully_def() && sig_b.is_fully_def() && sig_ci.is_fully_def() && sig_bi.is_fully_def());
+ sig_a.extend_u0(GetSize(sig_y), signed_a);
+ sig_b.extend_u0(GetSize(sig_y), signed_b);
+
+ bool carry = sig_ci[0] == RTLIL::S1;
+ bool b_inv = sig_bi[0] == RTLIL::S1;
+
+ for (int i = 0; i < GetSize(sig_y); i++)
+ {
+ RTLIL::SigSpec x_inputs = { sig_a[i], sig_b[i], sig_bi[0] };
+
+ if (!x_inputs.is_fully_def()) {
+ set(sig_x[i], RTLIL::Sx);
+ } else {
+ bool bit_a = sig_a[i] == RTLIL::S1;
+ bool bit_b = (sig_b[i] == RTLIL::S1) != b_inv;
+ bool bit_x = bit_a != bit_b;
+ set(sig_x[i], bit_x ? RTLIL::S1 : RTLIL::S0);
+ }
+
+ if (any_input_undef) {
+ set(sig_y[i], RTLIL::Sx);
+ set(sig_co[i], RTLIL::Sx);
+ } else {
+ bool bit_a = sig_a[i] == RTLIL::S1;
+ bool bit_b = (sig_b[i] == RTLIL::S1) != b_inv;
+ bool bit_y = (bit_a != bit_b) != carry;
+ carry = (bit_a && bit_b) || (bit_a && carry) || (bit_b && carry);
+ set(sig_y[i], bit_y ? RTLIL::S1 : RTLIL::S0);
+ set(sig_co[i], carry ? RTLIL::S1 : RTLIL::S0);
+ }
+ }
+ }
+ else if (cell->type == "$macc")
+ {
+ Macc macc;
+ macc.from_cell(cell);
+
+ if (!eval(macc.bit_ports, undef, cell))
+ return false;
+
+ for (auto &port : macc.ports) {
+ if (!eval(port.in_a, undef, cell))
+ return false;
+ if (!eval(port.in_b, undef, cell))
+ return false;
+ }
+
+ RTLIL::Const result(0, GetSize(cell->getPort("\\Y")));
+ if (!macc.eval(result))
+ log_abort();
+
+ set(cell->getPort("\\Y"), result);
+ }
+ else
+ {
+ RTLIL::SigSpec sig_c, sig_d;
+
+ if (cell->type.in("$_AOI3_", "$_OAI3_", "$_AOI4_", "$_OAI4_")) {
+ if (cell->hasPort("\\C"))
+ sig_c = cell->getPort("\\C");
+ if (cell->hasPort("\\D"))
+ sig_d = cell->getPort("\\D");
+ }
+
+ if (sig_a.size() > 0 && !eval(sig_a, undef, cell))
+ return false;
+ if (sig_b.size() > 0 && !eval(sig_b, undef, cell))
+ return false;
+ if (sig_c.size() > 0 && !eval(sig_c, undef, cell))
+ return false;
+ 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()));
+ }
+
+ return true;
+ }
+
+ bool eval(RTLIL::SigSpec &sig, RTLIL::SigSpec &undef, RTLIL::Cell *busy_cell = NULL)
+ {
+ assign_map.apply(sig);
+ values_map.apply(sig);
+
+ if (sig.is_fully_const())
+ return true;
+
+ if (stop_signals.check_any(sig)) {
+ undef = stop_signals.extract(sig);
+ return false;
+ }
+
+ if (busy_cell) {
+ if (busy.count(busy_cell) > 0) {
+ undef = sig;
+ return false;
+ }
+ busy.insert(busy_cell);
+ }
+
+ std::set<RTLIL::Cell*> driver_cells;
+ sig2driver.find(sig, driver_cells);
+ for (auto cell : driver_cells) {
+ if (!eval(cell, undef)) {
+ if (busy_cell)
+ busy.erase(busy_cell);
+ return false;
+ }
+ }
+
+ if (busy_cell)
+ busy.erase(busy_cell);
+
+ values_map.apply(sig);
+ if (sig.is_fully_const())
+ return true;
+
+ for (auto &c : sig.chunks())
+ if (c.wire != NULL)
+ undef.append(c);
+ return false;
+ }
+
+ bool eval(RTLIL::SigSpec &sig)
+ {
+ RTLIL::SigSpec undef;
+ return eval(sig, undef);
+ }
+};
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/kernel/cost.h b/kernel/cost.h
new file mode 100644
index 00000000..4f12889f
--- /dev/null
+++ b/kernel/cost.h
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef COST_H
+#define COST_H
+
+#include <kernel/yosys.h>
+
+YOSYS_NAMESPACE_BEGIN
+
+int get_cell_cost(RTLIL::Cell *cell, dict<RTLIL::IdString, int> *mod_cost_cache = nullptr);
+
+int get_cell_cost(RTLIL::IdString type, const dict<RTLIL::IdString, RTLIL::Const> &parameters = dict<RTLIL::IdString, RTLIL::Const>(),
+ RTLIL::Design *design = nullptr, dict<RTLIL::IdString, int> *mod_cost_cache = nullptr)
+{
+ static dict<RTLIL::IdString, int> gate_cost = {
+ { "$_BUF_", 1 },
+ { "$_NOT_", 2 },
+ { "$_AND_", 4 },
+ { "$_NAND_", 4 },
+ { "$_OR_", 4 },
+ { "$_NOR_", 4 },
+ { "$_XOR_", 8 },
+ { "$_XNOR_", 8 },
+ { "$_AOI3_", 6 },
+ { "$_OAI3_", 6 },
+ { "$_AOI4_", 8 },
+ { "$_OAI4_", 8 },
+ { "$_MUX_", 4 }
+ };
+
+ if (gate_cost.count(type))
+ return gate_cost.at(type);
+
+ if (parameters.empty() && design && design->module(type))
+ {
+ RTLIL::Module *mod = design->module(type);
+
+ if (mod->attributes.count("\\cost"))
+ return mod->attributes.at("\\cost").as_int();
+
+ dict<RTLIL::IdString, int> local_mod_cost_cache;
+ if (mod_cost_cache == nullptr)
+ mod_cost_cache = &local_mod_cost_cache;
+
+ if (mod_cost_cache->count(mod->name))
+ return mod_cost_cache->at(mod->name);
+
+ int module_cost = 1;
+ for (auto c : mod->cells())
+ module_cost += get_cell_cost(c, mod_cost_cache);
+
+ (*mod_cost_cache)[mod->name] = module_cost;
+ return module_cost;
+ }
+
+ log_warning("Can't determine cost of %s cell (%d parameters).\n", log_id(type), GetSize(parameters));
+ return 1;
+}
+
+int get_cell_cost(RTLIL::Cell *cell, dict<RTLIL::IdString, int> *mod_cost_cache)
+{
+ return get_cell_cost(cell->type, cell->parameters, cell->module->design, mod_cost_cache);
+}
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/kernel/driver.cc b/kernel/driver.cc
new file mode 100644
index 00000000..f8d00c38
--- /dev/null
+++ b/kernel/driver.cc
@@ -0,0 +1,525 @@
+/*
+ * 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 "libs/sha1/sha1.h"
+
+#ifdef YOSYS_ENABLE_READLINE
+# include <readline/readline.h>
+# include <readline/history.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+
+#ifdef __linux__
+# include <sys/types.h>
+# include <unistd.h>
+#endif
+
+#if !defined(_WIN32) || defined(__MINGW32__)
+# include <unistd.h>
+#else
+char *optarg;
+int optind = 1, optcur = 1;
+int getopt(int argc, char **argv, const char *optstring)
+{
+ if (optind >= argc || argv[optind][0] != '-')
+ return -1;
+
+ bool takes_arg = false;
+ int opt = argv[optind][optcur];
+ for (int i = 0; optstring[i]; i++)
+ if (opt == optstring[i] && optstring[i + 1] == ':')
+ takes_arg = true;
+
+ if (!takes_arg) {
+ if (argv[optind][++optcur] == 0)
+ optind++, optcur = 1;
+ return opt;
+ }
+
+ if (argv[optind][++optcur]) {
+ optarg = argv[optind++] + optcur;
+ optcur = 1;
+ return opt;
+ }
+
+ optarg = argv[++optind];
+ optind++, optcur = 1;
+ return opt;
+}
+#endif
+
+
+USING_YOSYS_NAMESPACE
+
+#ifdef EMSCRIPTEN
+# include <sys/stat.h>
+# include <sys/types.h>
+
+extern "C" int main(int, char**);
+extern "C" void run(const char*);
+extern "C" const char *errmsg();
+extern "C" const char *prompt();
+
+int main(int, char**)
+{
+ mkdir("/work", 0777);
+ chdir("/work");
+ log_files.push_back(stdout);
+ log_error_stderr = true;
+ yosys_banner();
+ yosys_setup();
+}
+
+void run(const char *command)
+{
+ int selSize = GetSize(yosys_get_design()->selection_stack);
+ try {
+ log_last_error = "Internal error (see JavaScript console for details)";
+ run_pass(command);
+ log_last_error = "";
+ } catch (...) {
+ while (GetSize(yosys_get_design()->selection_stack) > selSize)
+ yosys_get_design()->selection_stack.pop_back();
+ throw;
+ }
+}
+
+const char *errmsg()
+{
+ return log_last_error.c_str();
+}
+
+const char *prompt()
+{
+ const char *p = create_prompt(yosys_get_design(), 0);
+ while (*p == '\n') p++;
+ return p;
+}
+
+#else /* EMSCRIPTEN */
+
+int main(int argc, char **argv)
+{
+ std::string frontend_command = "auto";
+ std::string backend_command = "auto";
+ std::vector<std::string> passes_commands;
+ std::vector<std::string> plugin_filenames;
+ std::string output_filename = "";
+ std::string scriptfile = "";
+ bool scriptfile_tcl = false;
+ bool got_output_filename = false;
+ bool print_banner = true;
+ bool print_stats = true;
+ bool call_abort = false;
+ bool timing_details = false;
+ bool mode_v = false;
+ bool mode_q = false;
+
+#ifdef YOSYS_ENABLE_READLINE
+ int history_offset = 0;
+ std::string history_file;
+ if (getenv("HOME") != NULL) {
+ history_file = stringf("%s/.yosys_history", getenv("HOME"));
+ read_history(history_file.c_str());
+ history_offset = where_history();
+ }
+#endif
+
+ if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-help") || !strcmp(argv[1], "--help")))
+ {
+ printf("\n");
+ printf("Usage: %s [options] [<infile> [..]]\n", argv[0]);
+ printf("\n");
+ printf(" -Q\n");
+ printf(" suppress printing of banner (copyright, disclaimer, version)\n");
+ printf("\n");
+ printf(" -T\n");
+ printf(" suppress printing of footer (log hash, version, timing statistics)\n");
+ printf("\n");
+ printf(" -q\n");
+ printf(" quiet operation. only write warnings and error messages to console\n");
+ printf(" use this option twice to also quiet warning messages\n");
+ printf("\n");
+ printf(" -v <level>\n");
+ printf(" print log headers up to level <level> to the console. (this\n");
+ printf(" implies -q for everything except the 'End of script.' message.)\n");
+ printf("\n");
+ printf(" -t\n");
+ printf(" annotate all log messages with a time stamp\n");
+ printf("\n");
+ printf(" -d\n");
+ printf(" print more detailed timing stats at exit\n");
+ printf("\n");
+ printf(" -l logfile\n");
+ printf(" write log messages to the specified file\n");
+ printf("\n");
+ printf(" -L logfile\n");
+ printf(" like -l but open log file in line buffered mode\n");
+ printf("\n");
+ printf(" -o outfile\n");
+ printf(" write the design to the specified file on exit\n");
+ printf("\n");
+ printf(" -b backend\n");
+ printf(" use this backend for the output file specified on the command line\n");
+ printf("\n");
+ printf(" -f frontend\n");
+ printf(" use the specified frontend for the input files on the command line\n");
+ printf("\n");
+ printf(" -H\n");
+ printf(" print the command list\n");
+ printf("\n");
+ printf(" -h command\n");
+ printf(" print the help message for the specified command\n");
+ printf("\n");
+ printf(" -s scriptfile\n");
+ printf(" execute the commands in the script file\n");
+ printf("\n");
+ printf(" -c tcl_scriptfile\n");
+ printf(" execute the commands in the tcl script file (see 'help tcl' for details)\n");
+ printf("\n");
+ printf(" -p command\n");
+ printf(" execute the commands\n");
+ printf("\n");
+ printf(" -m module_file\n");
+ printf(" load the specified module (aka plugin)\n");
+ printf("\n");
+ printf(" -X\n");
+ printf(" enable tracing of core data structure changes. for debugging\n");
+ printf("\n");
+ printf(" -M\n");
+ printf(" will slightly randomize allocated pointer addresses. for debugging\n");
+ printf("\n");
+ printf(" -A\n");
+ printf(" will call abort() at the end of the script. for debugging\n");
+ printf("\n");
+ printf(" -D <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");
+ printf("\n");
+ printf(" -V\n");
+ printf(" print version information and exit\n");
+ printf("\n");
+ printf("The option -S is an shortcut for calling the \"synth\" command, a default\n");
+ printf("script for transforming the Verilog input to a gate-level netlist. For example:\n");
+ printf("\n");
+ printf(" yosys -o output.blif -S input.v\n");
+ printf("\n");
+ printf("For more complex synthesis jobs it is recommended to use the read_* and write_*\n");
+ printf("commands in a script file instead of specifying input and output files on the\n");
+ printf("command line.\n");
+ printf("\n");
+ printf("When no commands, script files or input files are specified on the command\n");
+ printf("line, yosys automatically enters the interactive command mode. Use the 'help'\n");
+ printf("command to get information on the individual commands.\n");
+ printf("\n");
+ exit(0);
+ }
+
+ int opt;
+ while ((opt = getopt(argc, argv, "MXAQTVSm:f:Hh:b:o:p:l:L:qv:tds:c:D:")) != -1)
+ {
+ switch (opt)
+ {
+ case 'M':
+ memhasher_on();
+ break;
+ case 'X':
+ yosys_xtrace++;
+ break;
+ case 'A':
+ call_abort = true;
+ break;
+ case 'Q':
+ print_banner = false;
+ break;
+ case 'T':
+ print_stats = false;
+ break;
+ case 'V':
+ printf("%s\n", yosys_version_str);
+ exit(0);
+ case 'S':
+ passes_commands.push_back("synth");
+ break;
+ case 'm':
+ plugin_filenames.push_back(optarg);
+ break;
+ case 'f':
+ frontend_command = optarg;
+ break;
+ case 'H':
+ passes_commands.push_back("help");
+ break;
+ case 'h':
+ passes_commands.push_back(stringf("help %s", optarg));
+ break;
+ case 'b':
+ backend_command = optarg;
+ break;
+ case 'p':
+ passes_commands.push_back(optarg);
+ break;
+ case 'o':
+ output_filename = optarg;
+ got_output_filename = true;
+ break;
+ case 'l':
+ case 'L':
+ log_files.push_back(fopen(optarg, "wt"));
+ if (log_files.back() == NULL) {
+ fprintf(stderr, "Can't open log file `%s' for writing!\n", optarg);
+ exit(1);
+ }
+ if (opt == 'L')
+ setvbuf(log_files.back(), NULL, _IOLBF, 0);
+ break;
+ case 'q':
+ mode_q = true;
+ if (log_errfile == stderr)
+ log_quiet_warnings = true;
+ log_errfile = stderr;
+ break;
+ case 'v':
+ mode_v = true;
+ log_errfile = stderr;
+ log_verbose_level = atoi(optarg);
+ break;
+ case 't':
+ log_time = true;
+ break;
+ case 'd':
+ timing_details = true;
+ break;
+ case 's':
+ scriptfile = optarg;
+ scriptfile_tcl = false;
+ break;
+ case 'c':
+ scriptfile = optarg;
+ scriptfile_tcl = true;
+ break;
+ case 'D':
+ {
+ auto args = split_tokens(optarg, ":");
+ if (!args.empty() && args[0] == "ALL") {
+ if (GetSize(args) != 1) {
+ fprintf(stderr, "Invalid number of tokens in -D ALL.\n");
+ exit(1);
+ }
+ log_hdump_all = true;
+ } else {
+ if (!args.empty() && !args[0].empty() && args[0].back() == '.')
+ args[0].pop_back();
+ if (GetSize(args) == 1)
+ args.push_back("yosys_dump_" + args[0] + ".il");
+ if (GetSize(args) != 2) {
+ fprintf(stderr, "Invalid number of tokens in -D.\n");
+ exit(1);
+ }
+ log_hdump[args[0]].insert(args[1]);
+ }
+ }
+ break;
+ default:
+ fprintf(stderr, "Run '%s -h' for help.\n", argv[0]);
+ exit(1);
+ }
+ }
+
+ if (log_errfile == NULL) {
+ log_files.push_back(stdout);
+ log_error_stderr = true;
+ }
+
+ if (print_banner)
+ yosys_banner();
+
+ if (print_stats)
+ log_hasher = new SHA1;
+
+ yosys_setup();
+
+ for (auto &fn : plugin_filenames)
+ load_plugin(fn, {});
+
+ if (optind == argc && passes_commands.size() == 0 && scriptfile.empty()) {
+ if (!got_output_filename)
+ backend_command = "";
+ shell(yosys_design);
+ }
+
+ while (optind < argc)
+ run_frontend(argv[optind++], frontend_command, output_filename == "-" ? &backend_command : NULL);
+
+ if (!scriptfile.empty()) {
+ if (scriptfile_tcl) {
+#ifdef YOSYS_ENABLE_TCL
+ if (Tcl_EvalFile(yosys_get_tcl_interp(), scriptfile.c_str()) != TCL_OK)
+ log_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(yosys_get_tcl_interp()));
+#else
+ log_error("Can't exectue TCL script: this version of yosys is not built with TCL support enabled.\n");
+#endif
+ } else
+ run_frontend(scriptfile, "script", output_filename == "-" ? &backend_command : NULL);
+ }
+
+ for (auto it = passes_commands.begin(); it != passes_commands.end(); it++)
+ run_pass(*it);
+
+ if (!backend_command.empty())
+ run_backend(output_filename, backend_command);
+
+ if (print_stats)
+ {
+ std::string hash = log_hasher->final().substr(0, 10);
+ delete log_hasher;
+ log_hasher = nullptr;
+
+ log_time = false;
+ yosys_xtrace = 0;
+ log_spacer();
+
+ if (mode_v && !mode_q)
+ log_files.push_back(stderr);
+
+#ifdef _WIN32
+ log("End of script. Logfile hash: %s\n", hash.c_str());
+#else
+ std::string meminfo;
+ std::string stats_divider = ", ";
+# ifdef __linux__
+ std::ifstream statm;
+ statm.open(stringf("/proc/%lld/statm", (long long)getpid()));
+ if (statm.is_open()) {
+ int sz_total, sz_resident;
+ statm >> sz_total >> sz_resident;
+ meminfo = stringf(", MEM: %.2f MB total, %.2f MB resident",
+ sz_total * (getpagesize() / 1024.0 / 1024.0),
+ sz_resident * (getpagesize() / 1024.0 / 1024.0));
+ stats_divider = "\n";
+ }
+# endif
+
+ struct rusage ru_buffer;
+ getrusage(RUSAGE_SELF, &ru_buffer);
+ log("End of script. Logfile hash: %s%sCPU: user %.2fs system %.2fs%s\n", hash.c_str(),
+ stats_divider.c_str(), ru_buffer.ru_utime.tv_sec + 1e-6 * ru_buffer.ru_utime.tv_usec,
+ ru_buffer.ru_stime.tv_sec + 1e-6 * ru_buffer.ru_stime.tv_usec, meminfo.c_str());
+#endif
+ log("%s\n", yosys_version_str);
+
+ int64_t total_ns = 0;
+ std::set<tuple<int64_t, int, std::string>> timedat;
+
+ for (auto &it : pass_register)
+ if (it.second->call_counter) {
+ total_ns += it.second->runtime_ns + 1;
+ timedat.insert(make_tuple(it.second->runtime_ns + 1, it.second->call_counter, it.first));
+ }
+
+ if (timing_details)
+ {
+ log("Time spent:\n");
+ for (auto it = timedat.rbegin(); it != timedat.rend(); it++) {
+ log("%5d%% %5d calls %8.3f sec %s\n", int(100*std::get<0>(*it) / total_ns),
+ std::get<1>(*it), std::get<0>(*it) / 1000000000.0, std::get<2>(*it).c_str());
+ }
+ }
+ else
+ {
+ int out_count = 0;
+ log("Time spent:");
+ for (auto it = timedat.rbegin(); it != timedat.rend() && out_count < 4; it++, out_count++) {
+ if (out_count >= 2 && (std::get<0>(*it) < 1000000000 || int(100*std::get<0>(*it) / total_ns) < 20)) {
+ log(", ...");
+ break;
+ }
+ log("%s %d%% %dx %s (%d sec)", out_count ? "," : "", int(100*std::get<0>(*it) / total_ns),
+ std::get<1>(*it), std::get<2>(*it).c_str(), int(std::get<0>(*it) / 1000000000));
+ }
+ log("%s\n", out_count ? "" : " no commands executed");
+ }
+ }
+
+#if defined(YOSYS_ENABLE_COVER) && defined(__linux__)
+ if (getenv("YOSYS_COVER_DIR") || getenv("YOSYS_COVER_FILE"))
+ {
+ string filename;
+ FILE *f;
+
+ if (getenv("YOSYS_COVER_DIR")) {
+ filename = stringf("%s/yosys_cover_%d_XXXXXX.txt", getenv("YOSYS_COVER_DIR"), getpid());
+ filename = make_temp_file(filename);
+ } else {
+ filename = getenv("YOSYS_COVER_FILE");
+ }
+
+ f = fopen(filename.c_str(), "a+");
+
+ if (f == NULL)
+ log_error("Can't create coverage file `%s'.\n", filename.c_str());
+
+ log("<writing coverage file \"%s\">\n", filename.c_str());
+
+ for (auto &it : get_coverage_data())
+ fprintf(f, "%-60s %10d %s\n", it.second.first.c_str(), it.second.second, it.first.c_str());
+
+ fclose(f);
+ }
+#endif
+
+ memhasher_off();
+ if (call_abort)
+ abort();
+
+#ifdef YOSYS_ENABLE_READLINE
+ if (!history_file.empty()) {
+ if (history_offset > 0) {
+ history_truncate_file(history_file.c_str(), 100);
+ append_history(where_history() - history_offset, history_file.c_str());
+ } else
+ write_history(history_file.c_str());
+ }
+
+ clear_history();
+ HIST_ENTRY **hist_list = history_list();
+ if (hist_list != NULL)
+ free(hist_list);
+#endif
+
+ log_flush();
+#if defined(_MSC_VER)
+ _exit(0);
+#elif defined(_WIN32)
+ _Exit(0);
+#endif
+
+ yosys_shutdown();
+
+ return 0;
+}
+
+#endif /* EMSCRIPTEN */
+
diff --git a/kernel/hashlib.h b/kernel/hashlib.h
new file mode 100644
index 00000000..3c824b8c
--- /dev/null
+++ b/kernel/hashlib.h
@@ -0,0 +1,1052 @@
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+
+// -------------------------------------------------------
+// Written by Clifford Wolf <clifford@clifford.at> in 2014
+// -------------------------------------------------------
+
+#ifndef HASHLIB_H
+#define HASHLIB_H
+
+#include <stdexcept>
+#include <algorithm>
+#include <string>
+#include <vector>
+
+namespace hashlib {
+
+const int hashtable_size_trigger = 2;
+const int hashtable_size_factor = 3;
+
+// The XOR version of DJB2
+inline unsigned int mkhash(unsigned int a, unsigned int b) {
+ return ((a << 5) + a) ^ b;
+}
+
+// traditionally 5381 is used as starting value for the djb2 hash
+const unsigned int mkhash_init = 5381;
+
+// The ADD version of DJB2
+// (use this version for cache locality in b)
+inline unsigned int mkhash_add(unsigned int a, unsigned int b) {
+ return ((a << 5) + a) + b;
+}
+
+inline unsigned int mkhash_xorshift(unsigned int a) {
+ if (sizeof(a) == 4) {
+ a ^= a << 13;
+ a ^= a >> 17;
+ a ^= a << 5;
+ } else if (sizeof(a) == 8) {
+ a ^= a << 13;
+ a ^= a >> 7;
+ a ^= a << 17;
+ } else
+ throw std::runtime_error("mkhash_xorshift() only implemented for 32 bit and 64 bit ints");
+ return a;
+}
+
+template<typename T> struct hash_ops {
+ static inline bool cmp(const T &a, const T &b) {
+ return a == b;
+ }
+ static inline unsigned int hash(const T &a) {
+ return a.hash();
+ }
+};
+
+struct hash_int_ops {
+ template<typename T>
+ static inline bool cmp(T a, T b) {
+ return a == b;
+ }
+};
+
+template<> struct hash_ops<int32_t> : hash_int_ops
+{
+ static inline unsigned int hash(int32_t a) {
+ return a;
+ }
+};
+template<> struct hash_ops<int64_t> : hash_int_ops
+{
+ static inline unsigned int hash(int64_t a) {
+ return mkhash((unsigned int)(a), (unsigned int)(a >> 32));
+ }
+};
+
+template<> struct hash_ops<std::string> {
+ static inline bool cmp(const std::string &a, const std::string &b) {
+ return a == b;
+ }
+ static inline unsigned int hash(const std::string &a) {
+ unsigned int v = 0;
+ for (auto c : a)
+ v = mkhash(v, c);
+ return v;
+ }
+};
+
+template<typename P, typename Q> struct hash_ops<std::pair<P, Q>> {
+ static inline bool cmp(std::pair<P, Q> a, std::pair<P, Q> b) {
+ return a == b;
+ }
+ static inline unsigned int hash(std::pair<P, Q> a) {
+ return mkhash(hash_ops<P>::hash(a.first), hash_ops<Q>::hash(a.second));
+ }
+};
+
+template<typename... T> struct hash_ops<std::tuple<T...>> {
+ static inline bool cmp(std::tuple<T...> a, std::tuple<T...> b) {
+ return a == b;
+ }
+ template<size_t I = 0>
+ static inline typename std::enable_if<I == sizeof...(T), unsigned int>::type hash(std::tuple<T...>) {
+ return mkhash_init;
+ }
+ template<size_t I = 0>
+ static inline typename std::enable_if<I != sizeof...(T), unsigned int>::type hash(std::tuple<T...> a) {
+ typedef hash_ops<typename std::tuple_element<I, std::tuple<T...>>::type> element_ops_t;
+ return mkhash(hash<I+1>(a), element_ops_t::hash(std::get<I>(a)));
+ }
+};
+
+template<typename T> struct hash_ops<std::vector<T>> {
+ static inline bool cmp(std::vector<T> a, std::vector<T> b) {
+ return a == b;
+ }
+ static inline unsigned int hash(std::vector<T> a) {
+ unsigned int h = mkhash_init;
+ for (auto k : a)
+ h = mkhash(h, hash_ops<T>::hash(k));
+ return h;
+ }
+};
+
+struct hash_cstr_ops {
+ static inline bool cmp(const char *a, const char *b) {
+ for (int i = 0; a[i] || b[i]; i++)
+ if (a[i] != b[i])
+ return false;
+ return true;
+ }
+ static inline unsigned int hash(const char *a) {
+ unsigned int hash = mkhash_init;
+ while (*a)
+ hash = mkhash(hash, *(a++));
+ return hash;
+ }
+};
+
+struct hash_ptr_ops {
+ static inline bool cmp(const void *a, const void *b) {
+ return a == b;
+ }
+ static inline unsigned int hash(const void *a) {
+ return (unsigned long)a;
+ }
+};
+
+struct hash_obj_ops {
+ static inline bool cmp(const void *a, const void *b) {
+ return a == b;
+ }
+ template<typename T>
+ static inline unsigned int hash(const T *a) {
+ return a ? a->hash() : 0;
+ }
+};
+
+template<typename T>
+inline unsigned int mkhash(const T &v) {
+ return hash_ops<T>().hash(v);
+}
+
+inline int hashtable_size(int min_size)
+{
+ static std::vector<int> zero_and_some_primes = {
+ 0, 23, 29, 37, 47, 59, 79, 101, 127, 163, 211, 269, 337, 431, 541, 677,
+ 853, 1069, 1361, 1709, 2137, 2677, 3347, 4201, 5261, 6577, 8231, 10289,
+ 12889, 16127, 20161, 25219, 31531, 39419, 49277, 61603, 77017, 96281,
+ 120371, 150473, 188107, 235159, 293957, 367453, 459317, 574157, 717697,
+ 897133, 1121423, 1401791, 1752239, 2190299, 2737937, 3422429, 4278037,
+ 5347553, 6684443, 8355563, 10444457, 13055587, 16319519, 20399411,
+ 25499291, 31874149, 39842687, 49803361, 62254207, 77817767, 97272239,
+ 121590311, 151987889, 189984863, 237481091, 296851369, 371064217
+ };
+
+ for (auto p : zero_and_some_primes)
+ if (p >= min_size) return p;
+
+ if (sizeof(int) == 4)
+ throw std::length_error("hash table exceeded maximum size. use a ILP64 abi for larger tables.");
+
+ for (auto p : zero_and_some_primes)
+ if (100129 * p > min_size) return 100129 * p;
+
+ throw std::length_error("hash table exceeded maximum size.");
+}
+
+template<typename K, typename T, typename OPS = hash_ops<K>> class dict;
+template<typename K, int offset = 0, typename OPS = hash_ops<K>> class idict;
+template<typename K, typename OPS = hash_ops<K>> class pool;
+template<typename K, typename OPS = hash_ops<K>> class mfp;
+
+template<typename K, typename T, typename OPS>
+class dict
+{
+ struct entry_t
+ {
+ std::pair<K, T> udata;
+ int next;
+
+ entry_t() { }
+ entry_t(const std::pair<K, T> &udata, int next) : udata(udata), next(next) { }
+ entry_t(std::pair<K, T> &&udata, int next) : udata(std::move(udata)), next(next) { }
+ };
+
+ std::vector<int> hashtable;
+ std::vector<entry_t> entries;
+ OPS ops;
+
+#ifdef NDEBUG
+ static inline void do_assert(bool) { }
+#else
+ static inline void do_assert(bool cond) {
+ if (!cond) throw std::runtime_error("dict<> assert failed.");
+ }
+#endif
+
+ int do_hash(const K &key) const
+ {
+ unsigned int hash = 0;
+ if (!hashtable.empty())
+ hash = ops.hash(key) % (unsigned int)(hashtable.size());
+ return hash;
+ }
+
+ void do_rehash()
+ {
+ hashtable.clear();
+ hashtable.resize(hashtable_size(entries.capacity() * hashtable_size_factor), -1);
+
+ for (int i = 0; i < int(entries.size()); i++) {
+ do_assert(-1 <= entries[i].next && entries[i].next < int(entries.size()));
+ int hash = do_hash(entries[i].udata.first);
+ entries[i].next = hashtable[hash];
+ hashtable[hash] = i;
+ }
+ }
+
+ int do_erase(int index, int hash)
+ {
+ do_assert(index < int(entries.size()));
+ if (hashtable.empty() || index < 0)
+ return 0;
+
+ int k = hashtable[hash];
+ do_assert(0 <= k && k < int(entries.size()));
+
+ if (k == index) {
+ hashtable[hash] = entries[index].next;
+ } else {
+ while (entries[k].next != index) {
+ k = entries[k].next;
+ do_assert(0 <= k && k < int(entries.size()));
+ }
+ entries[k].next = entries[index].next;
+ }
+
+ int back_idx = entries.size()-1;
+
+ if (index != back_idx)
+ {
+ int back_hash = do_hash(entries[back_idx].udata.first);
+
+ k = hashtable[back_hash];
+ do_assert(0 <= k && k < int(entries.size()));
+
+ if (k == back_idx) {
+ hashtable[back_hash] = index;
+ } else {
+ while (entries[k].next != back_idx) {
+ k = entries[k].next;
+ do_assert(0 <= k && k < int(entries.size()));
+ }
+ entries[k].next = index;
+ }
+
+ entries[index] = std::move(entries[back_idx]);
+ }
+
+ entries.pop_back();
+
+ if (entries.empty())
+ hashtable.clear();
+
+ return 1;
+ }
+
+ int do_lookup(const K &key, int &hash) const
+ {
+ if (hashtable.empty())
+ return -1;
+
+ if (entries.size() * hashtable_size_trigger > hashtable.size()) {
+ ((dict*)this)->do_rehash();
+ hash = do_hash(key);
+ }
+
+ int index = hashtable[hash];
+
+ while (index >= 0 && !ops.cmp(entries[index].udata.first, key)) {
+ index = entries[index].next;
+ do_assert(-1 <= index && index < int(entries.size()));
+ }
+
+ return index;
+ }
+
+ int do_insert(const K &key, int &hash)
+ {
+ if (hashtable.empty()) {
+ entries.push_back(entry_t(std::pair<K, T>(key, T()), -1));
+ do_rehash();
+ hash = do_hash(key);
+ } else {
+ entries.push_back(entry_t(std::pair<K, T>(key, T()), hashtable[hash]));
+ hashtable[hash] = entries.size() - 1;
+ }
+ return entries.size() - 1;
+ }
+
+ int do_insert(const std::pair<K, T> &value, int &hash)
+ {
+ if (hashtable.empty()) {
+ entries.push_back(entry_t(value, -1));
+ do_rehash();
+ hash = do_hash(value.first);
+ } else {
+ entries.push_back(entry_t(value, hashtable[hash]));
+ hashtable[hash] = entries.size() - 1;
+ }
+ return entries.size() - 1;
+ }
+
+public:
+ class const_iterator : public std::iterator<std::forward_iterator_tag, std::pair<K, T>>
+ {
+ friend class dict;
+ protected:
+ const dict *ptr;
+ int index;
+ const_iterator(const dict *ptr, int index) : ptr(ptr), index(index) { }
+ public:
+ const_iterator() { }
+ const_iterator operator++() { index--; return *this; }
+ bool operator<(const const_iterator &other) const { return index > other.index; }
+ bool operator==(const const_iterator &other) const { return index == other.index; }
+ bool operator!=(const const_iterator &other) const { return index != other.index; }
+ const std::pair<K, T> &operator*() const { return ptr->entries[index].udata; }
+ const std::pair<K, T> *operator->() const { return &ptr->entries[index].udata; }
+ };
+
+ class iterator : public std::iterator<std::forward_iterator_tag, std::pair<K, T>>
+ {
+ friend class dict;
+ protected:
+ dict *ptr;
+ int index;
+ iterator(dict *ptr, int index) : ptr(ptr), index(index) { }
+ public:
+ iterator() { }
+ iterator operator++() { index--; return *this; }
+ bool operator<(const iterator &other) const { return index > other.index; }
+ bool operator==(const iterator &other) const { return index == other.index; }
+ bool operator!=(const iterator &other) const { return index != other.index; }
+ std::pair<K, T> &operator*() { return ptr->entries[index].udata; }
+ std::pair<K, T> *operator->() { return &ptr->entries[index].udata; }
+ const std::pair<K, T> &operator*() const { return ptr->entries[index].udata; }
+ const std::pair<K, T> *operator->() const { return &ptr->entries[index].udata; }
+ operator const_iterator() const { return const_iterator(ptr, index); }
+ };
+
+ dict()
+ {
+ }
+
+ dict(const dict &other)
+ {
+ entries = other.entries;
+ do_rehash();
+ }
+
+ dict(dict &&other)
+ {
+ swap(other);
+ }
+
+ dict &operator=(const dict &other) {
+ entries = other.entries;
+ do_rehash();
+ return *this;
+ }
+
+ dict &operator=(dict &&other) {
+ clear();
+ swap(other);
+ return *this;
+ }
+
+ dict(const std::initializer_list<std::pair<K, T>> &list)
+ {
+ for (auto &it : list)
+ insert(it);
+ }
+
+ template<class InputIterator>
+ dict(InputIterator first, InputIterator last)
+ {
+ insert(first, last);
+ }
+
+ template<class InputIterator>
+ void insert(InputIterator first, InputIterator last)
+ {
+ for (; first != last; ++first)
+ insert(*first);
+ }
+
+ std::pair<iterator, bool> insert(const K &key)
+ {
+ int hash = do_hash(key);
+ int i = do_lookup(key, hash);
+ if (i >= 0)
+ return std::pair<iterator, bool>(iterator(this, i), false);
+ i = do_insert(key, hash);
+ return std::pair<iterator, bool>(iterator(this, i), true);
+ }
+
+ std::pair<iterator, bool> insert(const std::pair<K, T> &value)
+ {
+ int hash = do_hash(value.first);
+ int i = do_lookup(value.first, hash);
+ if (i >= 0)
+ return std::pair<iterator, bool>(iterator(this, i), false);
+ i = do_insert(value, hash);
+ return std::pair<iterator, bool>(iterator(this, i), true);
+ }
+
+ int erase(const K &key)
+ {
+ int hash = do_hash(key);
+ int index = do_lookup(key, hash);
+ return do_erase(index, hash);
+ }
+
+ iterator erase(iterator it)
+ {
+ int hash = do_hash(it->first);
+ do_erase(it.index, hash);
+ return ++it;
+ }
+
+ int count(const K &key) const
+ {
+ int hash = do_hash(key);
+ int i = do_lookup(key, hash);
+ return i < 0 ? 0 : 1;
+ }
+
+ int count(const K &key, const_iterator it) const
+ {
+ int hash = do_hash(key);
+ int i = do_lookup(key, hash);
+ return i < 0 || i > it.index ? 0 : 1;
+ }
+
+ iterator find(const K &key)
+ {
+ int hash = do_hash(key);
+ int i = do_lookup(key, hash);
+ if (i < 0)
+ return end();
+ return iterator(this, i);
+ }
+
+ const_iterator find(const K &key) const
+ {
+ int hash = do_hash(key);
+ int i = do_lookup(key, hash);
+ if (i < 0)
+ return end();
+ return const_iterator(this, i);
+ }
+
+ T& at(const K &key)
+ {
+ int hash = do_hash(key);
+ int i = do_lookup(key, hash);
+ if (i < 0)
+ throw std::out_of_range("dict::at()");
+ return entries[i].udata.second;
+ }
+
+ const T& at(const K &key) const
+ {
+ int hash = do_hash(key);
+ int i = do_lookup(key, hash);
+ if (i < 0)
+ throw std::out_of_range("dict::at()");
+ return entries[i].udata.second;
+ }
+
+ T at(const K &key, const T &defval) const
+ {
+ int hash = do_hash(key);
+ int i = do_lookup(key, hash);
+ if (i < 0)
+ return defval;
+ return entries[i].udata.second;
+ }
+
+ T& operator[](const K &key)
+ {
+ int hash = do_hash(key);
+ int i = do_lookup(key, hash);
+ if (i < 0)
+ i = do_insert(std::pair<K, T>(key, T()), hash);
+ return entries[i].udata.second;
+ }
+
+ template<typename Compare = std::less<K>>
+ void sort(Compare comp = Compare())
+ {
+ std::sort(entries.begin(), entries.end(), [comp](const entry_t &a, const entry_t &b){ return comp(b.udata.first, a.udata.first); });
+ do_rehash();
+ }
+
+ void swap(dict &other)
+ {
+ hashtable.swap(other.hashtable);
+ entries.swap(other.entries);
+ }
+
+ bool operator==(const dict &other) const {
+ if (size() != other.size())
+ return false;
+ for (auto &it : entries) {
+ auto oit = other.find(it.udata.first);
+ if (oit == other.end() || !(oit->second == it.udata.second))
+ return false;
+ }
+ return true;
+ }
+
+ bool operator!=(const dict &other) const {
+ return !operator==(other);
+ }
+
+ void reserve(size_t n) { entries.reserve(n); }
+ size_t size() const { return entries.size(); }
+ bool empty() const { return entries.empty(); }
+ void clear() { hashtable.clear(); entries.clear(); }
+
+ iterator begin() { return iterator(this, int(entries.size())-1); }
+ iterator end() { return iterator(nullptr, -1); }
+
+ const_iterator begin() const { return const_iterator(this, int(entries.size())-1); }
+ const_iterator end() const { return const_iterator(nullptr, -1); }
+};
+
+template<typename K, typename OPS>
+class pool
+{
+ template<typename, int, typename> friend class idict;
+
+protected:
+ struct entry_t
+ {
+ K udata;
+ int next;
+
+ entry_t() { }
+ entry_t(const K &udata, int next) : udata(udata), next(next) { }
+ };
+
+ std::vector<int> hashtable;
+ std::vector<entry_t> entries;
+ OPS ops;
+
+#ifdef NDEBUG
+ static inline void do_assert(bool) { }
+#else
+ static inline void do_assert(bool cond) {
+ if (!cond) throw std::runtime_error("pool<> assert failed.");
+ }
+#endif
+
+ int do_hash(const K &key) const
+ {
+ unsigned int hash = 0;
+ if (!hashtable.empty())
+ hash = ops.hash(key) % (unsigned int)(hashtable.size());
+ return hash;
+ }
+
+ void do_rehash()
+ {
+ hashtable.clear();
+ hashtable.resize(hashtable_size(entries.capacity() * hashtable_size_factor), -1);
+
+ for (int i = 0; i < int(entries.size()); i++) {
+ do_assert(-1 <= entries[i].next && entries[i].next < int(entries.size()));
+ int hash = do_hash(entries[i].udata);
+ entries[i].next = hashtable[hash];
+ hashtable[hash] = i;
+ }
+ }
+
+ int do_erase(int index, int hash)
+ {
+ do_assert(index < int(entries.size()));
+ if (hashtable.empty() || index < 0)
+ return 0;
+
+ int k = hashtable[hash];
+ if (k == index) {
+ hashtable[hash] = entries[index].next;
+ } else {
+ while (entries[k].next != index) {
+ k = entries[k].next;
+ do_assert(0 <= k && k < int(entries.size()));
+ }
+ entries[k].next = entries[index].next;
+ }
+
+ int back_idx = entries.size()-1;
+
+ if (index != back_idx)
+ {
+ int back_hash = do_hash(entries[back_idx].udata);
+
+ k = hashtable[back_hash];
+ if (k == back_idx) {
+ hashtable[back_hash] = index;
+ } else {
+ while (entries[k].next != back_idx) {
+ k = entries[k].next;
+ do_assert(0 <= k && k < int(entries.size()));
+ }
+ entries[k].next = index;
+ }
+
+ entries[index] = std::move(entries[back_idx]);
+ }
+
+ entries.pop_back();
+
+ if (entries.empty())
+ hashtable.clear();
+
+ return 1;
+ }
+
+ int do_lookup(const K &key, int &hash) const
+ {
+ if (hashtable.empty())
+ return -1;
+
+ if (entries.size() * hashtable_size_trigger > hashtable.size()) {
+ ((pool*)this)->do_rehash();
+ hash = do_hash(key);
+ }
+
+ int index = hashtable[hash];
+
+ while (index >= 0 && !ops.cmp(entries[index].udata, key)) {
+ index = entries[index].next;
+ do_assert(-1 <= index && index < int(entries.size()));
+ }
+
+ return index;
+ }
+
+ int do_insert(const K &value, int &hash)
+ {
+ if (hashtable.empty()) {
+ entries.push_back(entry_t(value, -1));
+ do_rehash();
+ hash = do_hash(value);
+ } else {
+ entries.push_back(entry_t(value, hashtable[hash]));
+ hashtable[hash] = entries.size() - 1;
+ }
+ return entries.size() - 1;
+ }
+
+public:
+ class const_iterator : public std::iterator<std::forward_iterator_tag, K>
+ {
+ friend class pool;
+ protected:
+ const pool *ptr;
+ int index;
+ const_iterator(const pool *ptr, int index) : ptr(ptr), index(index) { }
+ public:
+ const_iterator() { }
+ const_iterator operator++() { index--; return *this; }
+ bool operator==(const const_iterator &other) const { return index == other.index; }
+ bool operator!=(const const_iterator &other) const { return index != other.index; }
+ const K &operator*() const { return ptr->entries[index].udata; }
+ const K *operator->() const { return &ptr->entries[index].udata; }
+ };
+
+ class iterator : public std::iterator<std::forward_iterator_tag, K>
+ {
+ friend class pool;
+ protected:
+ pool *ptr;
+ int index;
+ iterator(pool *ptr, int index) : ptr(ptr), index(index) { }
+ public:
+ iterator() { }
+ iterator operator++() { index--; return *this; }
+ bool operator==(const iterator &other) const { return index == other.index; }
+ bool operator!=(const iterator &other) const { return index != other.index; }
+ K &operator*() { return ptr->entries[index].udata; }
+ K *operator->() { return &ptr->entries[index].udata; }
+ const K &operator*() const { return ptr->entries[index].udata; }
+ const K *operator->() const { return &ptr->entries[index].udata; }
+ operator const_iterator() const { return const_iterator(ptr, index); }
+ };
+
+ pool()
+ {
+ }
+
+ pool(const pool &other)
+ {
+ entries = other.entries;
+ do_rehash();
+ }
+
+ pool(pool &&other)
+ {
+ swap(other);
+ }
+
+ pool &operator=(const pool &other) {
+ entries = other.entries;
+ do_rehash();
+ return *this;
+ }
+
+ pool &operator=(pool &&other) {
+ clear();
+ swap(other);
+ return *this;
+ }
+
+ pool(const std::initializer_list<K> &list)
+ {
+ for (auto &it : list)
+ insert(it);
+ }
+
+ template<class InputIterator>
+ pool(InputIterator first, InputIterator last)
+ {
+ insert(first, last);
+ }
+
+ template<class InputIterator>
+ void insert(InputIterator first, InputIterator last)
+ {
+ for (; first != last; ++first)
+ insert(*first);
+ }
+
+ std::pair<iterator, bool> insert(const K &value)
+ {
+ int hash = do_hash(value);
+ int i = do_lookup(value, hash);
+ if (i >= 0)
+ return std::pair<iterator, bool>(iterator(this, i), false);
+ i = do_insert(value, hash);
+ return std::pair<iterator, bool>(iterator(this, i), true);
+ }
+
+ int erase(const K &key)
+ {
+ int hash = do_hash(key);
+ int index = do_lookup(key, hash);
+ return do_erase(index, hash);
+ }
+
+ iterator erase(iterator it)
+ {
+ int hash = do_hash(*it);
+ do_erase(it.index, hash);
+ return ++it;
+ }
+
+ int count(const K &key) const
+ {
+ int hash = do_hash(key);
+ int i = do_lookup(key, hash);
+ return i < 0 ? 0 : 1;
+ }
+
+ int count(const K &key, const_iterator it) const
+ {
+ int hash = do_hash(key);
+ int i = do_lookup(key, hash);
+ return i < 0 || i > it.index ? 0 : 1;
+ }
+
+ iterator find(const K &key)
+ {
+ int hash = do_hash(key);
+ int i = do_lookup(key, hash);
+ if (i < 0)
+ return end();
+ return iterator(this, i);
+ }
+
+ const_iterator find(const K &key) const
+ {
+ int hash = do_hash(key);
+ int i = do_lookup(key, hash);
+ if (i < 0)
+ return end();
+ return const_iterator(this, i);
+ }
+
+ bool operator[](const K &key)
+ {
+ int hash = do_hash(key);
+ int i = do_lookup(key, hash);
+ return i >= 0;
+ }
+
+ template<typename Compare = std::less<K>>
+ void sort(Compare comp = Compare())
+ {
+ std::sort(entries.begin(), entries.end(), [comp](const entry_t &a, const entry_t &b){ return comp(b.udata, a.udata); });
+ do_rehash();
+ }
+
+ K pop()
+ {
+ iterator it = begin();
+ K ret = *it;
+ erase(it);
+ return ret;
+ }
+
+ void swap(pool &other)
+ {
+ hashtable.swap(other.hashtable);
+ entries.swap(other.entries);
+ }
+
+ bool operator==(const pool &other) const {
+ if (size() != other.size())
+ return false;
+ for (auto &it : entries)
+ if (!other.count(it.udata))
+ return false;
+ return true;
+ }
+
+ bool operator!=(const pool &other) const {
+ return !operator==(other);
+ }
+
+ void reserve(size_t n) { entries.reserve(n); }
+ size_t size() const { return entries.size(); }
+ bool empty() const { return entries.empty(); }
+ void clear() { hashtable.clear(); entries.clear(); }
+
+ iterator begin() { return iterator(this, int(entries.size())-1); }
+ iterator end() { return iterator(nullptr, -1); }
+
+ const_iterator begin() const { return const_iterator(this, int(entries.size())-1); }
+ const_iterator end() const { return const_iterator(nullptr, -1); }
+};
+
+template<typename K, int offset, typename OPS>
+class idict
+{
+ pool<K, OPS> database;
+
+public:
+ typedef typename pool<K, OPS>::const_iterator const_iterator;
+
+ int operator()(const K &key)
+ {
+ int hash = database.do_hash(key);
+ int i = database.do_lookup(key, hash);
+ if (i < 0)
+ i = database.do_insert(key, hash);
+ return i + offset;
+ }
+
+ int at(const K &key) const
+ {
+ int hash = database.do_hash(key);
+ int i = database.do_lookup(key, hash);
+ if (i < 0)
+ throw std::out_of_range("idict::at()");
+ return i + offset;
+ }
+
+ int at(const K &key, int defval) const
+ {
+ int hash = database.do_hash(key);
+ int i = database.do_lookup(key, hash);
+ if (i < 0)
+ return defval;
+ return i + offset;
+ }
+
+ int count(const K &key) const
+ {
+ int hash = database.do_hash(key);
+ int i = database.do_lookup(key, hash);
+ return i < 0 ? 0 : 1;
+ }
+
+ void expect(const K &key, int i)
+ {
+ int j = (*this)(key);
+ if (i != j)
+ throw std::out_of_range("idict::expect()");
+ }
+
+ const K &operator[](int index) const
+ {
+ return database.entries.at(index - offset).udata;
+ }
+
+ void swap(idict &other)
+ {
+ database.swap(other.database);
+ }
+
+ void reserve(size_t n) { database.reserve(n); }
+ size_t size() const { return database.size(); }
+ bool empty() const { return database.empty(); }
+ void clear() { database.clear(); }
+
+ const_iterator begin() const { return database.begin(); }
+ const_iterator end() const { return database.end(); }
+};
+
+template<typename K, typename OPS>
+class mfp
+{
+ mutable idict<K, 0, OPS> database;
+ mutable std::vector<int> parents;
+
+public:
+ typedef typename idict<K, 0, OPS>::const_iterator const_iterator;
+
+ int operator()(const K &key) const
+ {
+ int i = database(key);
+ parents.resize(database.size(), -1);
+ return i;
+ }
+
+ const K &operator[](int index) const
+ {
+ return database[index];
+ }
+
+ int ifind(int i) const
+ {
+ int p = i, k = i;
+
+ while (parents[p] != -1)
+ p = parents[p];
+
+ while (k != p) {
+ int next_k = parents[k];
+ parents[k] = p;
+ k = next_k;
+ }
+
+ return p;
+ }
+
+ void imerge(int i, int j)
+ {
+ i = ifind(i);
+ j = ifind(j);
+
+ if (i != j)
+ parents[i] = j;
+ }
+
+ void ipromote(int i)
+ {
+ int k = i;
+
+ while (k != -1) {
+ int next_k = parents[k];
+ parents[k] = i;
+ k = next_k;
+ }
+
+ parents[i] = -1;
+ }
+
+ int lookup(const K &a) const
+ {
+ return ifind((*this)(a));
+ }
+
+ const K &find(const K &a) const
+ {
+ int i = database.at(a, -1);
+ if (i < 0)
+ return a;
+ return (*this)[ifind(i)];
+ }
+
+ void merge(const K &a, const K &b)
+ {
+ imerge((*this)(a), (*this)(b));
+ }
+
+ void promote(const K &a)
+ {
+ int i = database.at(a, -1);
+ if (i >= 0)
+ ipromote(i);
+ }
+
+ void swap(mfp &other)
+ {
+ database.swap(other.database);
+ parents.swap(other.parents);
+ }
+
+ void reserve(size_t n) { database.reserve(n); }
+ size_t size() const { return database.size(); }
+ bool empty() const { return database.empty(); }
+ void clear() { database.clear(); parents.clear(); }
+
+ const_iterator begin() const { return database.begin(); }
+ const_iterator end() const { return database.end(); }
+};
+
+} /* namespace hashlib */
+
+#endif
diff --git a/kernel/log.cc b/kernel/log.cc
new file mode 100644
index 00000000..abc401f5
--- /dev/null
+++ b/kernel/log.cc
@@ -0,0 +1,516 @@
+/*
+ * 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 "libs/sha1/sha1.h"
+#include "backends/ilang/ilang_backend.h"
+
+#if !defined(_WIN32) || defined(__MINGW32__)
+# include <sys/time.h>
+#endif
+
+#ifdef __linux__
+# include <dlfcn.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <vector>
+#include <list>
+
+YOSYS_NAMESPACE_BEGIN
+
+std::vector<FILE*> log_files;
+std::vector<std::ostream*> log_streams;
+std::map<std::string, std::set<std::string>> log_hdump;
+bool log_hdump_all = false;
+FILE *log_errfile = NULL;
+SHA1 *log_hasher = NULL;
+
+bool log_time = false;
+bool log_error_stderr = false;
+bool log_cmd_error_throw = false;
+bool log_quiet_warnings = false;
+int log_verbose_level;
+string log_last_error;
+
+vector<int> header_count;
+pool<RTLIL::IdString> log_id_cache;
+vector<shared_str> string_buf;
+int string_buf_index = -1;
+
+static struct timeval initial_tv = { 0, 0 };
+static bool next_print_log = false;
+static int log_newline_count = 0;
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+// this will get time information and return it in timeval, simulating gettimeofday()
+int gettimeofday(struct timeval *tv, struct timezone *tz)
+{
+ LARGE_INTEGER counter;
+ LARGE_INTEGER freq;
+
+ QueryPerformanceFrequency(&freq);
+ QueryPerformanceCounter(&counter);
+
+ counter.QuadPart *= 1000000;
+ counter.QuadPart /= freq.QuadPart;
+
+ tv->tv_sec = long(counter.QuadPart / 1000000);
+ tv->tv_usec = counter.QuadPart % 1000000;
+
+ return 0;
+}
+#endif
+
+void logv(const char *format, va_list ap)
+{
+ while (format[0] == '\n' && format[1] != 0) {
+ log("\n");
+ format++;
+ }
+
+ std::string str = vstringf(format, ap);
+
+ if (str.empty())
+ return;
+
+ size_t nnl_pos = str.find_last_not_of('\n');
+ if (nnl_pos == std::string::npos)
+ log_newline_count += GetSize(str);
+ else
+ log_newline_count = GetSize(str) - nnl_pos - 1;
+
+ if (log_hasher)
+ log_hasher->update(str);
+
+ if (log_time)
+ {
+ std::string time_str;
+
+ if (next_print_log || initial_tv.tv_sec == 0) {
+ next_print_log = false;
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ if (initial_tv.tv_sec == 0)
+ initial_tv = tv;
+ if (tv.tv_usec < initial_tv.tv_usec) {
+ tv.tv_sec--;
+ tv.tv_usec += 1000000;
+ }
+ tv.tv_sec -= initial_tv.tv_sec;
+ tv.tv_usec -= initial_tv.tv_usec;
+ time_str += stringf("[%05d.%06d] ", int(tv.tv_sec), int(tv.tv_usec));
+ }
+
+ if (format[0] && format[strlen(format)-1] == '\n')
+ next_print_log = true;
+
+ for (auto f : log_files)
+ fputs(time_str.c_str(), f);
+
+ for (auto f : log_streams)
+ *f << time_str;
+ }
+
+ for (auto f : log_files)
+ fputs(str.c_str(), f);
+
+ for (auto f : log_streams)
+ *f << str;
+}
+
+void logv_header(RTLIL::Design *design, const char *format, va_list ap)
+{
+ bool pop_errfile = false;
+
+ log_spacer();
+ if (header_count.size() > 0)
+ header_count.back()++;
+
+ if (int(header_count.size()) <= log_verbose_level && log_errfile != NULL) {
+ log_files.push_back(log_errfile);
+ pop_errfile = true;
+ }
+
+ std::string header_id;
+
+ for (int c : header_count)
+ header_id += stringf("%s%d", header_id.empty() ? "" : ".", c);
+
+ log("%s. ", header_id.c_str());
+ logv(format, ap);
+ log_flush();
+
+ if (log_hdump_all)
+ log_hdump[header_id].insert("yosys_dump_" + header_id + ".il");
+
+ 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());
+ Pass::call(design, {"dump", "-o", filename});
+ }
+
+ if (pop_errfile)
+ log_files.pop_back();
+}
+
+void logv_warning(const char *format, va_list ap)
+{
+ if (log_errfile != NULL && !log_quiet_warnings)
+ log_files.push_back(log_errfile);
+
+ log("Warning: ");
+ logv(format, ap);
+ log_flush();
+
+ if (log_errfile != NULL && !log_quiet_warnings)
+ log_files.pop_back();
+}
+
+void logv_error(const char *format, va_list ap)
+{
+#ifdef EMSCRIPTEN
+ auto backup_log_files = log_files;
+#endif
+
+ if (log_errfile != NULL)
+ log_files.push_back(log_errfile);
+
+ if (log_error_stderr)
+ for (auto &f : log_files)
+ if (f == stdout)
+ f = stderr;
+
+ log_last_error = vstringf(format, ap);
+ log("ERROR: %s", log_last_error.c_str());
+ log_flush();
+
+#ifdef EMSCRIPTEN
+ log_files = backup_log_files;
+ throw 0;
+#elif defined(_MSC_VER)
+ _exit(1);
+#else
+ _Exit(1);
+#endif
+}
+
+void log(const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ logv(format, ap);
+ va_end(ap);
+}
+
+void log_header(RTLIL::Design *design, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ logv_header(design, format, ap);
+ va_end(ap);
+}
+
+void log_warning(const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ logv_warning(format, ap);
+ va_end(ap);
+}
+
+void log_error(const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ logv_error(format, ap);
+}
+
+void log_cmd_error(const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+
+ if (log_cmd_error_throw) {
+ log_last_error = vstringf(format, ap);
+ log("ERROR: %s", log_last_error.c_str());
+ log_flush();
+ throw log_cmd_error_exception();
+ }
+
+ logv_error(format, ap);
+}
+
+void log_spacer()
+{
+ while (log_newline_count < 2)
+ log("\n");
+}
+
+void log_push()
+{
+ header_count.push_back(0);
+}
+
+void log_pop()
+{
+ header_count.pop_back();
+ log_id_cache.clear();
+ string_buf.clear();
+ string_buf_index = -1;
+ log_flush();
+}
+
+#if defined(__linux__) && defined(YOSYS_ENABLE_PLUGINS)
+void log_backtrace(const char *prefix, int levels)
+{
+ if (levels <= 0) return;
+
+ Dl_info dli;
+ void *p;
+
+ if ((p = __builtin_extract_return_addr(__builtin_return_address(0))) && dladdr(p, &dli)) {
+ log("%sframe #1: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr);
+ } else {
+ log("%sframe #1: ---\n", prefix);
+ return;
+ }
+
+ if (levels <= 1) return;
+
+ if ((p = __builtin_extract_return_addr(__builtin_return_address(1))) && dladdr(p, &dli)) {
+ log("%sframe #2: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr);
+ } else {
+ log("%sframe #2: ---\n", prefix);
+ return;
+ }
+
+ if (levels <= 2) return;
+
+ if ((p = __builtin_extract_return_addr(__builtin_return_address(2))) && dladdr(p, &dli)) {
+ log("%sframe #3: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr);
+ } else {
+ log("%sframe #3: ---\n", prefix);
+ return;
+ }
+
+ if (levels <= 3) return;
+
+ if ((p = __builtin_extract_return_addr(__builtin_return_address(3))) && dladdr(p, &dli)) {
+ log("%sframe #4: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr);
+ } else {
+ log("%sframe #4: ---\n", prefix);
+ return;
+ }
+
+ if (levels <= 4) return;
+
+ if ((p = __builtin_extract_return_addr(__builtin_return_address(4))) && dladdr(p, &dli)) {
+ log("%sframe #5: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr);
+ } else {
+ log("%sframe #5: ---\n", prefix);
+ return;
+ }
+
+ if (levels <= 5) return;
+
+ if ((p = __builtin_extract_return_addr(__builtin_return_address(5))) && dladdr(p, &dli)) {
+ log("%sframe #6: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr);
+ } else {
+ log("%sframe #6: ---\n", prefix);
+ return;
+ }
+
+ if (levels <= 6) return;
+
+ if ((p = __builtin_extract_return_addr(__builtin_return_address(6))) && dladdr(p, &dli)) {
+ log("%sframe #7: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr);
+ } else {
+ log("%sframe #7: ---\n", prefix);
+ return;
+ }
+
+ if (levels <= 7) return;
+
+ if ((p = __builtin_extract_return_addr(__builtin_return_address(7))) && dladdr(p, &dli)) {
+ log("%sframe #8: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr);
+ } else {
+ log("%sframe #8: ---\n", prefix);
+ return;
+ }
+
+ if (levels <= 8) return;
+
+ if ((p = __builtin_extract_return_addr(__builtin_return_address(8))) && dladdr(p, &dli)) {
+ log("%sframe #9: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr);
+ } else {
+ log("%sframe #9: ---\n", prefix);
+ return;
+ }
+
+ if (levels <= 9) return;
+}
+#else
+void log_backtrace(const char*, int) { }
+#endif
+
+void log_reset_stack()
+{
+ while (header_count.size() > 1)
+ header_count.pop_back();
+ log_id_cache.clear();
+ string_buf.clear();
+ string_buf_index = -1;
+ log_flush();
+}
+
+void log_flush()
+{
+ for (auto f : log_files)
+ fflush(f);
+
+ for (auto f : log_streams)
+ f->flush();
+}
+
+void log_dump_val_worker(RTLIL::IdString v) {
+ log("%s", log_id(v));
+}
+
+void log_dump_val_worker(RTLIL::SigSpec v) {
+ log("%s", log_signal(v));
+}
+
+const char *log_signal(const RTLIL::SigSpec &sig, bool autoint)
+{
+ std::stringstream buf;
+ ILANG_BACKEND::dump_sigspec(buf, sig, autoint);
+
+ if (string_buf.size() < 100) {
+ string_buf.push_back(buf.str());
+ return string_buf.back().c_str();
+ } else {
+ if (++string_buf_index == 100)
+ string_buf_index = 0;
+ string_buf[string_buf_index] = buf.str();
+ return string_buf[string_buf_index].c_str();
+ }
+}
+
+const char *log_const(const RTLIL::Const &value, bool autoint)
+{
+ if ((value.flags & RTLIL::CONST_FLAG_STRING) == 0)
+ return log_signal(value, autoint);
+
+ std::string str = "\"" + value.decode_string() + "\"";
+
+ if (string_buf.size() < 100) {
+ string_buf.push_back(str);
+ return string_buf.back().c_str();
+ } else {
+ if (++string_buf_index == 100)
+ string_buf_index = 0;
+ string_buf[string_buf_index] = str;
+ return string_buf[string_buf_index].c_str();
+ }
+}
+
+const char *log_id(RTLIL::IdString str)
+{
+ log_id_cache.insert(str);
+ const char *p = str.c_str();
+ if (p[0] != '\\')
+ return p;
+ if (p[1] == '$' || p[1] == '\\' || p[1] == 0)
+ return p;
+ if (p[1] >= '0' && p[1] <= '9')
+ return p;
+ return p+1;
+}
+
+void log_module(RTLIL::Module *module, std::string indent)
+{
+ std::stringstream buf;
+ ILANG_BACKEND::dump_module(buf, indent, module, module->design, false);
+ log("%s", buf.str().c_str());
+}
+
+void log_cell(RTLIL::Cell *cell, std::string indent)
+{
+ std::stringstream buf;
+ ILANG_BACKEND::dump_cell(buf, indent, cell);
+ log("%s", buf.str().c_str());
+}
+
+// ---------------------------------------------------
+// This is the magic behind the code coverage counters
+// ---------------------------------------------------
+#if defined(YOSYS_ENABLE_COVER) && defined(__linux__)
+
+dict<std::string, std::pair<std::string, int>> extra_coverage_data;
+
+void cover_extra(std::string parent, std::string id, bool increment) {
+ if (extra_coverage_data.count(id) == 0) {
+ for (CoverData *p = __start_yosys_cover_list; p != __stop_yosys_cover_list; p++)
+ if (p->id == parent)
+ extra_coverage_data[id].first = stringf("%s:%d:%s", p->file, p->line, p->func);
+ log_assert(extra_coverage_data.count(id));
+ }
+ if (increment)
+ extra_coverage_data[id].second++;
+}
+
+dict<std::string, std::pair<std::string, int>> get_coverage_data()
+{
+ dict<std::string, std::pair<std::string, int>> coverage_data;
+
+ for (auto &it : pass_register) {
+ std::string key = stringf("passes.%s", it.first.c_str());
+ coverage_data[key].first = stringf("%s:%d:%s", __FILE__, __LINE__, __FUNCTION__);
+ coverage_data[key].second += it.second->call_counter;
+ }
+
+ for (auto &it : extra_coverage_data) {
+ if (coverage_data.count(it.first))
+ log_warning("found duplicate coverage id \"%s\".\n", it.first.c_str());
+ coverage_data[it.first].first = it.second.first;
+ coverage_data[it.first].second += it.second.second;
+ }
+
+ for (CoverData *p = __start_yosys_cover_list; p != __stop_yosys_cover_list; p++) {
+ if (coverage_data.count(p->id))
+ log_warning("found duplicate coverage id \"%s\".\n", p->id);
+ coverage_data[p->id].first = stringf("%s:%d:%s", p->file, p->line, p->func);
+ coverage_data[p->id].second += p->counter;
+ }
+
+ for (auto &it : coverage_data)
+ if (!it.second.first.compare(0, strlen(YOSYS_SRC "/"), YOSYS_SRC "/"))
+ it.second.first = it.second.first.substr(strlen(YOSYS_SRC "/"));
+
+ return coverage_data;
+}
+
+#endif
+
+YOSYS_NAMESPACE_END
+
diff --git a/kernel/log.h b/kernel/log.h
new file mode 100644
index 00000000..53480db3
--- /dev/null
+++ b/kernel/log.h
@@ -0,0 +1,308 @@
+/*
+ * 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"
+
+#ifndef LOG_H
+#define LOG_H
+
+#include <time.h>
+
+#ifndef _WIN32
+# include <sys/time.h>
+# include <sys/resource.h>
+#endif
+
+#if defined(_MSC_VER)
+// At least this is not in MSVC++ 2013.
+# define __PRETTY_FUNCTION__ __FUNCTION__
+#endif
+
+// from libs/sha1/sha1.h
+class SHA1;
+
+YOSYS_NAMESPACE_BEGIN
+
+#define S__LINE__sub2(x) #x
+#define S__LINE__sub1(x) S__LINE__sub2(x)
+#define S__LINE__ S__LINE__sub1(__LINE__)
+
+struct log_cmd_error_exception { };
+
+extern std::vector<FILE*> log_files;
+extern std::vector<std::ostream*> log_streams;
+extern std::map<std::string, std::set<std::string>> log_hdump;
+extern bool log_hdump_all;
+extern FILE *log_errfile;
+extern SHA1 *log_hasher;
+
+extern bool log_time;
+extern bool log_error_stderr;
+extern bool log_cmd_error_throw;
+extern bool log_quiet_warnings;
+extern int log_verbose_level;
+extern string log_last_error;
+
+void logv(const char *format, va_list ap);
+void logv_header(RTLIL::Design *design, const char *format, va_list ap);
+void logv_warning(const char *format, va_list ap);
+YS_NORETURN void logv_error(const char *format, va_list ap) YS_ATTRIBUTE(noreturn);
+
+void log(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
+void log_header(RTLIL::Design *design, const char *format, ...) YS_ATTRIBUTE(format(printf, 2, 3));
+void log_warning(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
+YS_NORETURN void log_error(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2), noreturn);
+YS_NORETURN void log_cmd_error(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2), noreturn);
+
+void log_spacer();
+void log_push();
+void log_pop();
+
+void log_backtrace(const char *prefix, int levels);
+void log_reset_stack();
+void log_flush();
+
+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) {
+ return log_id(obj->name);
+}
+
+void log_module(RTLIL::Module *module, std::string indent = "");
+void log_cell(RTLIL::Cell *cell, std::string indent = "");
+
+#ifndef NDEBUG
+static inline void log_assert_worker(bool cond, const char *expr, const char *file, int line) {
+ if (!cond) log_error("Assert `%s' failed in %s:%d.\n", expr, file, line);
+}
+# define log_assert(_assert_expr_) YOSYS_NAMESPACE_PREFIX log_assert_worker(_assert_expr_, #_assert_expr_, __FILE__, __LINE__)
+#else
+# define log_assert(_assert_expr_)
+#endif
+
+#define log_abort() YOSYS_NAMESPACE_PREFIX log_error("Abort in %s:%d.\n", __FILE__, __LINE__)
+#define log_ping() YOSYS_NAMESPACE_PREFIX log("-- %s:%d %s --\n", __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+
+// ---------------------------------------------------
+// This is the magic behind the code coverage counters
+// ---------------------------------------------------
+
+#if defined(YOSYS_ENABLE_COVER) && defined(__linux__)
+
+#define cover(_id) do { \
+ static CoverData __d __attribute__((section("yosys_cover_list"), aligned(1), used)) = { __FILE__, __FUNCTION__, _id, __LINE__, 0 }; \
+ __d.counter++; \
+} while (0)
+
+struct CoverData {
+ const char *file, *func, *id;
+ int line, counter;
+} YS_ATTRIBUTE(packed);
+
+// this two symbols are created by the linker for the "yosys_cover_list" ELF section
+extern "C" struct CoverData __start_yosys_cover_list[];
+extern "C" struct CoverData __stop_yosys_cover_list[];
+
+extern dict<std::string, std::pair<std::string, int>> extra_coverage_data;
+
+void cover_extra(std::string parent, std::string id, bool increment = true);
+dict<std::string, std::pair<std::string, int>> get_coverage_data();
+
+#define cover_list(_id, ...) do { cover(_id); \
+ std::string r = cover_list_worker(_id, __VA_ARGS__); \
+ log_assert(r.empty()); \
+} while (0)
+
+static inline std::string cover_list_worker(std::string, std::string last) {
+ return last;
+}
+
+template<typename... T>
+std::string cover_list_worker(std::string prefix, std::string first, T... rest) {
+ std::string selected = cover_list_worker(prefix, rest...);
+ cover_extra(prefix, prefix + "." + first, first == selected);
+ return first == selected ? "" : selected;
+}
+
+#else
+# define cover(...) do { } while (0)
+# define cover_list(...) do { } while (0)
+#endif
+
+
+// ------------------------------------------------------------
+// everything below this line are utilities for troubleshooting
+// ------------------------------------------------------------
+
+// simple timer for performance measurements
+// toggle the '#if 1' to get a baseline for the performance penalty added by the measurement
+struct PerformanceTimer
+{
+#if 1
+ int64_t total_ns;
+
+ PerformanceTimer() {
+ total_ns = 0;
+ }
+
+ static int64_t query() {
+# if _WIN32
+ return 0;
+# elif defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
+ struct timespec ts;
+ clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
+ return int64_t(ts.tv_sec)*1000000000 + ts.tv_nsec;
+# elif defined(RUSAGE_SELF)
+ struct rusage rusage;
+ int64_t t;
+ if (getrusage(RUSAGE_SELF, &rusage) == -1) {
+ log_cmd_error("getrusage failed!\n");
+ log_abort();
+ }
+ t = 1000000000ULL * (int64_t) rusage.ru_utime.tv_sec + (int64_t) rusage.ru_utime.tv_usec * 1000ULL;
+ t += 1000000000ULL * (int64_t) rusage.ru_stime.tv_sec + (int64_t) rusage.ru_stime.tv_usec * 1000ULL;
+ return t;
+# else
+# error Dont know how to measure per-process CPU time. Need alternative method (times()/clocks()/gettimeofday()?).
+# endif
+ }
+
+ void reset() {
+ total_ns = 0;
+ }
+
+ void begin() {
+ total_ns -= query();
+ }
+
+ void end() {
+ total_ns += query();
+ }
+
+ float sec() const {
+ return total_ns * 1e-9f;
+ }
+#else
+ static int64_t query() { return 0; }
+ void reset() { }
+ void begin() { }
+ void end() { }
+ float sec() const { return 0; }
+#endif
+};
+
+// simple API for quickly dumping values when debugging
+
+static inline void log_dump_val_worker(short v) { log("%d", v); }
+static inline void log_dump_val_worker(unsigned short v) { log("%u", v); }
+static inline void log_dump_val_worker(int v) { log("%d", v); }
+static inline void log_dump_val_worker(unsigned int v) { log("%u", v); }
+static inline void log_dump_val_worker(long int v) { log("%ld", v); }
+static inline void log_dump_val_worker(unsigned long int v) { log("%lu", v); }
+#ifndef _WIN32
+static inline void log_dump_val_worker(long long int v) { log("%lld", v); }
+static inline void log_dump_val_worker(unsigned long long int v) { log("%lld", v); }
+#endif
+static inline void log_dump_val_worker(char c) { log(c >= 32 && c < 127 ? "'%c'" : "'\\x%02x'", c); }
+static inline void log_dump_val_worker(unsigned char c) { log(c >= 32 && c < 127 ? "'%c'" : "'\\x%02x'", c); }
+static inline void log_dump_val_worker(bool v) { log("%s", v ? "true" : "false"); }
+static inline void log_dump_val_worker(double v) { log("%f", v); }
+static inline void log_dump_val_worker(char *v) { log("%s", v); }
+static inline void log_dump_val_worker(const char *v) { log("%s", v); }
+static inline void log_dump_val_worker(std::string v) { log("%s", v.c_str()); }
+static inline void log_dump_val_worker(PerformanceTimer p) { log("%f seconds", p.sec()); }
+static inline void log_dump_args_worker(const char *p YS_ATTRIBUTE(unused)) { log_assert(*p == 0); }
+void log_dump_val_worker(RTLIL::IdString v);
+void log_dump_val_worker(RTLIL::SigSpec v);
+
+template<typename K, typename T, typename OPS>
+static inline void log_dump_val_worker(dict<K, T, OPS> &v) {
+ log("{");
+ bool first = true;
+ for (auto &it : v) {
+ log(first ? " " : ", ");
+ log_dump_val_worker(it.first);
+ log(": ");
+ log_dump_val_worker(it.second);
+ first = false;
+ }
+ log(" }");
+}
+
+template<typename K, typename OPS>
+static inline void log_dump_val_worker(pool<K, OPS> &v) {
+ log("{");
+ bool first = true;
+ for (auto &it : v) {
+ log(first ? " " : ", ");
+ log_dump_val_worker(it);
+ first = false;
+ }
+ log(" }");
+}
+
+template<typename T>
+static inline void log_dump_val_worker(T *ptr) { log("%p", ptr); }
+
+template<typename T, typename ... Args>
+void log_dump_args_worker(const char *p, T first, Args ... args)
+{
+ int next_p_state = 0;
+ const char *next_p = p;
+ while (*next_p && (next_p_state != 0 || *next_p != ',')) {
+ if (*next_p == '"')
+ do {
+ next_p++;
+ while (*next_p == '\\' && *(next_p + 1))
+ next_p += 2;
+ } while (*next_p && *next_p != '"');
+ if (*next_p == '\'') {
+ next_p++;
+ if (*next_p == '\\')
+ next_p++;
+ if (*next_p)
+ next_p++;
+ }
+ if (*next_p == '(' || *next_p == '[' || *next_p == '{')
+ next_p_state++;
+ if ((*next_p == ')' || *next_p == ']' || *next_p == '}') && next_p_state > 0)
+ next_p_state--;
+ next_p++;
+ }
+ log("\n\t%.*s => ", int(next_p - p), p);
+ if (*next_p == ',')
+ next_p++;
+ while (*next_p == ' ' || *next_p == '\t' || *next_p == '\r' || *next_p == '\n')
+ next_p++;
+ log_dump_val_worker(first);
+ log_dump_args_worker(next_p, args ...);
+}
+
+#define log_dump(...) do { \
+ log("DEBUG DUMP IN %s AT %s:%d:", __PRETTY_FUNCTION__, __FILE__, __LINE__); \
+ log_dump_args_worker(#__VA_ARGS__, __VA_ARGS__); \
+ log("\n"); \
+} while (0)
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/kernel/macc.h b/kernel/macc.h
new file mode 100644
index 00000000..286ce567
--- /dev/null
+++ b/kernel/macc.h
@@ -0,0 +1,242 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef MACC_H
+#define MACC_H
+
+#include "kernel/yosys.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+struct Macc
+{
+ struct port_t {
+ RTLIL::SigSpec in_a, in_b;
+ bool is_signed, do_subtract;
+ };
+
+ std::vector<port_t> ports;
+ RTLIL::SigSpec bit_ports;
+
+ void optimize(int width)
+ {
+ std::vector<port_t> new_ports;
+ RTLIL::SigSpec new_bit_ports;
+ RTLIL::Const off(0, width);
+
+ for (auto &port : ports)
+ {
+ if (GetSize(port.in_a) == 0 && GetSize(port.in_b) == 0)
+ continue;
+
+ if (GetSize(port.in_a) < GetSize(port.in_b))
+ std::swap(port.in_a, port.in_b);
+
+ if (GetSize(port.in_a) == 1 && GetSize(port.in_b) == 0 && !port.is_signed && !port.do_subtract) {
+ bit_ports.append(port.in_a);
+ continue;
+ }
+
+ if (port.in_a.is_fully_const() && port.in_b.is_fully_const()) {
+ RTLIL::Const v = port.in_a.as_const();
+ if (GetSize(port.in_b))
+ v = const_mul(v, port.in_b.as_const(), port.is_signed, port.is_signed, width);
+ if (port.do_subtract)
+ off = const_sub(off, v, port.is_signed, port.is_signed, width);
+ else
+ off = const_add(off, v, port.is_signed, port.is_signed, width);
+ continue;
+ }
+
+ if (port.is_signed) {
+ while (GetSize(port.in_a) > 1 && port.in_a[GetSize(port.in_a)-1] == port.in_a[GetSize(port.in_a)-2])
+ port.in_a.remove(GetSize(port.in_a)-1);
+ while (GetSize(port.in_b) > 1 && port.in_b[GetSize(port.in_b)-1] == port.in_b[GetSize(port.in_b)-2])
+ port.in_b.remove(GetSize(port.in_b)-1);
+ } else {
+ while (GetSize(port.in_a) > 1 && port.in_a[GetSize(port.in_a)-1] == RTLIL::S0)
+ port.in_a.remove(GetSize(port.in_a)-1);
+ while (GetSize(port.in_b) > 1 && port.in_b[GetSize(port.in_b)-1] == RTLIL::S0)
+ port.in_b.remove(GetSize(port.in_b)-1);
+ }
+
+ new_ports.push_back(port);
+ }
+
+ for (auto &bit : bit_ports)
+ if (bit == RTLIL::S1)
+ off = const_add(off, RTLIL::Const(1, width), false, false, width);
+ else if (bit != RTLIL::S0)
+ new_bit_ports.append(bit);
+
+ if (off.as_bool()) {
+ port_t port;
+ port.in_a = off;
+ port.is_signed = false;
+ port.do_subtract = false;
+ new_ports.push_back(port);
+ }
+
+ new_ports.swap(ports);
+ bit_ports = new_bit_ports;
+ }
+
+ void from_cell(RTLIL::Cell *cell)
+ {
+ RTLIL::SigSpec port_a = cell->getPort("\\A");
+
+ ports.clear();
+ bit_ports = cell->getPort("\\B");
+
+ std::vector<RTLIL::State> config_bits = cell->getParam("\\CONFIG").bits;
+ int config_cursor = 0;
+
+#ifndef NDEBUG
+ int config_width = cell->getParam("\\CONFIG_WIDTH").as_int();
+ log_assert(GetSize(config_bits) >= config_width);
+#endif
+
+ int num_bits = 0;
+ if (config_bits[config_cursor++] == RTLIL::S1) num_bits |= 1;
+ if (config_bits[config_cursor++] == RTLIL::S1) num_bits |= 2;
+ if (config_bits[config_cursor++] == RTLIL::S1) num_bits |= 4;
+ if (config_bits[config_cursor++] == RTLIL::S1) num_bits |= 8;
+
+ int port_a_cursor = 0;
+ while (port_a_cursor < GetSize(port_a))
+ {
+ log_assert(config_cursor + 2 + 2*num_bits <= config_width);
+
+ port_t this_port;
+ this_port.is_signed = config_bits[config_cursor++] == RTLIL::S1;
+ this_port.do_subtract = config_bits[config_cursor++] == RTLIL::S1;
+
+ int size_a = 0;
+ for (int i = 0; i < num_bits; i++)
+ if (config_bits[config_cursor++] == RTLIL::S1)
+ size_a |= 1 << i;
+
+ this_port.in_a = port_a.extract(port_a_cursor, size_a);
+ port_a_cursor += size_a;
+
+ int size_b = 0;
+ for (int i = 0; i < num_bits; i++)
+ if (config_bits[config_cursor++] == RTLIL::S1)
+ size_b |= 1 << i;
+
+ this_port.in_b = port_a.extract(port_a_cursor, size_b);
+ port_a_cursor += size_b;
+
+ if (size_a || size_b)
+ ports.push_back(this_port);
+ }
+
+ log_assert(config_cursor == config_width);
+ log_assert(port_a_cursor == GetSize(port_a));
+ }
+
+ void to_cell(RTLIL::Cell *cell) const
+ {
+ RTLIL::SigSpec port_a;
+ std::vector<RTLIL::State> config_bits;
+ int max_size = 0, num_bits = 0;
+
+ for (auto &port : ports) {
+ max_size = max(max_size, GetSize(port.in_a));
+ max_size = max(max_size, GetSize(port.in_b));
+ }
+
+ while (max_size)
+ num_bits++, max_size /= 2;
+
+ log_assert(num_bits < 16);
+ config_bits.push_back(num_bits & 1 ? RTLIL::S1 : RTLIL::S0);
+ config_bits.push_back(num_bits & 2 ? RTLIL::S1 : RTLIL::S0);
+ config_bits.push_back(num_bits & 4 ? RTLIL::S1 : RTLIL::S0);
+ config_bits.push_back(num_bits & 8 ? RTLIL::S1 : RTLIL::S0);
+
+ for (auto &port : ports)
+ {
+ if (GetSize(port.in_a) == 0)
+ continue;
+
+ config_bits.push_back(port.is_signed ? RTLIL::S1 : RTLIL::S0);
+ config_bits.push_back(port.do_subtract ? RTLIL::S1 : RTLIL::S0);
+
+ int size_a = GetSize(port.in_a);
+ for (int i = 0; i < num_bits; i++)
+ config_bits.push_back(size_a & (1 << i) ? RTLIL::S1 : RTLIL::S0);
+
+ int size_b = GetSize(port.in_b);
+ for (int i = 0; i < num_bits; i++)
+ config_bits.push_back(size_b & (1 << i) ? RTLIL::S1 : RTLIL::S0);
+
+ port_a.append(port.in_a);
+ port_a.append(port.in_b);
+ }
+
+ cell->setPort("\\A", port_a);
+ cell->setPort("\\B", bit_ports);
+ cell->setParam("\\CONFIG", config_bits);
+ cell->setParam("\\CONFIG_WIDTH", GetSize(config_bits));
+ cell->setParam("\\A_WIDTH", GetSize(port_a));
+ cell->setParam("\\B_WIDTH", GetSize(bit_ports));
+ }
+
+ bool eval(RTLIL::Const &result) const
+ {
+ for (auto &bit : result.bits)
+ bit = RTLIL::S0;
+
+ for (auto &port : ports)
+ {
+ if (!port.in_a.is_fully_const() || !port.in_b.is_fully_const())
+ return false;
+
+ RTLIL::Const summand;
+ if (GetSize(port.in_b) == 0)
+ summand = const_pos(port.in_a.as_const(), port.in_b.as_const(), port.is_signed, port.is_signed, GetSize(result));
+ else
+ summand = const_mul(port.in_a.as_const(), port.in_b.as_const(), port.is_signed, port.is_signed, GetSize(result));
+
+ if (port.do_subtract)
+ result = const_sub(result, summand, port.is_signed, port.is_signed, GetSize(result));
+ else
+ result = const_add(result, summand, port.is_signed, port.is_signed, GetSize(result));
+ }
+
+ for (auto bit : bit_ports) {
+ if (bit.wire)
+ return false;
+ result = const_add(result, bit.data, false, false, GetSize(result));
+ }
+
+ return true;
+ }
+
+ Macc(RTLIL::Cell *cell = nullptr)
+ {
+ if (cell != nullptr)
+ from_cell(cell);
+ }
+};
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/kernel/modtools.h b/kernel/modtools.h
new file mode 100644
index 00000000..ffcb48d4
--- /dev/null
+++ b/kernel/modtools.h
@@ -0,0 +1,582 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef MODTOOLS_H
+#define MODTOOLS_H
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+struct ModIndex : public RTLIL::Monitor
+{
+ struct PortInfo {
+ RTLIL::Cell* cell;
+ RTLIL::IdString port;
+ int offset;
+
+ PortInfo() : cell(), port(), offset() { }
+ PortInfo(RTLIL::Cell* _c, RTLIL::IdString _p, int _o) : cell(_c), port(_p), offset(_o) { }
+
+ bool operator<(const PortInfo &other) const {
+ if (cell != other.cell)
+ return cell < other.cell;
+ if (offset != other.offset)
+ return offset < other.offset;
+ return port < other.port;
+ }
+
+ bool operator==(const PortInfo &other) const {
+ return cell == other.cell && port == other.port && offset == other.offset;
+ }
+
+ unsigned int hash() const {
+ return mkhash_add(mkhash(cell->name.hash(), port.hash()), offset);
+ }
+ };
+
+ struct SigBitInfo
+ {
+ bool is_input, is_output;
+ pool<PortInfo> ports;
+
+ SigBitInfo() : is_input(false), is_output(false) { }
+
+ bool operator==(const SigBitInfo &other) const {
+ return is_input == other.is_input && is_output == other.is_output && ports == other.ports;
+ }
+
+ void merge(const SigBitInfo &other)
+ {
+ is_input = is_input || other.is_input;
+ is_output = is_output || other.is_output;
+ ports.insert(other.ports.begin(), other.ports.end());
+ }
+ };
+
+ SigMap sigmap;
+ RTLIL::Module *module;
+ std::map<RTLIL::SigBit, SigBitInfo> database;
+ int auto_reload_counter;
+ bool auto_reload_module;
+
+ void port_add(RTLIL::Cell *cell, RTLIL::IdString port, const RTLIL::SigSpec &sig)
+ {
+ for (int i = 0; i < GetSize(sig); i++) {
+ RTLIL::SigBit bit = sigmap(sig[i]);
+ if (bit.wire)
+ database[bit].ports.insert(PortInfo(cell, port, i));
+ }
+ }
+
+ void port_del(RTLIL::Cell *cell, RTLIL::IdString port, const RTLIL::SigSpec &sig)
+ {
+ for (int i = 0; i < GetSize(sig); i++) {
+ RTLIL::SigBit bit = sigmap(sig[i]);
+ if (bit.wire)
+ database[bit].ports.erase(PortInfo(cell, port, i));
+ }
+ }
+
+ const SigBitInfo &info(RTLIL::SigBit bit)
+ {
+ return database[sigmap(bit)];
+ }
+
+ void reload_module(bool reset_sigmap = true)
+ {
+ if (reset_sigmap) {
+ sigmap.clear();
+ sigmap.set(module);
+ }
+
+ database.clear();
+ for (auto wire : module->wires())
+ if (wire->port_input || wire->port_output)
+ for (int i = 0; i < GetSize(wire); i++) {
+ RTLIL::SigBit bit = sigmap(RTLIL::SigBit(wire, i));
+ if (bit.wire && wire->port_input)
+ database[bit].is_input = true;
+ if (bit.wire && wire->port_output)
+ database[bit].is_output = true;
+ }
+ for (auto cell : module->cells())
+ for (auto &conn : cell->connections())
+ port_add(cell, conn.first, conn.second);
+
+ if (auto_reload_module) {
+ if (++auto_reload_counter > 2)
+ log_warning("Auto-reload in ModIndex -- possible performance bug!\n");
+ auto_reload_module = false;
+ }
+ }
+
+ void check()
+ {
+#ifndef NDEBUG
+ if (auto_reload_module)
+ return;
+
+ for (auto it : database)
+ log_assert(it.first == sigmap(it.first));
+
+ auto database_bak = std::move(database);
+ reload_module(false);
+
+ if (!(database == database_bak))
+ {
+ for (auto &it : database_bak)
+ if (!database.count(it.first))
+ log("ModuleIndex::check(): Only in database_bak, not database: %s\n", log_signal(it.first));
+
+ for (auto &it : database)
+ if (!database_bak.count(it.first))
+ log("ModuleIndex::check(): Only in database, not database_bak: %s\n", log_signal(it.first));
+ else if (!(it.second == database_bak.at(it.first)))
+ log("ModuleIndex::check(): Different content for database[%s].\n", log_signal(it.first));
+
+ log_assert(database == database_bak);
+ }
+#endif
+ }
+
+ virtual void notify_connect(RTLIL::Cell *cell, const RTLIL::IdString &port, const RTLIL::SigSpec &old_sig, RTLIL::SigSpec &sig) YS_OVERRIDE
+ {
+ log_assert(module == cell->module);
+
+ if (auto_reload_module)
+ return;
+
+ port_del(cell, port, old_sig);
+ port_add(cell, port, sig);
+ }
+
+ virtual void notify_connect(RTLIL::Module *mod YS_ATTRIBUTE(unused), const RTLIL::SigSig &sigsig) YS_OVERRIDE
+ {
+ log_assert(module == mod);
+
+ if (auto_reload_module)
+ return;
+
+ for (int i = 0; i < GetSize(sigsig.first); i++)
+ {
+ RTLIL::SigBit lhs = sigmap(sigsig.first[i]);
+ RTLIL::SigBit rhs = sigmap(sigsig.second[i]);
+ bool has_lhs = database.count(lhs) != 0;
+ bool has_rhs = database.count(rhs) != 0;
+
+ if (!has_lhs && !has_rhs) {
+ sigmap.add(lhs, rhs);
+ } else
+ if (!has_rhs) {
+ SigBitInfo new_info = database.at(lhs);
+ database.erase(lhs);
+ sigmap.add(lhs, rhs);
+ lhs = sigmap(lhs);
+ if (lhs.wire)
+ database[lhs] = new_info;
+ } else
+ if (!has_lhs) {
+ SigBitInfo new_info = database.at(rhs);
+ database.erase(rhs);
+ sigmap.add(lhs, rhs);
+ rhs = sigmap(rhs);
+ if (rhs.wire)
+ database[rhs] = new_info;
+ } else {
+ SigBitInfo new_info = database.at(lhs);
+ new_info.merge(database.at(rhs));
+ database.erase(lhs);
+ database.erase(rhs);
+ sigmap.add(lhs, rhs);
+ rhs = sigmap(rhs);
+ if (rhs.wire)
+ database[rhs] = new_info;
+ }
+ }
+ }
+
+ virtual void notify_connect(RTLIL::Module *mod YS_ATTRIBUTE(unused), const std::vector<RTLIL::SigSig>&) YS_OVERRIDE
+ {
+ log_assert(module == mod);
+ auto_reload_module = true;
+ }
+
+ virtual void notify_blackout(RTLIL::Module *mod YS_ATTRIBUTE(unused)) YS_OVERRIDE
+ {
+ log_assert(module == mod);
+ auto_reload_module = true;
+ }
+
+ ModIndex(RTLIL::Module *_m) : sigmap(_m), module(_m)
+ {
+ auto_reload_counter = 0;
+ auto_reload_module = true;
+ module->monitors.insert(this);
+ }
+
+ ~ModIndex()
+ {
+ module->monitors.erase(this);
+ }
+
+ SigBitInfo *query(RTLIL::SigBit bit)
+ {
+ if (auto_reload_module)
+ reload_module();
+
+ auto it = database.find(sigmap(bit));
+ if (it == database.end())
+ return nullptr;
+ else
+ return &it->second;
+ }
+
+ bool query_is_input(RTLIL::SigBit bit)
+ {
+ const SigBitInfo *info = query(bit);
+ if (info == nullptr)
+ return false;
+ return info->is_input;
+ }
+
+ bool query_is_output(RTLIL::SigBit bit)
+ {
+ const SigBitInfo *info = query(bit);
+ if (info == nullptr)
+ return false;
+ return info->is_output;
+ }
+
+ pool<PortInfo> &query_ports(RTLIL::SigBit bit)
+ {
+ static pool<PortInfo> empty_result_set;
+ SigBitInfo *info = query(bit);
+ if (info == nullptr)
+ return empty_result_set;
+ return info->ports;
+ }
+
+ void dump_db()
+ {
+ log("--- ModIndex Dump ---\n");
+
+ if (auto_reload_module) {
+ log("AUTO-RELOAD\n");
+ reload_module();
+ }
+
+ for (auto &it : database) {
+ log("BIT %s:\n", log_signal(it.first));
+ if (it.second.is_input)
+ log(" PRIMARY INPUT\n");
+ if (it.second.is_output)
+ log(" PRIMARY OUTPUT\n");
+ for (auto &port : it.second.ports)
+ log(" PORT: %s.%s[%d] (%s)\n", log_id(port.cell),
+ log_id(port.port), port.offset, log_id(port.cell->type));
+ }
+ }
+};
+
+struct ModWalker
+{
+ struct PortBit
+ {
+ RTLIL::Cell *cell;
+ RTLIL::IdString port;
+ int offset;
+
+ bool operator<(const PortBit &other) const {
+ if (cell != other.cell)
+ return cell < other.cell;
+ if (port != other.port)
+ return port < other.port;
+ return offset < other.offset;
+ }
+
+ bool operator==(const PortBit &other) const {
+ return cell == other.cell && port == other.port && offset == other.offset;
+ }
+
+ unsigned int hash() const {
+ return mkhash_add(mkhash(cell->name.hash(), port.hash()), offset);
+ }
+ };
+
+ RTLIL::Design *design;
+ RTLIL::Module *module;
+
+ CellTypes ct;
+ SigMap sigmap;
+
+ dict<RTLIL::SigBit, pool<PortBit>> signal_drivers;
+ dict<RTLIL::SigBit, pool<PortBit>> signal_consumers;
+ pool<RTLIL::SigBit> signal_inputs, signal_outputs;
+
+ dict<RTLIL::Cell*, pool<RTLIL::SigBit>> cell_outputs, cell_inputs;
+
+ void add_wire(RTLIL::Wire *wire)
+ {
+ if (wire->port_input) {
+ std::vector<RTLIL::SigBit> bits = sigmap(wire);
+ for (auto bit : bits)
+ if (bit.wire != NULL)
+ signal_inputs.insert(bit);
+ }
+
+ if (wire->port_output) {
+ std::vector<RTLIL::SigBit> bits = sigmap(wire);
+ for (auto bit : bits)
+ if (bit.wire != NULL)
+ signal_outputs.insert(bit);
+ }
+ }
+
+ void add_cell_port(RTLIL::Cell *cell, RTLIL::IdString port, std::vector<RTLIL::SigBit> bits, bool is_output, bool is_input)
+ {
+ for (int i = 0; i < int(bits.size()); i++)
+ if (bits[i].wire != NULL) {
+ PortBit pbit = { cell, port, i };
+ if (is_output) {
+ signal_drivers[bits[i]].insert(pbit);
+ cell_outputs[cell].insert(bits[i]);
+ }
+ if (is_input) {
+ signal_consumers[bits[i]].insert(pbit);
+ cell_inputs[cell].insert(bits[i]);
+ }
+ }
+ }
+
+ void add_cell(RTLIL::Cell *cell)
+ {
+ if (ct.cell_known(cell->type)) {
+ for (auto &conn : cell->connections())
+ add_cell_port(cell, conn.first, sigmap(conn.second),
+ ct.cell_output(cell->type, conn.first),
+ ct.cell_input(cell->type, conn.first));
+ } else {
+ for (auto &conn : cell->connections())
+ add_cell_port(cell, conn.first, sigmap(conn.second), true, true);
+ }
+ }
+
+ ModWalker() : design(NULL), module(NULL)
+ {
+ }
+
+ ModWalker(RTLIL::Design *design, RTLIL::Module *module, CellTypes *filter_ct = NULL)
+ {
+ setup(design, module, filter_ct);
+ }
+
+ void setup(RTLIL::Design *design, RTLIL::Module *module, CellTypes *filter_ct = NULL)
+ {
+ this->design = design;
+ this->module = module;
+
+ ct.clear();
+ ct.setup(design);
+ sigmap.set(module);
+
+ signal_drivers.clear();
+ signal_consumers.clear();
+ signal_inputs.clear();
+ signal_outputs.clear();
+
+ for (auto &it : module->wires_)
+ add_wire(it.second);
+ for (auto &it : module->cells_)
+ if (filter_ct == NULL || filter_ct->cell_known(it.second->type))
+ add_cell(it.second);
+ }
+
+ // get_* methods -- single RTLIL::SigBit
+
+ template<typename T>
+ inline bool get_drivers(pool<PortBit> &result, RTLIL::SigBit bit) const
+ {
+ bool found = false;
+ if (signal_drivers.count(bit)) {
+ const pool<PortBit> &r = signal_drivers.at(bit);
+ result.insert(r.begin(), r.end());
+ found = true;
+ }
+ return found;
+ }
+
+ template<typename T>
+ inline bool get_consumers(pool<PortBit> &result, RTLIL::SigBit bit) const
+ {
+ bool found = false;
+ if (signal_consumers.count(bit)) {
+ const pool<PortBit> &r = signal_consumers.at(bit);
+ result.insert(r.begin(), r.end());
+ found = true;
+ }
+ return found;
+ }
+
+ template<typename T>
+ inline bool get_inputs(pool<RTLIL::SigBit> &result, RTLIL::SigBit bit) const
+ {
+ bool found = false;
+ if (signal_inputs.count(bit))
+ result.insert(bit), found = true;
+ return found;
+ }
+
+ template<typename T>
+ inline bool get_outputs(pool<RTLIL::SigBit> &result, RTLIL::SigBit bit) const
+ {
+ bool found = false;
+ if (signal_outputs.count(bit))
+ result.insert(bit), found = true;
+ return found;
+ }
+
+ // get_* methods -- container of RTLIL::SigBit's (always by reference)
+
+ template<typename T>
+ inline bool get_drivers(pool<PortBit> &result, const T &bits) const
+ {
+ bool found = false;
+ for (RTLIL::SigBit bit : bits)
+ if (signal_drivers.count(bit)) {
+ const pool<PortBit> &r = signal_drivers.at(bit);
+ result.insert(r.begin(), r.end());
+ found = true;
+ }
+ return found;
+ }
+
+ template<typename T>
+ inline bool get_consumers(pool<PortBit> &result, const T &bits) const
+ {
+ bool found = false;
+ for (RTLIL::SigBit bit : bits)
+ if (signal_consumers.count(bit)) {
+ const pool<PortBit> &r = signal_consumers.at(bit);
+ result.insert(r.begin(), r.end());
+ found = true;
+ }
+ return found;
+ }
+
+ template<typename T>
+ inline bool get_inputs(pool<RTLIL::SigBit> &result, const T &bits) const
+ {
+ bool found = false;
+ for (RTLIL::SigBit bit : bits)
+ if (signal_inputs.count(bit))
+ result.insert(bit), found = true;
+ return found;
+ }
+
+ template<typename T>
+ inline bool get_outputs(pool<RTLIL::SigBit> &result, const T &bits) const
+ {
+ bool found = false;
+ for (RTLIL::SigBit bit : bits)
+ if (signal_outputs.count(bit))
+ result.insert(bit), found = true;
+ return found;
+ }
+
+ // get_* methods -- call by RTLIL::SigSpec (always by value)
+
+ bool get_drivers(pool<PortBit> &result, RTLIL::SigSpec signal) const
+ {
+ std::vector<RTLIL::SigBit> bits = sigmap(signal);
+ return get_drivers(result, bits);
+ }
+
+ bool get_consumers(pool<PortBit> &result, RTLIL::SigSpec signal) const
+ {
+ std::vector<RTLIL::SigBit> bits = sigmap(signal);
+ return get_consumers(result, bits);
+ }
+
+ bool get_inputs(pool<RTLIL::SigBit> &result, RTLIL::SigSpec signal) const
+ {
+ std::vector<RTLIL::SigBit> bits = sigmap(signal);
+ return get_inputs(result, bits);
+ }
+
+ bool get_outputs(pool<RTLIL::SigBit> &result, RTLIL::SigSpec signal) const
+ {
+ std::vector<RTLIL::SigBit> bits = sigmap(signal);
+ return get_outputs(result, bits);
+ }
+
+ // has_* methods -- call by reference
+
+ template<typename T>
+ inline bool has_drivers(const T &sig) const {
+ pool<PortBit> result;
+ return get_drivers(result, sig);
+ }
+
+ template<typename T>
+ inline bool has_consumers(const T &sig) const {
+ pool<PortBit> result;
+ return get_consumers(result, sig);
+ }
+
+ template<typename T>
+ inline bool has_inputs(const T &sig) const {
+ pool<RTLIL::SigBit> result;
+ return get_inputs(result, sig);
+ }
+
+ template<typename T>
+ inline bool has_outputs(const T &sig) const {
+ pool<RTLIL::SigBit> result;
+ return get_outputs(result, sig);
+ }
+
+ // has_* methods -- call by value
+
+ inline bool has_drivers(RTLIL::SigSpec sig) const {
+ pool<PortBit> result;
+ return get_drivers(result, sig);
+ }
+
+ inline bool has_consumers(RTLIL::SigSpec sig) const {
+ pool<PortBit> result;
+ return get_consumers(result, sig);
+ }
+
+ inline bool has_inputs(RTLIL::SigSpec sig) const {
+ pool<RTLIL::SigBit> result;
+ return get_inputs(result, sig);
+ }
+
+ inline bool has_outputs(RTLIL::SigSpec sig) const {
+ pool<RTLIL::SigBit> result;
+ return get_outputs(result, sig);
+ }
+};
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/kernel/register.cc b/kernel/register.cc
new file mode 100644
index 00000000..7a1d0b44
--- /dev/null
+++ b/kernel/register.cc
@@ -0,0 +1,813 @@
+/*
+ * 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/satgen.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+YOSYS_NAMESPACE_BEGIN
+
+#define MAX_REG_COUNT 1000
+
+bool echo_mode = false;
+Pass *first_queued_pass;
+Pass *current_pass;
+
+std::map<std::string, Frontend*> frontend_register;
+std::map<std::string, Pass*> pass_register;
+std::map<std::string, Backend*> backend_register;
+
+std::vector<std::string> Frontend::next_args;
+
+Pass::Pass(std::string name, std::string short_help) : pass_name(name), short_help(short_help)
+{
+ next_queued_pass = first_queued_pass;
+ first_queued_pass = this;
+ call_counter = 0;
+ runtime_ns = 0;
+}
+
+void Pass::run_register()
+{
+ log_assert(pass_register.count(pass_name) == 0);
+ pass_register[pass_name] = this;
+}
+
+void Pass::init_register()
+{
+ while (first_queued_pass) {
+ first_queued_pass->run_register();
+ first_queued_pass = first_queued_pass->next_queued_pass;
+ }
+}
+
+void Pass::done_register()
+{
+ frontend_register.clear();
+ pass_register.clear();
+ backend_register.clear();
+ log_assert(first_queued_pass == NULL);
+}
+
+Pass::~Pass()
+{
+}
+
+Pass::pre_post_exec_state_t Pass::pre_execute()
+{
+ pre_post_exec_state_t state;
+ call_counter++;
+ state.begin_ns = PerformanceTimer::query();
+ state.parent_pass = current_pass;
+ current_pass = this;
+ clear_flags();
+ return state;
+}
+
+void Pass::post_execute(Pass::pre_post_exec_state_t state)
+{
+ int64_t time_ns = PerformanceTimer::query() - state.begin_ns;
+ runtime_ns += time_ns;
+ current_pass = state.parent_pass;
+ if (current_pass)
+ current_pass->runtime_ns -= time_ns;
+}
+
+void Pass::help()
+{
+ log("\n");
+ log("No help message for command `%s'.\n", pass_name.c_str());
+ log("\n");
+}
+
+void Pass::clear_flags()
+{
+}
+
+void Pass::cmd_log_args(const std::vector<std::string> &args)
+{
+ if (args.size() <= 1)
+ return;
+ log("Full command line:");
+ for (size_t i = 0; i < args.size(); i++)
+ log(" %s", args[i].c_str());
+ log("\n");
+}
+
+void Pass::cmd_error(const std::vector<std::string> &args, size_t argidx, std::string msg)
+{
+ std::string command_text;
+ int error_pos = 0;
+
+ for (size_t i = 0; i < args.size(); i++) {
+ if (i < argidx)
+ error_pos += args[i].size() + 1;
+ command_text = command_text + (command_text.empty() ? "" : " ") + args[i];
+ }
+
+ log("\nSyntax error in command `%s':\n", command_text.c_str());
+ help();
+
+ log_cmd_error("Command syntax error: %s\n> %s\n> %*s^\n",
+ msg.c_str(), command_text.c_str(), error_pos, "");
+}
+
+void Pass::extra_args(std::vector<std::string> args, size_t argidx, RTLIL::Design *design, bool select)
+{
+ for (; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+
+ if (arg.substr(0, 1) == "-")
+ cmd_error(args, argidx, "Unknown option or option in arguments.");
+
+ if (!select)
+ cmd_error(args, argidx, "Extra argument.");
+
+ handle_extra_select_args(this, args, argidx, args.size(), design);
+ break;
+ }
+ // cmd_log_args(args);
+}
+
+void Pass::call(RTLIL::Design *design, std::string command)
+{
+ std::vector<std::string> args;
+
+ std::string cmd_buf = command;
+ std::string tok = next_token(cmd_buf, " \t\r\n", true);
+
+ if (tok.empty())
+ return;
+
+ if (tok[0] == '!') {
+ cmd_buf = command.substr(command.find('!') + 1);
+ while (!cmd_buf.empty() && (cmd_buf.back() == ' ' || cmd_buf.back() == '\t' ||
+ cmd_buf.back() == '\r' || cmd_buf.back() == '\n'))
+ cmd_buf.resize(cmd_buf.size()-1);
+ log_header(design, "Shell command: %s\n", cmd_buf.c_str());
+ int retCode = run_command(cmd_buf);
+ if (retCode != 0)
+ log_cmd_error("Shell command returned error code %d.\n", retCode);
+ return;
+ }
+
+ while (!tok.empty()) {
+ if (tok == "#") {
+ int stop;
+ for (stop = 0; stop < GetSize(cmd_buf); stop++)
+ if (cmd_buf[stop] == '\r' || cmd_buf[stop] == '\n')
+ break;
+ cmd_buf = cmd_buf.substr(stop);
+ } else
+ if (tok.back() == ';') {
+ int num_semikolon = 0;
+ while (!tok.empty() && tok.back() == ';')
+ tok.resize(tok.size()-1), num_semikolon++;
+ if (!tok.empty())
+ args.push_back(tok);
+ call(design, args);
+ args.clear();
+ if (num_semikolon == 2)
+ call(design, "clean");
+ if (num_semikolon == 3)
+ call(design, "clean -purge");
+ } else
+ args.push_back(tok);
+ bool found_nl = false;
+ for (auto c : cmd_buf) {
+ if (c == ' ' || c == '\t')
+ continue;
+ if (c == '\r' || c == '\n')
+ found_nl = true;
+ break;
+ }
+ if (found_nl) {
+ call(design, args);
+ args.clear();
+ }
+ tok = next_token(cmd_buf, " \t\r\n", true);
+ }
+
+ call(design, args);
+}
+
+void Pass::call(RTLIL::Design *design, std::vector<std::string> args)
+{
+ if (args.size() == 0 || args[0][0] == '#' || args[0][0] == ':')
+ return;
+
+ if (echo_mode) {
+ log("%s", create_prompt(design, 0));
+ for (size_t i = 0; i < args.size(); i++)
+ log("%s%s", i ? " " : "", args[i].c_str());
+ log("\n");
+ }
+
+ if (pass_register.count(args[0]) == 0)
+ log_cmd_error("No such command: %s (type 'help' for a command overview)\n", args[0].c_str());
+
+ size_t orig_sel_stack_pos = design->selection_stack.size();
+ auto state = pass_register[args[0]]->pre_execute();
+ pass_register[args[0]]->execute(args, design);
+ pass_register[args[0]]->post_execute(state);
+ while (design->selection_stack.size() > orig_sel_stack_pos)
+ design->selection_stack.pop_back();
+
+ design->check();
+}
+
+void Pass::call_on_selection(RTLIL::Design *design, const RTLIL::Selection &selection, std::string command)
+{
+ std::string backup_selected_active_module = design->selected_active_module;
+ design->selected_active_module.clear();
+ design->selection_stack.push_back(selection);
+
+ Pass::call(design, command);
+
+ design->selection_stack.pop_back();
+ design->selected_active_module = backup_selected_active_module;
+}
+
+void Pass::call_on_selection(RTLIL::Design *design, const RTLIL::Selection &selection, std::vector<std::string> args)
+{
+ std::string backup_selected_active_module = design->selected_active_module;
+ design->selected_active_module.clear();
+ design->selection_stack.push_back(selection);
+
+ Pass::call(design, args);
+
+ design->selection_stack.pop_back();
+ design->selected_active_module = backup_selected_active_module;
+}
+
+void Pass::call_on_module(RTLIL::Design *design, RTLIL::Module *module, std::string command)
+{
+ std::string backup_selected_active_module = design->selected_active_module;
+ design->selected_active_module = module->name.str();
+ design->selection_stack.push_back(RTLIL::Selection(false));
+ design->selection_stack.back().select(module);
+
+ Pass::call(design, command);
+
+ design->selection_stack.pop_back();
+ design->selected_active_module = backup_selected_active_module;
+}
+
+void Pass::call_on_module(RTLIL::Design *design, RTLIL::Module *module, std::vector<std::string> args)
+{
+ std::string backup_selected_active_module = design->selected_active_module;
+ design->selected_active_module = module->name.str();
+ design->selection_stack.push_back(RTLIL::Selection(false));
+ design->selection_stack.back().select(module);
+
+ Pass::call(design, args);
+
+ design->selection_stack.pop_back();
+ design->selected_active_module = backup_selected_active_module;
+}
+
+bool ScriptPass::check_label(std::string label, std::string info)
+{
+ if (active_design == nullptr) {
+ log("\n");
+ if (info.empty())
+ log(" %s:\n", label.c_str());
+ else
+ log(" %s: %s\n", label.c_str(), info.c_str());
+ return true;
+ } else {
+ if (!active_run_from.empty() && active_run_from == active_run_to) {
+ block_active = (label == active_run_from);
+ } else {
+ if (label == active_run_from)
+ block_active = true;
+ if (label == active_run_to)
+ block_active = false;
+ }
+ return block_active;
+ }
+}
+
+void ScriptPass::run(std::string command, std::string info)
+{
+ if (active_design == nullptr) {
+ if (info.empty())
+ log(" %s\n", command.c_str());
+ else
+ log(" %s %s\n", command.c_str(), info.c_str());
+ } else
+ Pass::call(active_design, command);
+}
+
+void ScriptPass::run_script(RTLIL::Design *design, std::string run_from, std::string run_to)
+{
+ help_mode = false;
+ active_design = design;
+ block_active = run_from.empty();
+ active_run_from = run_from;
+ active_run_to = run_to;
+ script();
+}
+
+void ScriptPass::help_script()
+{
+ clear_flags();
+ help_mode = true;
+ active_design = nullptr;
+ block_active = true;
+ active_run_from.clear();
+ active_run_to.clear();
+ script();
+}
+
+Frontend::Frontend(std::string name, std::string short_help) :
+ Pass(name.rfind("=", 0) == 0 ? name.substr(1) : "read_" + name, short_help),
+ frontend_name(name.rfind("=", 0) == 0 ? name.substr(1) : name)
+{
+}
+
+void Frontend::run_register()
+{
+ log_assert(pass_register.count(pass_name) == 0);
+ pass_register[pass_name] = this;
+
+ log_assert(frontend_register.count(frontend_name) == 0);
+ frontend_register[frontend_name] = this;
+}
+
+Frontend::~Frontend()
+{
+}
+
+void Frontend::execute(std::vector<std::string> args, RTLIL::Design *design)
+{
+ log_assert(next_args.empty());
+ do {
+ std::istream *f = NULL;
+ next_args.clear();
+ auto state = pre_execute();
+ execute(f, std::string(), args, design);
+ post_execute(state);
+ args = next_args;
+ delete f;
+ } while (!args.empty());
+}
+
+FILE *Frontend::current_script_file = NULL;
+std::string Frontend::last_here_document;
+
+void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx)
+{
+ bool called_with_fp = f != NULL;
+
+ next_args.clear();
+
+ if (argidx < args.size())
+ {
+ std::string arg = args[argidx];
+
+ if (arg.substr(0, 1) == "-")
+ cmd_error(args, argidx, "Unknown option or option in arguments.");
+ if (f != NULL)
+ cmd_error(args, argidx, "Extra filename argument in direct file mode.");
+
+ filename = arg;
+ if (filename == "<<" && argidx+1 < args.size())
+ filename += args[++argidx];
+ if (filename.substr(0, 2) == "<<") {
+ if (Frontend::current_script_file == NULL)
+ log_error("Unexpected here document '%s' outside of script!\n", filename.c_str());
+ if (filename.size() <= 2)
+ log_error("Missing EOT marker in here document!\n");
+ std::string eot_marker = filename.substr(2);
+ last_here_document.clear();
+ while (1) {
+ std::string buffer;
+ char block[4096];
+ while (1) {
+ if (fgets(block, 4096, Frontend::current_script_file) == NULL)
+ log_error("Unexpected end of file in here document '%s'!\n", filename.c_str());
+ buffer += block;
+ if (buffer.size() > 0 && (buffer[buffer.size() - 1] == '\n' || buffer[buffer.size() - 1] == '\r'))
+ break;
+ }
+ size_t indent = buffer.find_first_not_of(" \t\r\n");
+ if (indent != std::string::npos && buffer.substr(indent, eot_marker.size()) == eot_marker)
+ break;
+ last_here_document += buffer;
+ }
+ f = new std::istringstream(last_here_document);
+ } else {
+ rewrite_filename(filename);
+ vector<string> filenames = glob_filename(filename);
+ filename = filenames.front();
+ if (GetSize(filenames) > 1) {
+ next_args.insert(next_args.end(), args.begin(), args.begin()+argidx);
+ next_args.insert(next_args.end(), filenames.begin()+1, filenames.end());
+ }
+ std::ifstream *ff = new std::ifstream;
+ ff->open(filename.c_str());
+ if (ff->fail())
+ delete ff;
+ else
+ f = ff;
+ }
+ if (f == NULL)
+ log_cmd_error("Can't open input file `%s' for reading: %s\n", filename.c_str(), strerror(errno));
+
+ for (size_t i = argidx+1; i < args.size(); i++)
+ if (args[i].substr(0, 1) == "-")
+ cmd_error(args, i, "Found option, expected arguments.");
+
+ if (argidx+1 < args.size()) {
+ if (next_args.empty())
+ next_args.insert(next_args.end(), args.begin(), args.begin()+argidx);
+ next_args.insert(next_args.end(), args.begin()+argidx+1, args.end());
+ args.erase(args.begin()+argidx+1, args.end());
+ }
+ }
+
+ if (f == NULL)
+ cmd_error(args, argidx, "No filename given.");
+
+ if (called_with_fp)
+ args.push_back(filename);
+ args[0] = pass_name;
+ // cmd_log_args(args);
+}
+
+void Frontend::frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::string command)
+{
+ std::vector<std::string> args;
+ char *s = strdup(command.c_str());
+ for (char *p = strtok(s, " \t\r\n"); p; p = strtok(NULL, " \t\r\n"))
+ args.push_back(p);
+ free(s);
+ frontend_call(design, f, filename, args);
+}
+
+void Frontend::frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::vector<std::string> args)
+{
+ if (args.size() == 0)
+ return;
+ if (frontend_register.count(args[0]) == 0)
+ log_cmd_error("No such frontend: %s\n", args[0].c_str());
+
+ if (f != NULL) {
+ auto state = frontend_register[args[0]]->pre_execute();
+ frontend_register[args[0]]->execute(f, filename, args, design);
+ frontend_register[args[0]]->post_execute(state);
+ } else if (filename == "-") {
+ std::istream *f_cin = &std::cin;
+ auto state = frontend_register[args[0]]->pre_execute();
+ frontend_register[args[0]]->execute(f_cin, "<stdin>", args, design);
+ frontend_register[args[0]]->post_execute(state);
+ } else {
+ if (!filename.empty())
+ args.push_back(filename);
+ frontend_register[args[0]]->execute(args, design);
+ }
+
+ design->check();
+}
+
+Backend::Backend(std::string name, std::string short_help) :
+ Pass(name.rfind("=", 0) == 0 ? name.substr(1) : "write_" + name, short_help),
+ backend_name(name.rfind("=", 0) == 0 ? name.substr(1) : name)
+{
+}
+
+void Backend::run_register()
+{
+ log_assert(pass_register.count(pass_name) == 0);
+ pass_register[pass_name] = this;
+
+ log_assert(backend_register.count(backend_name) == 0);
+ backend_register[backend_name] = this;
+}
+
+Backend::~Backend()
+{
+}
+
+void Backend::execute(std::vector<std::string> args, RTLIL::Design *design)
+{
+ std::ostream *f = NULL;
+ auto state = pre_execute();
+ execute(f, std::string(), args, design);
+ post_execute(state);
+ if (f != &std::cout)
+ delete f;
+}
+
+void Backend::extra_args(std::ostream *&f, std::string &filename, std::vector<std::string> args, size_t argidx)
+{
+ bool called_with_fp = f != NULL;
+
+ for (; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+
+ if (arg.substr(0, 1) == "-" && arg != "-")
+ cmd_error(args, argidx, "Unknown option or option in arguments.");
+ if (f != NULL)
+ cmd_error(args, argidx, "Extra filename argument in direct file mode.");
+
+ if (arg == "-") {
+ filename = "<stdout>";
+ f = &std::cout;
+ continue;
+ }
+
+ filename = arg;
+ std::ofstream *ff = new std::ofstream;
+ ff->open(filename.c_str(), std::ofstream::trunc);
+ if (ff->fail()) {
+ delete ff;
+ log_cmd_error("Can't open output file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
+ }
+ f = ff;
+ }
+
+ if (called_with_fp)
+ args.push_back(filename);
+ args[0] = pass_name;
+ // cmd_log_args(args);
+
+ if (f == NULL) {
+ filename = "<stdout>";
+ f = &std::cout;
+ }
+}
+
+void Backend::backend_call(RTLIL::Design *design, std::ostream *f, std::string filename, std::string command)
+{
+ std::vector<std::string> args;
+ char *s = strdup(command.c_str());
+ for (char *p = strtok(s, " \t\r\n"); p; p = strtok(NULL, " \t\r\n"))
+ args.push_back(p);
+ free(s);
+ backend_call(design, f, filename, args);
+}
+
+void Backend::backend_call(RTLIL::Design *design, std::ostream *f, std::string filename, std::vector<std::string> args)
+{
+ if (args.size() == 0)
+ return;
+ if (backend_register.count(args[0]) == 0)
+ log_cmd_error("No such backend: %s\n", args[0].c_str());
+
+ size_t orig_sel_stack_pos = design->selection_stack.size();
+
+ if (f != NULL) {
+ auto state = backend_register[args[0]]->pre_execute();
+ backend_register[args[0]]->execute(f, filename, args, design);
+ backend_register[args[0]]->post_execute(state);
+ } else if (filename == "-") {
+ std::ostream *f_cout = &std::cout;
+ auto state = backend_register[args[0]]->pre_execute();
+ backend_register[args[0]]->execute(f_cout, "<stdout>", args, design);
+ backend_register[args[0]]->post_execute(state);
+ } else {
+ if (!filename.empty())
+ args.push_back(filename);
+ backend_register[args[0]]->execute(args, design);
+ }
+
+ while (design->selection_stack.size() > orig_sel_stack_pos)
+ design->selection_stack.pop_back();
+
+ design->check();
+}
+
+static struct CellHelpMessages {
+ dict<string, string> cell_help, cell_code;
+ CellHelpMessages() {
+#include "techlibs/common/simlib_help.inc"
+#include "techlibs/common/simcells_help.inc"
+ cell_help.sort();
+ cell_code.sort();
+ }
+} cell_help_messages;
+
+struct HelpPass : public Pass {
+ HelpPass() : Pass("help", "display help messages") { }
+ virtual void help()
+ {
+ log("\n");
+ log(" help ................ list all commands\n");
+ log(" help <command> ...... print help message for given command\n");
+ log(" help -all ........... print complete command reference\n");
+ log("\n");
+ log(" help -cells .......... list all cell types\n");
+ log(" help <celltype> ..... print help message for given cell type\n");
+ log(" help <celltype>+ .... print verilog code for given cell type\n");
+ log("\n");
+ }
+ void escape_tex(std::string &tex)
+ {
+ for (size_t pos = 0; (pos = tex.find('_', pos)) != std::string::npos; pos += 2)
+ tex.replace(pos, 1, "\\_");
+ for (size_t pos = 0; (pos = tex.find('$', pos)) != std::string::npos; pos += 2)
+ tex.replace(pos, 1, "\\$");
+ }
+ void write_tex(FILE *f, std::string cmd, std::string title, std::string text)
+ {
+ size_t begin = text.find_first_not_of("\n"), end = text.find_last_not_of("\n");
+ if (begin != std::string::npos && end != std::string::npos && begin < end)
+ text = text.substr(begin, end-begin+1);
+ std::string cmd_unescaped = cmd;
+ escape_tex(cmd);
+ escape_tex(title);
+ fprintf(f, "\\section{%s -- %s}\n", cmd.c_str(), title.c_str());
+ fprintf(f, "\\label{cmd:%s}\n", cmd_unescaped.c_str());
+ fprintf(f, "\\begin{lstlisting}[numbers=left,frame=single]\n");
+ fprintf(f, "%s\n\\end{lstlisting}\n\n", text.c_str());
+ }
+ void escape_html(std::string &html)
+ {
+ size_t pos = 0;
+ while ((pos = html.find_first_of("<>&", pos)) != std::string::npos)
+ switch (html[pos]) {
+ case '<':
+ html.replace(pos, 1, "&lt;");
+ pos += 4;
+ break;
+ case '>':
+ html.replace(pos, 1, "&gt;");
+ pos += 4;
+ break;
+ case '&':
+ html.replace(pos, 1, "&amp;");
+ pos += 5;
+ break;
+ }
+ }
+ void write_html(FILE *idxf, std::string cmd, std::string title, std::string text)
+ {
+ FILE *f = fopen(stringf("cmd_%s.in", cmd.c_str()).c_str(), "wt");
+ fprintf(idxf, "<li><a href=\"cmd_%s.html\"> ", cmd.c_str());
+
+ escape_html(cmd);
+ escape_html(title);
+ escape_html(text);
+
+ fprintf(idxf, "%s</a> <span>%s</span></a>\n", cmd.c_str(), title.c_str());
+
+ fprintf(f, "@cmd_header %s@\n", cmd.c_str());
+ fprintf(f, "<h1>%s - %s</h1>\n", cmd.c_str(), title.c_str());
+ fprintf(f, "<pre>%s</pre>\n", text.c_str());
+ fprintf(f, "@footer@\n");
+
+ fclose(f);
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design*)
+ {
+ if (args.size() == 1) {
+ log("\n");
+ for (auto &it : pass_register)
+ log(" %-20s %s\n", it.first.c_str(), it.second->short_help.c_str());
+ log("\n");
+ log("Type 'help <command>' for more information on a command.\n");
+ log("Type 'help -cells' for a list of all cell types.\n");
+ log("\n");
+ return;
+ }
+
+ if (args.size() == 2) {
+ if (args[1] == "-all") {
+ for (auto &it : pass_register) {
+ log("\n\n");
+ log("%s -- %s\n", it.first.c_str(), it.second->short_help.c_str());
+ for (size_t i = 0; i < it.first.size() + it.second->short_help.size() + 6; i++)
+ log("=");
+ log("\n");
+ it.second->help();
+ }
+ }
+ else if (args[1] == "-cells") {
+ log("\n");
+ for (auto &it : cell_help_messages.cell_help) {
+ string line = split_tokens(it.second, "\n").at(0);
+ string cell_name = next_token(line);
+ log(" %-15s %s\n", cell_name.c_str(), line.c_str());
+ }
+ log("\n");
+ log("Type 'help <cell_type>' for more information on a cell type.\n");
+ log("\n");
+ return;
+ }
+ // this option is undocumented as it is for internal use only
+ else if (args[1] == "-write-tex-command-reference-manual") {
+ FILE *f = fopen("command-reference-manual.tex", "wt");
+ fprintf(f, "%% Generated using the yosys 'help -write-tex-command-reference-manual' command.\n\n");
+ for (auto &it : pass_register) {
+ std::ostringstream buf;
+ log_streams.push_back(&buf);
+ it.second->help();
+ log_streams.pop_back();
+ write_tex(f, it.first, it.second->short_help, buf.str());
+ }
+ fclose(f);
+ }
+ // this option is undocumented as it is for internal use only
+ else if (args[1] == "-write-web-command-reference-manual") {
+ FILE *f = fopen("templates/cmd_index.in", "wt");
+ for (auto &it : pass_register) {
+ std::ostringstream buf;
+ log_streams.push_back(&buf);
+ it.second->help();
+ log_streams.pop_back();
+ write_html(f, it.first, it.second->short_help, buf.str());
+ }
+ fclose(f);
+ }
+ else if (pass_register.count(args[1])) {
+ pass_register.at(args[1])->help();
+ }
+ else if (cell_help_messages.cell_help.count(args[1])) {
+ log("%s", cell_help_messages.cell_help.at(args[1]).c_str());
+ log("Run 'help %s+' to display the Verilog model for this cell type.\n", args[1].c_str());
+ log("\n");
+ }
+ else if (cell_help_messages.cell_code.count(args[1])) {
+ log("\n");
+ log("%s", cell_help_messages.cell_code.at(args[1]).c_str());
+ }
+ else
+ log("No such command or cell type: %s\n", args[1].c_str());
+ return;
+ }
+
+ help();
+ }
+} HelpPass;
+
+struct EchoPass : public Pass {
+ EchoPass() : Pass("echo", "turning echoing back of commands on and off") { }
+ virtual void help()
+ {
+ log("\n");
+ log(" echo on\n");
+ log("\n");
+ log("Print all commands to log before executing them.\n");
+ log("\n");
+ log("\n");
+ log(" echo off\n");
+ log("\n");
+ log("Do not print all commands to log before executing them. (default)\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design*)
+ {
+ if (args.size() > 2)
+ cmd_error(args, 2, "Unexpected argument.");
+
+ if (args.size() == 2) {
+ if (args[1] == "on")
+ echo_mode = true;
+ else if (args[1] == "off")
+ echo_mode = false;
+ else
+ cmd_error(args, 1, "Unexpected argument.");
+ }
+
+ log("echo %s\n", echo_mode ? "on" : "off");
+ }
+} EchoPass;
+
+SatSolver *yosys_satsolver_list;
+SatSolver *yosys_satsolver;
+
+struct MinisatSatSolver : public SatSolver {
+ MinisatSatSolver() : SatSolver("minisat") {
+ yosys_satsolver = this;
+ }
+ virtual ezSAT *create() YS_OVERRIDE {
+ return new ezMiniSAT();
+ }
+} MinisatSatSolver;
+
+YOSYS_NAMESPACE_END
+
diff --git a/kernel/register.h b/kernel/register.h
new file mode 100644
index 00000000..8024c56a
--- /dev/null
+++ b/kernel/register.h
@@ -0,0 +1,129 @@
+/*
+ * 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"
+
+#ifndef REGISTER_H
+#define REGISTER_H
+
+YOSYS_NAMESPACE_BEGIN
+
+struct Pass
+{
+ std::string pass_name, short_help;
+ Pass(std::string name, std::string short_help = "** document me **");
+ virtual ~Pass();
+
+ virtual void help();
+ virtual void clear_flags();
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design) = 0;
+
+ int call_counter;
+ int64_t runtime_ns;
+
+ struct pre_post_exec_state_t {
+ Pass *parent_pass;
+ int64_t begin_ns;
+ };
+
+ pre_post_exec_state_t pre_execute();
+ void post_execute(pre_post_exec_state_t state);
+
+ void cmd_log_args(const std::vector<std::string> &args);
+ void cmd_error(const std::vector<std::string> &args, size_t argidx, std::string msg);
+ void extra_args(std::vector<std::string> args, size_t argidx, RTLIL::Design *design, bool select = true);
+
+ static void call(RTLIL::Design *design, std::string command);
+ static void call(RTLIL::Design *design, std::vector<std::string> args);
+
+ static void call_on_selection(RTLIL::Design *design, const RTLIL::Selection &selection, std::string command);
+ static void call_on_selection(RTLIL::Design *design, const RTLIL::Selection &selection, std::vector<std::string> args);
+
+ static void call_on_module(RTLIL::Design *design, RTLIL::Module *module, std::string command);
+ static void call_on_module(RTLIL::Design *design, RTLIL::Module *module, std::vector<std::string> args);
+
+ Pass *next_queued_pass;
+ virtual void run_register();
+ static void init_register();
+ static void done_register();
+};
+
+struct ScriptPass : Pass
+{
+ bool block_active, help_mode;
+ RTLIL::Design *active_design;
+ std::string active_run_from, active_run_to;
+
+ ScriptPass(std::string name, std::string short_help = "** document me **") : Pass(name, short_help) { }
+
+ virtual void script() = 0;
+
+ bool check_label(std::string label, std::string info = std::string());
+ void run(std::string command, std::string info = std::string());
+ void run_script(RTLIL::Design *design, std::string run_from = std::string(), std::string run_to = std::string());
+ void help_script();
+};
+
+struct Frontend : Pass
+{
+ // for reading of here documents
+ static FILE *current_script_file;
+ static std::string last_here_document;
+
+ std::string frontend_name;
+ Frontend(std::string name, std::string short_help = "** document me **");
+ virtual void run_register() YS_OVERRIDE;
+ virtual ~Frontend();
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE YS_FINAL;
+ virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) = 0;
+
+ static std::vector<std::string> next_args;
+ void extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx);
+
+ static void frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::string command);
+ static void frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::vector<std::string> args);
+};
+
+struct Backend : Pass
+{
+ std::string backend_name;
+ Backend(std::string name, std::string short_help = "** document me **");
+ virtual void run_register() YS_OVERRIDE;
+ virtual ~Backend();
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE YS_FINAL;
+ virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) = 0;
+
+ void extra_args(std::ostream *&f, std::string &filename, std::vector<std::string> args, size_t argidx);
+
+ static void backend_call(RTLIL::Design *design, std::ostream *f, std::string filename, std::string command);
+ static void backend_call(RTLIL::Design *design, std::ostream *f, std::string filename, std::vector<std::string> args);
+};
+
+// implemented in passes/cmds/select.cc
+extern void handle_extra_select_args(Pass *pass, std::vector<std::string> args, size_t argidx, size_t args_size, RTLIL::Design *design);
+extern RTLIL::Selection eval_select_args(const vector<string> &args, RTLIL::Design *design);
+extern void eval_select_op(vector<RTLIL::Selection> &work, const string &op, RTLIL::Design *design);
+
+extern std::map<std::string, Pass*> pass_register;
+extern std::map<std::string, Frontend*> frontend_register;
+extern std::map<std::string, Backend*> backend_register;
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc
new file mode 100644
index 00000000..66bbf042
--- /dev/null
+++ b/kernel/rtlil.cc
@@ -0,0 +1,3679 @@
+/*
+ * 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/macc.h"
+#include "kernel/celltypes.h"
+#include "frontends/verilog/verilog_frontend.h"
+#include "backends/ilang/ilang_backend.h"
+
+#include <string.h>
+#include <algorithm>
+
+YOSYS_NAMESPACE_BEGIN
+
+RTLIL::IdString::destruct_guard_t RTLIL::IdString::destruct_guard;
+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_;
+
+RTLIL::Const::Const()
+{
+ flags = RTLIL::CONST_FLAG_NONE;
+}
+
+RTLIL::Const::Const(std::string str)
+{
+ flags = RTLIL::CONST_FLAG_STRING;
+ for (int i = str.size()-1; i >= 0; i--) {
+ unsigned char ch = str[i];
+ for (int j = 0; j < 8; j++) {
+ bits.push_back((ch & 1) != 0 ? RTLIL::S1 : RTLIL::S0);
+ ch = ch >> 1;
+ }
+ }
+}
+
+RTLIL::Const::Const(int val, int width)
+{
+ flags = RTLIL::CONST_FLAG_NONE;
+ for (int i = 0; i < width; i++) {
+ bits.push_back((val & 1) != 0 ? RTLIL::S1 : RTLIL::S0);
+ val = val >> 1;
+ }
+}
+
+RTLIL::Const::Const(RTLIL::State bit, int width)
+{
+ flags = RTLIL::CONST_FLAG_NONE;
+ for (int i = 0; i < width; i++)
+ bits.push_back(bit);
+}
+
+RTLIL::Const::Const(const std::vector<bool> &bits)
+{
+ flags = RTLIL::CONST_FLAG_NONE;
+ for (auto b : bits)
+ this->bits.push_back(b ? RTLIL::S1 : RTLIL::S0);
+}
+
+bool RTLIL::Const::operator <(const RTLIL::Const &other) const
+{
+ if (bits.size() != other.bits.size())
+ return bits.size() < other.bits.size();
+ for (size_t i = 0; i < bits.size(); i++)
+ if (bits[i] != other.bits[i])
+ return bits[i] < other.bits[i];
+ return false;
+}
+
+bool RTLIL::Const::operator ==(const RTLIL::Const &other) const
+{
+ return bits == other.bits;
+}
+
+bool RTLIL::Const::operator !=(const RTLIL::Const &other) const
+{
+ return bits != other.bits;
+}
+
+bool RTLIL::Const::as_bool() const
+{
+ for (size_t i = 0; i < bits.size(); i++)
+ if (bits[i] == RTLIL::S1)
+ return true;
+ return false;
+}
+
+int RTLIL::Const::as_int(bool is_signed) const
+{
+ int32_t ret = 0;
+ for (size_t i = 0; i < bits.size() && i < 32; i++)
+ if (bits[i] == RTLIL::S1)
+ ret |= 1 << i;
+ if (is_signed && bits.back() == RTLIL::S1)
+ for (size_t i = bits.size(); i < 32; i++)
+ ret |= 1 << i;
+ return ret;
+}
+
+std::string RTLIL::Const::as_string() const
+{
+ std::string ret;
+ for (size_t i = bits.size(); i > 0; i--)
+ switch (bits[i-1]) {
+ case S0: ret += "0"; break;
+ case S1: ret += "1"; break;
+ case Sx: ret += "x"; break;
+ case Sz: ret += "z"; break;
+ case Sa: ret += "-"; break;
+ case Sm: ret += "m"; break;
+ }
+ return ret;
+}
+
+RTLIL::Const RTLIL::Const::from_string(std::string str)
+{
+ Const c;
+ for (auto it = str.rbegin(); it != str.rend(); it++)
+ switch (*it) {
+ case '0': c.bits.push_back(State::S0); break;
+ case '1': c.bits.push_back(State::S1); break;
+ case 'x': c.bits.push_back(State::Sx); break;
+ case 'z': c.bits.push_back(State::Sz); break;
+ case 'm': c.bits.push_back(State::Sm); break;
+ default: c.bits.push_back(State::Sa);
+ }
+ return c;
+}
+
+std::string RTLIL::Const::decode_string() const
+{
+ std::string string;
+ std::vector<char> string_chars;
+ for (int i = 0; i < int (bits.size()); i += 8) {
+ char ch = 0;
+ for (int j = 0; j < 8 && i + j < int (bits.size()); j++)
+ if (bits[i + j] == RTLIL::State::S1)
+ ch |= 1 << j;
+ if (ch != 0)
+ string_chars.push_back(ch);
+ }
+ for (int i = int (string_chars.size()) - 1; i >= 0; i--)
+ string += string_chars[i];
+ return string;
+}
+
+void RTLIL::AttrObject::set_bool_attribute(RTLIL::IdString id)
+{
+ attributes[id] = RTLIL::Const(1);
+}
+
+bool RTLIL::AttrObject::get_bool_attribute(RTLIL::IdString id) const
+{
+ if (attributes.count(id) == 0)
+ return false;
+ return attributes.at(id).as_bool();
+}
+
+void RTLIL::AttrObject::set_strpool_attribute(RTLIL::IdString id, const pool<string> &data)
+{
+ string attrval;
+ for (auto &s : data) {
+ if (!attrval.empty())
+ attrval += "|";
+ attrval += s;
+ }
+ attributes[id] = RTLIL::Const(attrval);
+}
+
+void RTLIL::AttrObject::add_strpool_attribute(RTLIL::IdString id, const pool<string> &data)
+{
+ pool<string> union_data = get_strpool_attribute(id);
+ union_data.insert(data.begin(), data.end());
+ if (!union_data.empty())
+ set_strpool_attribute(id, union_data);
+}
+
+pool<string> RTLIL::AttrObject::get_strpool_attribute(RTLIL::IdString id) const
+{
+ pool<string> data;
+ if (attributes.count(id) != 0)
+ for (auto s : split_tokens(attributes.at(id).decode_string(), "|"))
+ data.insert(s);
+ return data;
+}
+
+bool RTLIL::Selection::selected_module(RTLIL::IdString mod_name) const
+{
+ if (full_selection)
+ return true;
+ if (selected_modules.count(mod_name) > 0)
+ return true;
+ if (selected_members.count(mod_name) > 0)
+ return true;
+ return false;
+}
+
+bool RTLIL::Selection::selected_whole_module(RTLIL::IdString mod_name) const
+{
+ if (full_selection)
+ return true;
+ if (selected_modules.count(mod_name) > 0)
+ return true;
+ return false;
+}
+
+bool RTLIL::Selection::selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) const
+{
+ if (full_selection)
+ return true;
+ if (selected_modules.count(mod_name) > 0)
+ return true;
+ if (selected_members.count(mod_name) > 0)
+ if (selected_members.at(mod_name).count(memb_name) > 0)
+ return true;
+ return false;
+}
+
+void RTLIL::Selection::optimize(RTLIL::Design *design)
+{
+ if (full_selection) {
+ selected_modules.clear();
+ selected_members.clear();
+ return;
+ }
+
+ std::vector<RTLIL::IdString> del_list, add_list;
+
+ del_list.clear();
+ for (auto mod_name : selected_modules) {
+ if (design->modules_.count(mod_name) == 0)
+ del_list.push_back(mod_name);
+ selected_members.erase(mod_name);
+ }
+ for (auto mod_name : del_list)
+ selected_modules.erase(mod_name);
+
+ del_list.clear();
+ for (auto &it : selected_members)
+ if (design->modules_.count(it.first) == 0)
+ del_list.push_back(it.first);
+ for (auto mod_name : del_list)
+ selected_members.erase(mod_name);
+
+ for (auto &it : selected_members) {
+ del_list.clear();
+ for (auto memb_name : it.second)
+ if (design->modules_[it.first]->count_id(memb_name) == 0)
+ del_list.push_back(memb_name);
+ for (auto memb_name : del_list)
+ it.second.erase(memb_name);
+ }
+
+ del_list.clear();
+ add_list.clear();
+ for (auto &it : selected_members)
+ if (it.second.size() == 0)
+ del_list.push_back(it.first);
+ else if (it.second.size() == design->modules_[it.first]->wires_.size() + design->modules_[it.first]->memories.size() +
+ design->modules_[it.first]->cells_.size() + design->modules_[it.first]->processes.size())
+ add_list.push_back(it.first);
+ for (auto mod_name : del_list)
+ selected_members.erase(mod_name);
+ for (auto mod_name : add_list) {
+ selected_members.erase(mod_name);
+ selected_modules.insert(mod_name);
+ }
+
+ if (selected_modules.size() == design->modules_.size()) {
+ full_selection = true;
+ selected_modules.clear();
+ selected_members.clear();
+ }
+}
+
+RTLIL::Design::Design()
+{
+ static unsigned int hashidx_count = 123456789;
+ hashidx_count = mkhash_xorshift(hashidx_count);
+ hashidx_ = hashidx_count;
+
+ refcount_modules_ = 0;
+ selection_stack.push_back(RTLIL::Selection());
+}
+
+RTLIL::Design::~Design()
+{
+ for (auto it = modules_.begin(); it != modules_.end(); ++it)
+ delete it->second;
+ for (auto n : verilog_packages)
+ delete n;
+}
+
+RTLIL::ObjRange<RTLIL::Module*> RTLIL::Design::modules()
+{
+ return RTLIL::ObjRange<RTLIL::Module*>(&modules_, &refcount_modules_);
+}
+
+RTLIL::Module *RTLIL::Design::module(RTLIL::IdString name)
+{
+ return modules_.count(name) ? modules_.at(name) : NULL;
+}
+
+RTLIL::Module *RTLIL::Design::top_module()
+{
+ RTLIL::Module *module = nullptr;
+ int module_count = 0;
+
+ for (auto mod : selected_modules()) {
+ if (mod->get_bool_attribute("\\top"))
+ return mod;
+ module_count++;
+ module = mod;
+ }
+
+ return module_count == 1 ? module : nullptr;
+}
+
+void RTLIL::Design::add(RTLIL::Module *module)
+{
+ log_assert(modules_.count(module->name) == 0);
+ log_assert(refcount_modules_ == 0);
+ modules_[module->name] = module;
+ module->design = this;
+
+ for (auto mon : monitors)
+ mon->notify_module_add(module);
+
+ if (yosys_xtrace) {
+ log("#X# New Module: %s\n", log_id(module));
+ log_backtrace("-X- ", yosys_xtrace-1);
+ }
+}
+
+RTLIL::Module *RTLIL::Design::addModule(RTLIL::IdString name)
+{
+ log_assert(modules_.count(name) == 0);
+ log_assert(refcount_modules_ == 0);
+
+ RTLIL::Module *module = new RTLIL::Module;
+ modules_[name] = module;
+ module->design = this;
+ module->name = name;
+
+ for (auto mon : monitors)
+ mon->notify_module_add(module);
+
+ if (yosys_xtrace) {
+ log("#X# New Module: %s\n", log_id(module));
+ log_backtrace("-X- ", yosys_xtrace-1);
+ }
+
+ return module;
+}
+
+void RTLIL::Design::scratchpad_unset(std::string varname)
+{
+ scratchpad.erase(varname);
+}
+
+void RTLIL::Design::scratchpad_set_int(std::string varname, int value)
+{
+ scratchpad[varname] = stringf("%d", value);
+}
+
+void RTLIL::Design::scratchpad_set_bool(std::string varname, bool value)
+{
+ scratchpad[varname] = value ? "true" : "false";
+}
+
+void RTLIL::Design::scratchpad_set_string(std::string varname, std::string value)
+{
+ scratchpad[varname] = value;
+}
+
+int RTLIL::Design::scratchpad_get_int(std::string varname, int default_value) const
+{
+ if (scratchpad.count(varname) == 0)
+ return default_value;
+
+ std::string str = scratchpad.at(varname);
+
+ if (str == "0" || str == "false")
+ return 0;
+
+ if (str == "1" || str == "true")
+ return 1;
+
+ char *endptr = nullptr;
+ long int parsed_value = strtol(str.c_str(), &endptr, 10);
+ return *endptr ? default_value : parsed_value;
+}
+
+bool RTLIL::Design::scratchpad_get_bool(std::string varname, bool default_value) const
+{
+ if (scratchpad.count(varname) == 0)
+ return default_value;
+
+ std::string str = scratchpad.at(varname);
+
+ if (str == "0" || str == "false")
+ return false;
+
+ if (str == "1" || str == "true")
+ return true;
+
+ return default_value;
+}
+
+std::string RTLIL::Design::scratchpad_get_string(std::string varname, std::string default_value) const
+{
+ if (scratchpad.count(varname) == 0)
+ return default_value;
+ return scratchpad.at(varname);
+}
+
+void RTLIL::Design::remove(RTLIL::Module *module)
+{
+ for (auto mon : monitors)
+ mon->notify_module_del(module);
+
+ if (yosys_xtrace) {
+ log("#X# Remove Module: %s\n", log_id(module));
+ log_backtrace("-X- ", yosys_xtrace-1);
+ }
+
+ log_assert(modules_.at(module->name) == module);
+ modules_.erase(module->name);
+ delete module;
+}
+
+void RTLIL::Design::rename(RTLIL::Module *module, RTLIL::IdString new_name)
+{
+ modules_.erase(module->name);
+ module->name = new_name;
+ add(module);
+}
+
+void RTLIL::Design::sort()
+{
+ scratchpad.sort();
+ modules_.sort(sort_by_id_str());
+ for (auto &it : modules_)
+ it.second->sort();
+}
+
+void RTLIL::Design::check()
+{
+#ifndef NDEBUG
+ for (auto &it : modules_) {
+ log_assert(this == it.second->design);
+ log_assert(it.first == it.second->name);
+ log_assert(!it.first.empty());
+ it.second->check();
+ }
+#endif
+}
+
+void RTLIL::Design::optimize()
+{
+ for (auto &it : modules_)
+ it.second->optimize();
+ for (auto &it : selection_stack)
+ it.optimize(this);
+ for (auto &it : selection_vars)
+ it.second.optimize(this);
+}
+
+bool RTLIL::Design::selected_module(RTLIL::IdString mod_name) const
+{
+ if (!selected_active_module.empty() && mod_name != selected_active_module)
+ return false;
+ if (selection_stack.size() == 0)
+ return true;
+ return selection_stack.back().selected_module(mod_name);
+}
+
+bool RTLIL::Design::selected_whole_module(RTLIL::IdString mod_name) const
+{
+ if (!selected_active_module.empty() && mod_name != selected_active_module)
+ return false;
+ if (selection_stack.size() == 0)
+ return true;
+ return selection_stack.back().selected_whole_module(mod_name);
+}
+
+bool RTLIL::Design::selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) const
+{
+ if (!selected_active_module.empty() && mod_name != selected_active_module)
+ return false;
+ if (selection_stack.size() == 0)
+ return true;
+ return selection_stack.back().selected_member(mod_name, memb_name);
+}
+
+bool RTLIL::Design::selected_module(RTLIL::Module *mod) const
+{
+ return selected_module(mod->name);
+}
+
+bool RTLIL::Design::selected_whole_module(RTLIL::Module *mod) const
+{
+ return selected_whole_module(mod->name);
+}
+
+std::vector<RTLIL::Module*> RTLIL::Design::selected_modules() const
+{
+ std::vector<RTLIL::Module*> result;
+ result.reserve(modules_.size());
+ for (auto &it : modules_)
+ if (selected_module(it.first) && !it.second->get_bool_attribute("\\blackbox"))
+ result.push_back(it.second);
+ return result;
+}
+
+std::vector<RTLIL::Module*> RTLIL::Design::selected_whole_modules() const
+{
+ std::vector<RTLIL::Module*> result;
+ result.reserve(modules_.size());
+ for (auto &it : modules_)
+ if (selected_whole_module(it.first) && !it.second->get_bool_attribute("\\blackbox"))
+ result.push_back(it.second);
+ return result;
+}
+
+std::vector<RTLIL::Module*> RTLIL::Design::selected_whole_modules_warn() const
+{
+ std::vector<RTLIL::Module*> result;
+ result.reserve(modules_.size());
+ for (auto &it : modules_)
+ if (it.second->get_bool_attribute("\\blackbox"))
+ continue;
+ else if (selected_whole_module(it.first))
+ result.push_back(it.second);
+ else if (selected_module(it.first))
+ log_warning("Ignoring partially selected module %s.\n", log_id(it.first));
+ return result;
+}
+
+RTLIL::Module::Module()
+{
+ static unsigned int hashidx_count = 123456789;
+ hashidx_count = mkhash_xorshift(hashidx_count);
+ hashidx_ = hashidx_count;
+
+ design = nullptr;
+ refcount_wires_ = 0;
+ refcount_cells_ = 0;
+}
+
+RTLIL::Module::~Module()
+{
+ for (auto it = wires_.begin(); it != wires_.end(); ++it)
+ delete it->second;
+ for (auto it = memories.begin(); it != memories.end(); ++it)
+ delete it->second;
+ for (auto it = cells_.begin(); it != cells_.end(); ++it)
+ delete it->second;
+ for (auto it = processes.begin(); it != processes.end(); ++it)
+ delete it->second;
+}
+
+RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, dict<RTLIL::IdString, RTLIL::Const>)
+{
+ 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);
+}
+
+#ifndef NDEBUG
+namespace {
+ struct InternalCellChecker
+ {
+ RTLIL::Module *module;
+ RTLIL::Cell *cell;
+ pool<RTLIL::IdString> expected_params, expected_ports;
+
+ InternalCellChecker(RTLIL::Module *module, RTLIL::Cell *cell) : module(module), cell(cell) { }
+
+ void error(int linenr)
+ {
+ std::stringstream buf;
+ ILANG_BACKEND::dump_cell(buf, " ", cell);
+
+ log_error("Found error in internal cell %s%s%s (%s) at %s:%d:\n%s",
+ module ? module->name.c_str() : "", module ? "." : "",
+ cell->name.c_str(), cell->type.c_str(), __FILE__, linenr, buf.str().c_str());
+ }
+
+ int param(const char *name)
+ {
+ if (cell->parameters.count(name) == 0)
+ error(__LINE__);
+ expected_params.insert(name);
+ return cell->parameters.at(name).as_int();
+ }
+
+ int param_bool(const char *name)
+ {
+ int v = param(name);
+ if (cell->parameters.at(name).bits.size() > 32)
+ error(__LINE__);
+ if (v != 0 && v != 1)
+ error(__LINE__);
+ return v;
+ }
+
+ void param_bits(const char *name, int width)
+ {
+ param(name);
+ if (int(cell->parameters.at(name).bits.size()) != width)
+ error(__LINE__);
+ }
+
+ void port(const char *name, int width)
+ {
+ if (!cell->hasPort(name))
+ error(__LINE__);
+ if (cell->getPort(name).size() != width)
+ error(__LINE__);
+ expected_ports.insert(name);
+ }
+
+ void check_expected(bool check_matched_sign = true)
+ {
+ for (auto &para : cell->parameters)
+ if (expected_params.count(para.first) == 0)
+ error(__LINE__);
+ for (auto &conn : cell->connections())
+ if (expected_ports.count(conn.first) == 0)
+ error(__LINE__);
+
+ if (expected_params.count("\\A_SIGNED") != 0 && expected_params.count("\\B_SIGNED") && check_matched_sign) {
+ bool a_is_signed = param("\\A_SIGNED") != 0;
+ bool b_is_signed = param("\\B_SIGNED") != 0;
+ if (a_is_signed != b_is_signed)
+ error(__LINE__);
+ }
+ }
+
+ void check_gate(const char *ports)
+ {
+ if (cell->parameters.size() != 0)
+ error(__LINE__);
+
+ for (const char *p = ports; *p; p++) {
+ char portname[3] = { '\\', *p, 0 };
+ if (!cell->hasPort(portname))
+ error(__LINE__);
+ if (cell->getPort(portname).size() != 1)
+ error(__LINE__);
+ }
+
+ for (auto &conn : cell->connections()) {
+ if (conn.first.size() != 2 || conn.first[0] != '\\')
+ error(__LINE__);
+ if (strchr(ports, conn.first[1]) == NULL)
+ error(__LINE__);
+ }
+ }
+
+ void check()
+ {
+ if (cell->type.substr(0, 1) != "$" || cell->type.substr(0, 3) == "$__" || cell->type.substr(0, 8) == "$paramod" ||
+ cell->type.substr(0, 9) == "$verific$" || cell->type.substr(0, 7) == "$array:" || cell->type.substr(0, 8) == "$extern:")
+ return;
+
+ if (cell->type.in("$not", "$pos", "$neg")) {
+ param_bool("\\A_SIGNED");
+ port("\\A", param("\\A_WIDTH"));
+ port("\\Y", param("\\Y_WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type.in("$and", "$or", "$xor", "$xnor")) {
+ param_bool("\\A_SIGNED");
+ param_bool("\\B_SIGNED");
+ port("\\A", param("\\A_WIDTH"));
+ port("\\B", param("\\B_WIDTH"));
+ port("\\Y", param("\\Y_WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool")) {
+ param_bool("\\A_SIGNED");
+ port("\\A", param("\\A_WIDTH"));
+ port("\\Y", param("\\Y_WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type.in("$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx")) {
+ param_bool("\\A_SIGNED");
+ param_bool("\\B_SIGNED");
+ port("\\A", param("\\A_WIDTH"));
+ port("\\B", param("\\B_WIDTH"));
+ port("\\Y", param("\\Y_WIDTH"));
+ check_expected(false);
+ return;
+ }
+
+ if (cell->type.in("$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt")) {
+ param_bool("\\A_SIGNED");
+ param_bool("\\B_SIGNED");
+ port("\\A", param("\\A_WIDTH"));
+ port("\\B", param("\\B_WIDTH"));
+ port("\\Y", param("\\Y_WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type.in("$add", "$sub", "$mul", "$div", "$mod", "$pow")) {
+ param_bool("\\A_SIGNED");
+ param_bool("\\B_SIGNED");
+ port("\\A", param("\\A_WIDTH"));
+ port("\\B", param("\\B_WIDTH"));
+ port("\\Y", param("\\Y_WIDTH"));
+ check_expected(cell->type != "$pow");
+ return;
+ }
+
+ if (cell->type == "$fa") {
+ port("\\A", param("\\WIDTH"));
+ port("\\B", param("\\WIDTH"));
+ port("\\C", param("\\WIDTH"));
+ port("\\X", param("\\WIDTH"));
+ port("\\Y", param("\\WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$lcu") {
+ port("\\P", param("\\WIDTH"));
+ port("\\G", param("\\WIDTH"));
+ port("\\CI", 1);
+ port("\\CO", param("\\WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$alu") {
+ param_bool("\\A_SIGNED");
+ param_bool("\\B_SIGNED");
+ port("\\A", param("\\A_WIDTH"));
+ port("\\B", param("\\B_WIDTH"));
+ port("\\CI", 1);
+ port("\\BI", 1);
+ port("\\X", param("\\Y_WIDTH"));
+ port("\\Y", param("\\Y_WIDTH"));
+ port("\\CO", param("\\Y_WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$macc") {
+ param("\\CONFIG");
+ param("\\CONFIG_WIDTH");
+ port("\\A", param("\\A_WIDTH"));
+ port("\\B", param("\\B_WIDTH"));
+ port("\\Y", param("\\Y_WIDTH"));
+ check_expected();
+ Macc().from_cell(cell);
+ return;
+ }
+
+ if (cell->type == "$logic_not") {
+ param_bool("\\A_SIGNED");
+ port("\\A", param("\\A_WIDTH"));
+ port("\\Y", param("\\Y_WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$logic_and" || cell->type == "$logic_or") {
+ param_bool("\\A_SIGNED");
+ param_bool("\\B_SIGNED");
+ port("\\A", param("\\A_WIDTH"));
+ port("\\B", param("\\B_WIDTH"));
+ port("\\Y", param("\\Y_WIDTH"));
+ check_expected(false);
+ return;
+ }
+
+ if (cell->type == "$slice") {
+ param("\\OFFSET");
+ port("\\A", param("\\A_WIDTH"));
+ port("\\Y", param("\\Y_WIDTH"));
+ if (param("\\OFFSET") + param("\\Y_WIDTH") > param("\\A_WIDTH"))
+ error(__LINE__);
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$concat") {
+ port("\\A", param("\\A_WIDTH"));
+ port("\\B", param("\\B_WIDTH"));
+ port("\\Y", param("\\A_WIDTH") + param("\\B_WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$mux") {
+ port("\\A", param("\\WIDTH"));
+ port("\\B", param("\\WIDTH"));
+ port("\\S", 1);
+ port("\\Y", param("\\WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$pmux") {
+ port("\\A", param("\\WIDTH"));
+ port("\\B", param("\\WIDTH") * param("\\S_WIDTH"));
+ port("\\S", param("\\S_WIDTH"));
+ port("\\Y", param("\\WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$lut") {
+ param("\\LUT");
+ port("\\A", param("\\WIDTH"));
+ port("\\Y", 1);
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$sop") {
+ param("\\DEPTH");
+ param("\\TABLE");
+ port("\\A", param("\\WIDTH"));
+ port("\\Y", 1);
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$sr") {
+ param_bool("\\SET_POLARITY");
+ param_bool("\\CLR_POLARITY");
+ port("\\SET", param("\\WIDTH"));
+ port("\\CLR", param("\\WIDTH"));
+ port("\\Q", param("\\WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$ff") {
+ port("\\D", param("\\WIDTH"));
+ port("\\Q", param("\\WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$dff") {
+ param_bool("\\CLK_POLARITY");
+ port("\\CLK", 1);
+ port("\\D", param("\\WIDTH"));
+ port("\\Q", param("\\WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$dffe") {
+ param_bool("\\CLK_POLARITY");
+ param_bool("\\EN_POLARITY");
+ port("\\CLK", 1);
+ port("\\EN", 1);
+ port("\\D", param("\\WIDTH"));
+ port("\\Q", param("\\WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$dffsr") {
+ param_bool("\\CLK_POLARITY");
+ param_bool("\\SET_POLARITY");
+ param_bool("\\CLR_POLARITY");
+ port("\\CLK", 1);
+ port("\\SET", param("\\WIDTH"));
+ port("\\CLR", param("\\WIDTH"));
+ port("\\D", param("\\WIDTH"));
+ port("\\Q", param("\\WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$adff") {
+ param_bool("\\CLK_POLARITY");
+ param_bool("\\ARST_POLARITY");
+ param_bits("\\ARST_VALUE", param("\\WIDTH"));
+ port("\\CLK", 1);
+ port("\\ARST", 1);
+ port("\\D", param("\\WIDTH"));
+ port("\\Q", param("\\WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$dlatch") {
+ param_bool("\\EN_POLARITY");
+ port("\\EN", 1);
+ port("\\D", param("\\WIDTH"));
+ port("\\Q", param("\\WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$dlatchsr") {
+ param_bool("\\EN_POLARITY");
+ param_bool("\\SET_POLARITY");
+ param_bool("\\CLR_POLARITY");
+ port("\\EN", 1);
+ port("\\SET", param("\\WIDTH"));
+ port("\\CLR", param("\\WIDTH"));
+ port("\\D", param("\\WIDTH"));
+ port("\\Q", param("\\WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$fsm") {
+ param("\\NAME");
+ param_bool("\\CLK_POLARITY");
+ param_bool("\\ARST_POLARITY");
+ param("\\STATE_BITS");
+ param("\\STATE_NUM");
+ param("\\STATE_NUM_LOG2");
+ param("\\STATE_RST");
+ param_bits("\\STATE_TABLE", param("\\STATE_BITS") * param("\\STATE_NUM"));
+ param("\\TRANS_NUM");
+ param_bits("\\TRANS_TABLE", param("\\TRANS_NUM") * (2*param("\\STATE_NUM_LOG2") + param("\\CTRL_IN_WIDTH") + param("\\CTRL_OUT_WIDTH")));
+ port("\\CLK", 1);
+ port("\\ARST", 1);
+ port("\\CTRL_IN", param("\\CTRL_IN_WIDTH"));
+ port("\\CTRL_OUT", param("\\CTRL_OUT_WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$memrd") {
+ param("\\MEMID");
+ param_bool("\\CLK_ENABLE");
+ param_bool("\\CLK_POLARITY");
+ param_bool("\\TRANSPARENT");
+ port("\\CLK", 1);
+ port("\\EN", 1);
+ port("\\ADDR", param("\\ABITS"));
+ port("\\DATA", param("\\WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$memwr") {
+ param("\\MEMID");
+ param_bool("\\CLK_ENABLE");
+ param_bool("\\CLK_POLARITY");
+ param("\\PRIORITY");
+ port("\\CLK", 1);
+ port("\\EN", param("\\WIDTH"));
+ port("\\ADDR", param("\\ABITS"));
+ port("\\DATA", param("\\WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$meminit") {
+ param("\\MEMID");
+ param("\\PRIORITY");
+ port("\\ADDR", param("\\ABITS"));
+ port("\\DATA", param("\\WIDTH") * param("\\WORDS"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$mem") {
+ param("\\MEMID");
+ param("\\SIZE");
+ param("\\OFFSET");
+ param("\\INIT");
+ param_bits("\\RD_CLK_ENABLE", max(1, param("\\RD_PORTS")));
+ param_bits("\\RD_CLK_POLARITY", max(1, param("\\RD_PORTS")));
+ param_bits("\\RD_TRANSPARENT", max(1, param("\\RD_PORTS")));
+ param_bits("\\WR_CLK_ENABLE", max(1, param("\\WR_PORTS")));
+ param_bits("\\WR_CLK_POLARITY", max(1, param("\\WR_PORTS")));
+ port("\\RD_CLK", param("\\RD_PORTS"));
+ port("\\RD_EN", param("\\RD_PORTS"));
+ port("\\RD_ADDR", param("\\RD_PORTS") * param("\\ABITS"));
+ port("\\RD_DATA", param("\\RD_PORTS") * param("\\WIDTH"));
+ port("\\WR_CLK", param("\\WR_PORTS"));
+ port("\\WR_EN", param("\\WR_PORTS") * param("\\WIDTH"));
+ port("\\WR_ADDR", param("\\WR_PORTS") * param("\\ABITS"));
+ port("\\WR_DATA", param("\\WR_PORTS") * param("\\WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$tribuf") {
+ port("\\A", param("\\WIDTH"));
+ port("\\Y", param("\\WIDTH"));
+ port("\\EN", 1);
+ check_expected();
+ return;
+ }
+
+ if (cell->type.in("$assert", "$assume")) {
+ port("\\A", 1);
+ port("\\EN", 1);
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$initstate") {
+ port("\\Y", 1);
+ check_expected();
+ return;
+ }
+
+ if (cell->type.in("$anyconst", "$anyseq")) {
+ port("\\Y", param("\\WIDTH"));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$equiv") {
+ port("\\A", 1);
+ port("\\B", 1);
+ port("\\Y", 1);
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$_BUF_") { check_gate("AY"); return; }
+ if (cell->type == "$_NOT_") { check_gate("AY"); return; }
+ if (cell->type == "$_AND_") { check_gate("ABY"); return; }
+ if (cell->type == "$_NAND_") { check_gate("ABY"); return; }
+ if (cell->type == "$_OR_") { check_gate("ABY"); return; }
+ if (cell->type == "$_NOR_") { check_gate("ABY"); return; }
+ if (cell->type == "$_XOR_") { check_gate("ABY"); return; }
+ if (cell->type == "$_XNOR_") { check_gate("ABY"); return; }
+ if (cell->type == "$_MUX_") { check_gate("ABSY"); return; }
+ if (cell->type == "$_AOI3_") { check_gate("ABCY"); return; }
+ if (cell->type == "$_OAI3_") { check_gate("ABCY"); return; }
+ if (cell->type == "$_AOI4_") { check_gate("ABCDY"); return; }
+ if (cell->type == "$_OAI4_") { check_gate("ABCDY"); return; }
+
+ if (cell->type == "$_TBUF_") { check_gate("AYE"); return; }
+
+ if (cell->type == "$_MUX4_") { check_gate("ABCDSTY"); return; }
+ if (cell->type == "$_MUX8_") { check_gate("ABCDEFGHSTUY"); return; }
+ if (cell->type == "$_MUX16_") { check_gate("ABCDEFGHIJKLMNOPSTUVY"); return; }
+
+ if (cell->type == "$_SR_NN_") { check_gate("SRQ"); return; }
+ if (cell->type == "$_SR_NP_") { check_gate("SRQ"); return; }
+ if (cell->type == "$_SR_PN_") { check_gate("SRQ"); return; }
+ if (cell->type == "$_SR_PP_") { check_gate("SRQ"); return; }
+
+ if (cell->type == "$_FF_") { check_gate("DQ"); return; }
+ if (cell->type == "$_DFF_N_") { check_gate("DQC"); return; }
+ if (cell->type == "$_DFF_P_") { check_gate("DQC"); return; }
+
+ if (cell->type == "$_DFFE_NN_") { check_gate("DQCE"); return; }
+ if (cell->type == "$_DFFE_NP_") { check_gate("DQCE"); return; }
+ if (cell->type == "$_DFFE_PN_") { check_gate("DQCE"); return; }
+ if (cell->type == "$_DFFE_PP_") { check_gate("DQCE"); return; }
+
+ if (cell->type == "$_DFF_NN0_") { check_gate("DQCR"); return; }
+ if (cell->type == "$_DFF_NN1_") { check_gate("DQCR"); return; }
+ if (cell->type == "$_DFF_NP0_") { check_gate("DQCR"); return; }
+ if (cell->type == "$_DFF_NP1_") { check_gate("DQCR"); return; }
+ if (cell->type == "$_DFF_PN0_") { check_gate("DQCR"); return; }
+ if (cell->type == "$_DFF_PN1_") { check_gate("DQCR"); return; }
+ if (cell->type == "$_DFF_PP0_") { check_gate("DQCR"); return; }
+ if (cell->type == "$_DFF_PP1_") { check_gate("DQCR"); return; }
+
+ if (cell->type == "$_DFFSR_NNN_") { check_gate("CSRDQ"); return; }
+ if (cell->type == "$_DFFSR_NNP_") { check_gate("CSRDQ"); return; }
+ if (cell->type == "$_DFFSR_NPN_") { check_gate("CSRDQ"); return; }
+ if (cell->type == "$_DFFSR_NPP_") { check_gate("CSRDQ"); return; }
+ if (cell->type == "$_DFFSR_PNN_") { check_gate("CSRDQ"); return; }
+ if (cell->type == "$_DFFSR_PNP_") { check_gate("CSRDQ"); return; }
+ if (cell->type == "$_DFFSR_PPN_") { check_gate("CSRDQ"); return; }
+ if (cell->type == "$_DFFSR_PPP_") { check_gate("CSRDQ"); return; }
+
+ if (cell->type == "$_DLATCH_N_") { check_gate("EDQ"); return; }
+ if (cell->type == "$_DLATCH_P_") { check_gate("EDQ"); return; }
+
+ if (cell->type == "$_DLATCHSR_NNN_") { check_gate("ESRDQ"); return; }
+ if (cell->type == "$_DLATCHSR_NNP_") { check_gate("ESRDQ"); return; }
+ if (cell->type == "$_DLATCHSR_NPN_") { check_gate("ESRDQ"); return; }
+ if (cell->type == "$_DLATCHSR_NPP_") { check_gate("ESRDQ"); return; }
+ if (cell->type == "$_DLATCHSR_PNN_") { check_gate("ESRDQ"); return; }
+ if (cell->type == "$_DLATCHSR_PNP_") { check_gate("ESRDQ"); return; }
+ if (cell->type == "$_DLATCHSR_PPN_") { check_gate("ESRDQ"); return; }
+ if (cell->type == "$_DLATCHSR_PPP_") { check_gate("ESRDQ"); return; }
+
+ error(__LINE__);
+ }
+ };
+}
+#endif
+
+void RTLIL::Module::sort()
+{
+ wires_.sort(sort_by_id_str());
+ cells_.sort(sort_by_id_str());
+ avail_parameters.sort(sort_by_id_str());
+ memories.sort(sort_by_id_str());
+ processes.sort(sort_by_id_str());
+ for (auto &it : cells_)
+ it.second->sort();
+ for (auto &it : wires_)
+ it.second->attributes.sort(sort_by_id_str());
+ for (auto &it : memories)
+ it.second->attributes.sort(sort_by_id_str());
+}
+
+void RTLIL::Module::check()
+{
+#ifndef NDEBUG
+ std::vector<bool> ports_declared;
+ for (auto &it : wires_) {
+ log_assert(this == it.second->module);
+ log_assert(it.first == it.second->name);
+ log_assert(!it.first.empty());
+ log_assert(it.second->width >= 0);
+ log_assert(it.second->port_id >= 0);
+ for (auto &it2 : it.second->attributes)
+ log_assert(!it2.first.empty());
+ if (it.second->port_id) {
+ log_assert(GetSize(ports) >= it.second->port_id);
+ log_assert(ports.at(it.second->port_id-1) == it.first);
+ log_assert(it.second->port_input || it.second->port_output);
+ if (GetSize(ports_declared) < it.second->port_id)
+ ports_declared.resize(it.second->port_id);
+ log_assert(ports_declared[it.second->port_id-1] == false);
+ ports_declared[it.second->port_id-1] = true;
+ } else
+ log_assert(!it.second->port_input && !it.second->port_output);
+ }
+ for (auto port_declared : ports_declared)
+ log_assert(port_declared == true);
+ log_assert(GetSize(ports) == GetSize(ports_declared));
+
+ for (auto &it : memories) {
+ log_assert(it.first == it.second->name);
+ log_assert(!it.first.empty());
+ log_assert(it.second->width >= 0);
+ log_assert(it.second->size >= 0);
+ for (auto &it2 : it.second->attributes)
+ log_assert(!it2.first.empty());
+ }
+
+ for (auto &it : cells_) {
+ log_assert(this == it.second->module);
+ log_assert(it.first == it.second->name);
+ log_assert(!it.first.empty());
+ log_assert(!it.second->type.empty());
+ for (auto &it2 : it.second->connections()) {
+ log_assert(!it2.first.empty());
+ it2.second.check();
+ }
+ for (auto &it2 : it.second->attributes)
+ log_assert(!it2.first.empty());
+ for (auto &it2 : it.second->parameters)
+ log_assert(!it2.first.empty());
+ InternalCellChecker checker(this, it.second);
+ checker.check();
+ }
+
+ for (auto &it : processes) {
+ log_assert(it.first == it.second->name);
+ log_assert(!it.first.empty());
+ // FIXME: More checks here..
+ }
+
+ for (auto &it : connections_) {
+ log_assert(it.first.size() == it.second.size());
+ log_assert(!it.first.has_const());
+ it.first.check();
+ it.second.check();
+ }
+
+ for (auto &it : attributes)
+ log_assert(!it.first.empty());
+#endif
+}
+
+void RTLIL::Module::optimize()
+{
+}
+
+void RTLIL::Module::cloneInto(RTLIL::Module *new_mod) const
+{
+ log_assert(new_mod->refcount_wires_ == 0);
+ log_assert(new_mod->refcount_cells_ == 0);
+
+ new_mod->avail_parameters = avail_parameters;
+
+ for (auto &conn : connections_)
+ new_mod->connect(conn);
+
+ for (auto &attr : attributes)
+ new_mod->attributes[attr.first] = attr.second;
+
+ for (auto &it : wires_)
+ new_mod->addWire(it.first, it.second);
+
+ for (auto &it : memories)
+ new_mod->memories[it.first] = new RTLIL::Memory(*it.second);
+
+ for (auto &it : cells_)
+ new_mod->addCell(it.first, it.second);
+
+ for (auto &it : processes)
+ new_mod->processes[it.first] = it.second->clone();
+
+ struct RewriteSigSpecWorker
+ {
+ RTLIL::Module *mod;
+ void operator()(RTLIL::SigSpec &sig)
+ {
+ std::vector<RTLIL::SigChunk> chunks = sig.chunks();
+ for (auto &c : chunks)
+ if (c.wire != NULL)
+ c.wire = mod->wires_.at(c.wire->name);
+ sig = chunks;
+ }
+ };
+
+ RewriteSigSpecWorker rewriteSigSpecWorker;
+ rewriteSigSpecWorker.mod = new_mod;
+ new_mod->rewrite_sigspecs(rewriteSigSpecWorker);
+ new_mod->fixup_ports();
+}
+
+RTLIL::Module *RTLIL::Module::clone() const
+{
+ RTLIL::Module *new_mod = new RTLIL::Module;
+ new_mod->name = name;
+ cloneInto(new_mod);
+ return new_mod;
+}
+
+bool RTLIL::Module::has_memories() const
+{
+ return !memories.empty();
+}
+
+bool RTLIL::Module::has_processes() const
+{
+ return !processes.empty();
+}
+
+bool RTLIL::Module::has_memories_warn() const
+{
+ if (!memories.empty())
+ log_warning("Ignoring module %s because it contains memories (run 'memory' command first).\n", log_id(this));
+ return !memories.empty();
+}
+
+bool RTLIL::Module::has_processes_warn() const
+{
+ if (!processes.empty())
+ log_warning("Ignoring module %s because it contains processes (run 'proc' command first).\n", log_id(this));
+ return !processes.empty();
+}
+
+std::vector<RTLIL::Wire*> RTLIL::Module::selected_wires() const
+{
+ std::vector<RTLIL::Wire*> result;
+ result.reserve(wires_.size());
+ for (auto &it : wires_)
+ if (design->selected(this, it.second))
+ result.push_back(it.second);
+ return result;
+}
+
+std::vector<RTLIL::Cell*> RTLIL::Module::selected_cells() const
+{
+ std::vector<RTLIL::Cell*> result;
+ result.reserve(wires_.size());
+ for (auto &it : cells_)
+ if (design->selected(this, it.second))
+ result.push_back(it.second);
+ return result;
+}
+
+void RTLIL::Module::add(RTLIL::Wire *wire)
+{
+ log_assert(!wire->name.empty());
+ log_assert(count_id(wire->name) == 0);
+ log_assert(refcount_wires_ == 0);
+ wires_[wire->name] = wire;
+ wire->module = this;
+}
+
+void RTLIL::Module::add(RTLIL::Cell *cell)
+{
+ log_assert(!cell->name.empty());
+ log_assert(count_id(cell->name) == 0);
+ log_assert(refcount_cells_ == 0);
+ cells_[cell->name] = cell;
+ cell->module = this;
+}
+
+namespace {
+ struct DeleteWireWorker
+ {
+ RTLIL::Module *module;
+ const pool<RTLIL::Wire*> *wires_p;
+
+ void operator()(RTLIL::SigSpec &sig) {
+ std::vector<RTLIL::SigChunk> chunks = sig;
+ for (auto &c : chunks)
+ if (c.wire != NULL && wires_p->count(c.wire)) {
+ c.wire = module->addWire(NEW_ID, c.width);
+ c.offset = 0;
+ }
+ sig = chunks;
+ }
+ };
+}
+
+void RTLIL::Module::remove(const pool<RTLIL::Wire*> &wires)
+{
+ log_assert(refcount_wires_ == 0);
+
+ DeleteWireWorker delete_wire_worker;
+ delete_wire_worker.module = this;
+ delete_wire_worker.wires_p = &wires;
+ rewrite_sigspecs(delete_wire_worker);
+
+ for (auto &it : wires) {
+ log_assert(wires_.count(it->name) != 0);
+ wires_.erase(it->name);
+ delete it;
+ }
+}
+
+void RTLIL::Module::remove(RTLIL::Cell *cell)
+{
+ while (!cell->connections_.empty())
+ cell->unsetPort(cell->connections_.begin()->first);
+
+ log_assert(cells_.count(cell->name) != 0);
+ log_assert(refcount_cells_ == 0);
+ cells_.erase(cell->name);
+ delete cell;
+}
+
+void RTLIL::Module::rename(RTLIL::Wire *wire, RTLIL::IdString new_name)
+{
+ log_assert(wires_[wire->name] == wire);
+ log_assert(refcount_wires_ == 0);
+ wires_.erase(wire->name);
+ wire->name = new_name;
+ add(wire);
+}
+
+void RTLIL::Module::rename(RTLIL::Cell *cell, RTLIL::IdString new_name)
+{
+ log_assert(cells_[cell->name] == cell);
+ log_assert(refcount_wires_ == 0);
+ cells_.erase(cell->name);
+ cell->name = new_name;
+ add(cell);
+}
+
+void RTLIL::Module::rename(RTLIL::IdString old_name, RTLIL::IdString new_name)
+{
+ log_assert(count_id(old_name) != 0);
+ if (wires_.count(old_name))
+ rename(wires_.at(old_name), new_name);
+ else if (cells_.count(old_name))
+ rename(cells_.at(old_name), new_name);
+ else
+ log_abort();
+}
+
+void RTLIL::Module::swap_names(RTLIL::Wire *w1, RTLIL::Wire *w2)
+{
+ log_assert(wires_[w1->name] == w1);
+ log_assert(wires_[w2->name] == w2);
+ log_assert(refcount_wires_ == 0);
+
+ wires_.erase(w1->name);
+ wires_.erase(w2->name);
+
+ std::swap(w1->name, w2->name);
+
+ wires_[w1->name] = w1;
+ wires_[w2->name] = w2;
+}
+
+void RTLIL::Module::swap_names(RTLIL::Cell *c1, RTLIL::Cell *c2)
+{
+ log_assert(cells_[c1->name] == c1);
+ log_assert(cells_[c2->name] == c2);
+ log_assert(refcount_cells_ == 0);
+
+ cells_.erase(c1->name);
+ cells_.erase(c2->name);
+
+ std::swap(c1->name, c2->name);
+
+ cells_[c1->name] = c1;
+ cells_[c2->name] = c2;
+}
+
+RTLIL::IdString RTLIL::Module::uniquify(RTLIL::IdString name)
+{
+ int index = 0;
+ return uniquify(name, index);
+}
+
+RTLIL::IdString RTLIL::Module::uniquify(RTLIL::IdString name, int &index)
+{
+ if (index == 0) {
+ if (count_id(name) == 0)
+ return name;
+ index++;
+ }
+
+ while (1) {
+ RTLIL::IdString new_name = stringf("%s_%d", name.c_str(), index);
+ if (count_id(new_name) == 0)
+ return new_name;
+ index++;
+ }
+}
+
+static bool fixup_ports_compare(const RTLIL::Wire *a, const RTLIL::Wire *b)
+{
+ if (a->port_id && !b->port_id)
+ return true;
+ if (!a->port_id && b->port_id)
+ return false;
+
+ if (a->port_id == b->port_id)
+ return a->name < b->name;
+ return a->port_id < b->port_id;
+}
+
+void RTLIL::Module::connect(const RTLIL::SigSig &conn)
+{
+ for (auto mon : monitors)
+ mon->notify_connect(this, conn);
+
+ if (design)
+ for (auto mon : design->monitors)
+ mon->notify_connect(this, conn);
+
+ // ignore all attempts to assign constants to other constants
+ if (conn.first.has_const()) {
+ RTLIL::SigSig new_conn;
+ for (int i = 0; i < GetSize(conn.first); i++)
+ if (conn.first[i].wire) {
+ new_conn.first.append(conn.first[i]);
+ new_conn.second.append(conn.second[i]);
+ }
+ if (GetSize(new_conn.first))
+ connect(new_conn);
+ return;
+ }
+
+ if (yosys_xtrace) {
+ log("#X# Connect (SigSig) in %s: %s = %s (%d bits)\n", log_id(this), log_signal(conn.first), log_signal(conn.second), GetSize(conn.first));
+ log_backtrace("-X- ", yosys_xtrace-1);
+ }
+
+ log_assert(GetSize(conn.first) == GetSize(conn.second));
+ connections_.push_back(conn);
+}
+
+void RTLIL::Module::connect(const RTLIL::SigSpec &lhs, const RTLIL::SigSpec &rhs)
+{
+ connect(RTLIL::SigSig(lhs, rhs));
+}
+
+void RTLIL::Module::new_connections(const std::vector<RTLIL::SigSig> &new_conn)
+{
+ for (auto mon : monitors)
+ mon->notify_connect(this, new_conn);
+
+ if (design)
+ for (auto mon : design->monitors)
+ mon->notify_connect(this, new_conn);
+
+ if (yosys_xtrace) {
+ log("#X# New connections vector in %s:\n", log_id(this));
+ for (auto &conn: new_conn)
+ log("#X# %s = %s (%d bits)\n", log_signal(conn.first), log_signal(conn.second), GetSize(conn.first));
+ log_backtrace("-X- ", yosys_xtrace-1);
+ }
+
+ connections_ = new_conn;
+}
+
+const std::vector<RTLIL::SigSig> &RTLIL::Module::connections() const
+{
+ return connections_;
+}
+
+void RTLIL::Module::fixup_ports()
+{
+ std::vector<RTLIL::Wire*> all_ports;
+
+ for (auto &w : wires_)
+ if (w.second->port_input || w.second->port_output)
+ all_ports.push_back(w.second);
+ else
+ w.second->port_id = 0;
+
+ std::sort(all_ports.begin(), all_ports.end(), fixup_ports_compare);
+
+ ports.clear();
+ for (size_t i = 0; i < all_ports.size(); i++) {
+ ports.push_back(all_ports[i]->name);
+ all_ports[i]->port_id = i+1;
+ }
+}
+
+RTLIL::Wire *RTLIL::Module::addWire(RTLIL::IdString name, int width)
+{
+ RTLIL::Wire *wire = new RTLIL::Wire;
+ wire->name = name;
+ wire->width = width;
+ add(wire);
+ return wire;
+}
+
+RTLIL::Wire *RTLIL::Module::addWire(RTLIL::IdString name, const RTLIL::Wire *other)
+{
+ RTLIL::Wire *wire = addWire(name);
+ wire->width = other->width;
+ wire->start_offset = other->start_offset;
+ wire->port_id = other->port_id;
+ wire->port_input = other->port_input;
+ wire->port_output = other->port_output;
+ wire->upto = other->upto;
+ wire->attributes = other->attributes;
+ return wire;
+}
+
+RTLIL::Cell *RTLIL::Module::addCell(RTLIL::IdString name, RTLIL::IdString type)
+{
+ RTLIL::Cell *cell = new RTLIL::Cell;
+ cell->name = name;
+ cell->type = type;
+ add(cell);
+ return cell;
+}
+
+RTLIL::Cell *RTLIL::Module::addCell(RTLIL::IdString name, const RTLIL::Cell *other)
+{
+ RTLIL::Cell *cell = addCell(name, other->type);
+ cell->connections_ = other->connections_;
+ cell->parameters = other->parameters;
+ cell->attributes = other->attributes;
+ return cell;
+}
+
+#define DEF_METHOD(_func, _y_size, _type) \
+ RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed) { \
+ RTLIL::Cell *cell = addCell(name, _type); \
+ cell->parameters["\\A_SIGNED"] = is_signed; \
+ cell->parameters["\\A_WIDTH"] = sig_a.size(); \
+ cell->parameters["\\Y_WIDTH"] = sig_y.size(); \
+ cell->setPort("\\A", sig_a); \
+ cell->setPort("\\Y", sig_y); \
+ return cell; \
+ } \
+ RTLIL::SigSpec RTLIL::Module::_func(RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed) { \
+ RTLIL::SigSpec sig_y = addWire(NEW_ID, _y_size); \
+ add ## _func(name, sig_a, sig_y, is_signed); \
+ return sig_y; \
+ }
+DEF_METHOD(Not, sig_a.size(), "$not")
+DEF_METHOD(Pos, sig_a.size(), "$pos")
+DEF_METHOD(Neg, sig_a.size(), "$neg")
+DEF_METHOD(ReduceAnd, 1, "$reduce_and")
+DEF_METHOD(ReduceOr, 1, "$reduce_or")
+DEF_METHOD(ReduceXor, 1, "$reduce_xor")
+DEF_METHOD(ReduceXnor, 1, "$reduce_xnor")
+DEF_METHOD(ReduceBool, 1, "$reduce_bool")
+DEF_METHOD(LogicNot, 1, "$logic_not")
+#undef DEF_METHOD
+
+#define DEF_METHOD(_func, _y_size, _type) \
+ RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed) { \
+ RTLIL::Cell *cell = addCell(name, _type); \
+ cell->parameters["\\A_SIGNED"] = is_signed; \
+ cell->parameters["\\B_SIGNED"] = is_signed; \
+ cell->parameters["\\A_WIDTH"] = sig_a.size(); \
+ cell->parameters["\\B_WIDTH"] = sig_b.size(); \
+ cell->parameters["\\Y_WIDTH"] = sig_y.size(); \
+ cell->setPort("\\A", sig_a); \
+ cell->setPort("\\B", sig_b); \
+ cell->setPort("\\Y", sig_y); \
+ return cell; \
+ } \
+ RTLIL::SigSpec RTLIL::Module::_func(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed) { \
+ RTLIL::SigSpec sig_y = addWire(NEW_ID, _y_size); \
+ add ## _func(name, sig_a, sig_b, sig_y, is_signed); \
+ return sig_y; \
+ }
+DEF_METHOD(And, max(sig_a.size(), sig_b.size()), "$and")
+DEF_METHOD(Or, max(sig_a.size(), sig_b.size()), "$or")
+DEF_METHOD(Xor, max(sig_a.size(), sig_b.size()), "$xor")
+DEF_METHOD(Xnor, max(sig_a.size(), sig_b.size()), "$xnor")
+DEF_METHOD(Shl, sig_a.size(), "$shl")
+DEF_METHOD(Shr, sig_a.size(), "$shr")
+DEF_METHOD(Sshl, sig_a.size(), "$sshl")
+DEF_METHOD(Sshr, sig_a.size(), "$sshr")
+DEF_METHOD(Shift, sig_a.size(), "$shift")
+DEF_METHOD(Shiftx, sig_a.size(), "$shiftx")
+DEF_METHOD(Lt, 1, "$lt")
+DEF_METHOD(Le, 1, "$le")
+DEF_METHOD(Eq, 1, "$eq")
+DEF_METHOD(Ne, 1, "$ne")
+DEF_METHOD(Eqx, 1, "$eqx")
+DEF_METHOD(Nex, 1, "$nex")
+DEF_METHOD(Ge, 1, "$ge")
+DEF_METHOD(Gt, 1, "$gt")
+DEF_METHOD(Add, max(sig_a.size(), sig_b.size()), "$add")
+DEF_METHOD(Sub, max(sig_a.size(), sig_b.size()), "$sub")
+DEF_METHOD(Mul, max(sig_a.size(), sig_b.size()), "$mul")
+DEF_METHOD(Div, max(sig_a.size(), sig_b.size()), "$div")
+DEF_METHOD(Mod, max(sig_a.size(), sig_b.size()), "$mod")
+DEF_METHOD(LogicAnd, 1, "$logic_and")
+DEF_METHOD(LogicOr, 1, "$logic_or")
+#undef DEF_METHOD
+
+#define DEF_METHOD(_func, _type, _pmux) \
+ RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_s, RTLIL::SigSpec sig_y) { \
+ RTLIL::Cell *cell = addCell(name, _type); \
+ cell->parameters["\\WIDTH"] = sig_a.size(); \
+ if (_pmux) cell->parameters["\\S_WIDTH"] = sig_s.size(); \
+ cell->setPort("\\A", sig_a); \
+ cell->setPort("\\B", sig_b); \
+ cell->setPort("\\S", sig_s); \
+ cell->setPort("\\Y", sig_y); \
+ return cell; \
+ } \
+ RTLIL::SigSpec RTLIL::Module::_func(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_s) { \
+ RTLIL::SigSpec sig_y = addWire(NEW_ID, sig_a.size()); \
+ add ## _func(name, sig_a, sig_b, sig_s, sig_y); \
+ return sig_y; \
+ }
+DEF_METHOD(Mux, "$mux", 0)
+DEF_METHOD(Pmux, "$pmux", 1)
+#undef DEF_METHOD
+
+#define DEF_METHOD_2(_func, _type, _P1, _P2) \
+ RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, RTLIL::SigBit sig1, RTLIL::SigBit sig2) { \
+ RTLIL::Cell *cell = addCell(name, _type); \
+ cell->setPort("\\" #_P1, sig1); \
+ cell->setPort("\\" #_P2, sig2); \
+ return cell; \
+ } \
+ RTLIL::SigBit RTLIL::Module::_func(RTLIL::IdString name, RTLIL::SigBit sig1) { \
+ RTLIL::SigBit sig2 = addWire(NEW_ID); \
+ add ## _func(name, sig1, sig2); \
+ return sig2; \
+ }
+#define DEF_METHOD_3(_func, _type, _P1, _P2, _P3) \
+ RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, RTLIL::SigBit sig1, RTLIL::SigBit sig2, RTLIL::SigBit sig3) { \
+ RTLIL::Cell *cell = addCell(name, _type); \
+ cell->setPort("\\" #_P1, sig1); \
+ cell->setPort("\\" #_P2, sig2); \
+ cell->setPort("\\" #_P3, sig3); \
+ return cell; \
+ } \
+ RTLIL::SigBit RTLIL::Module::_func(RTLIL::IdString name, RTLIL::SigBit sig1, RTLIL::SigBit sig2) { \
+ RTLIL::SigBit sig3 = addWire(NEW_ID); \
+ add ## _func(name, sig1, sig2, sig3); \
+ return sig3; \
+ }
+#define DEF_METHOD_4(_func, _type, _P1, _P2, _P3, _P4) \
+ RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, RTLIL::SigBit sig1, RTLIL::SigBit sig2, RTLIL::SigBit sig3, RTLIL::SigBit sig4) { \
+ RTLIL::Cell *cell = addCell(name, _type); \
+ cell->setPort("\\" #_P1, sig1); \
+ cell->setPort("\\" #_P2, sig2); \
+ cell->setPort("\\" #_P3, sig3); \
+ cell->setPort("\\" #_P4, sig4); \
+ return cell; \
+ } \
+ RTLIL::SigBit RTLIL::Module::_func(RTLIL::IdString name, RTLIL::SigBit sig1, RTLIL::SigBit sig2, RTLIL::SigBit sig3) { \
+ RTLIL::SigBit sig4 = addWire(NEW_ID); \
+ add ## _func(name, sig1, sig2, sig3, sig4); \
+ return sig4; \
+ }
+#define DEF_METHOD_5(_func, _type, _P1, _P2, _P3, _P4, _P5) \
+ RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, RTLIL::SigBit sig1, RTLIL::SigBit sig2, RTLIL::SigBit sig3, RTLIL::SigBit sig4, RTLIL::SigBit sig5) { \
+ RTLIL::Cell *cell = addCell(name, _type); \
+ cell->setPort("\\" #_P1, sig1); \
+ cell->setPort("\\" #_P2, sig2); \
+ cell->setPort("\\" #_P3, sig3); \
+ cell->setPort("\\" #_P4, sig4); \
+ cell->setPort("\\" #_P5, sig5); \
+ return cell; \
+ } \
+ RTLIL::SigBit RTLIL::Module::_func(RTLIL::IdString name, RTLIL::SigBit sig1, RTLIL::SigBit sig2, RTLIL::SigBit sig3, RTLIL::SigBit sig4) { \
+ RTLIL::SigBit sig5 = addWire(NEW_ID); \
+ add ## _func(name, sig1, sig2, sig3, sig4, sig5); \
+ return sig5; \
+ }
+DEF_METHOD_2(BufGate, "$_BUF_", A, Y)
+DEF_METHOD_2(NotGate, "$_NOT_", A, Y)
+DEF_METHOD_3(AndGate, "$_AND_", A, B, Y)
+DEF_METHOD_3(NandGate, "$_NAND_", A, B, Y)
+DEF_METHOD_3(OrGate, "$_OR_", A, B, Y)
+DEF_METHOD_3(NorGate, "$_NOR_", A, B, Y)
+DEF_METHOD_3(XorGate, "$_XOR_", A, B, Y)
+DEF_METHOD_3(XnorGate, "$_XNOR_", A, B, Y)
+DEF_METHOD_4(MuxGate, "$_MUX_", A, B, S, Y)
+DEF_METHOD_4(Aoi3Gate, "$_AOI3_", A, B, C, Y)
+DEF_METHOD_4(Oai3Gate, "$_OAI3_", A, B, C, Y)
+DEF_METHOD_5(Aoi4Gate, "$_AOI4_", A, B, C, D, Y)
+DEF_METHOD_5(Oai4Gate, "$_OAI4_", A, B, C, D, Y)
+#undef DEF_METHOD_2
+#undef DEF_METHOD_3
+#undef DEF_METHOD_4
+#undef DEF_METHOD_5
+
+RTLIL::Cell* RTLIL::Module::addPow(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool a_signed, bool b_signed)
+{
+ RTLIL::Cell *cell = addCell(name, "$pow");
+ cell->parameters["\\A_SIGNED"] = a_signed;
+ cell->parameters["\\B_SIGNED"] = b_signed;
+ cell->parameters["\\A_WIDTH"] = sig_a.size();
+ cell->parameters["\\B_WIDTH"] = sig_b.size();
+ cell->parameters["\\Y_WIDTH"] = sig_y.size();
+ cell->setPort("\\A", sig_a);
+ cell->setPort("\\B", sig_b);
+ cell->setPort("\\Y", sig_y);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addSlice(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, RTLIL::Const offset)
+{
+ RTLIL::Cell *cell = addCell(name, "$slice");
+ cell->parameters["\\A_WIDTH"] = sig_a.size();
+ cell->parameters["\\Y_WIDTH"] = sig_y.size();
+ cell->parameters["\\OFFSET"] = offset;
+ cell->setPort("\\A", sig_a);
+ cell->setPort("\\Y", sig_y);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addConcat(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y)
+{
+ RTLIL::Cell *cell = addCell(name, "$concat");
+ cell->parameters["\\A_WIDTH"] = sig_a.size();
+ cell->parameters["\\B_WIDTH"] = sig_b.size();
+ cell->setPort("\\A", sig_a);
+ cell->setPort("\\B", sig_b);
+ cell->setPort("\\Y", sig_y);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addLut(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, RTLIL::Const lut)
+{
+ RTLIL::Cell *cell = addCell(name, "$lut");
+ cell->parameters["\\LUT"] = lut;
+ cell->parameters["\\WIDTH"] = sig_a.size();
+ cell->setPort("\\A", sig_a);
+ cell->setPort("\\Y", sig_y);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addTribuf(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_y)
+{
+ RTLIL::Cell *cell = addCell(name, "$tribuf");
+ cell->parameters["\\WIDTH"] = sig_a.size();
+ cell->setPort("\\A", sig_a);
+ cell->setPort("\\EN", sig_en);
+ cell->setPort("\\Y", sig_y);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addAssert(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en)
+{
+ RTLIL::Cell *cell = addCell(name, "$assert");
+ cell->setPort("\\A", sig_a);
+ cell->setPort("\\EN", sig_en);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addAssume(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en)
+{
+ RTLIL::Cell *cell = addCell(name, "$assume");
+ cell->setPort("\\A", sig_a);
+ cell->setPort("\\EN", sig_en);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addEquiv(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y)
+{
+ RTLIL::Cell *cell = addCell(name, "$equiv");
+ cell->setPort("\\A", sig_a);
+ cell->setPort("\\B", sig_b);
+ cell->setPort("\\Y", sig_y);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addSr(RTLIL::IdString name, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, RTLIL::SigSpec sig_q, bool set_polarity, bool clr_polarity)
+{
+ RTLIL::Cell *cell = addCell(name, "$sr");
+ cell->parameters["\\SET_POLARITY"] = set_polarity;
+ cell->parameters["\\CLR_POLARITY"] = clr_polarity;
+ cell->parameters["\\WIDTH"] = sig_q.size();
+ cell->setPort("\\SET", sig_set);
+ cell->setPort("\\CLR", sig_clr);
+ cell->setPort("\\Q", sig_q);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addFf(RTLIL::IdString name, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q)
+{
+ RTLIL::Cell *cell = addCell(name, "$ff");
+ cell->parameters["\\WIDTH"] = sig_q.size();
+ cell->setPort("\\D", sig_d);
+ cell->setPort("\\Q", sig_q);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addDff(RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity)
+{
+ RTLIL::Cell *cell = addCell(name, "$dff");
+ cell->parameters["\\CLK_POLARITY"] = clk_polarity;
+ cell->parameters["\\WIDTH"] = sig_q.size();
+ cell->setPort("\\CLK", sig_clk);
+ cell->setPort("\\D", sig_d);
+ cell->setPort("\\Q", sig_q);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addDffe(RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity, bool en_polarity)
+{
+ RTLIL::Cell *cell = addCell(name, "$dffe");
+ cell->parameters["\\CLK_POLARITY"] = clk_polarity;
+ cell->parameters["\\EN_POLARITY"] = en_polarity;
+ cell->parameters["\\WIDTH"] = sig_q.size();
+ cell->setPort("\\CLK", sig_clk);
+ cell->setPort("\\EN", sig_en);
+ cell->setPort("\\D", sig_d);
+ cell->setPort("\\Q", sig_q);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addDffsr(RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr,
+ RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity, bool set_polarity, bool clr_polarity)
+{
+ RTLIL::Cell *cell = addCell(name, "$dffsr");
+ cell->parameters["\\CLK_POLARITY"] = clk_polarity;
+ cell->parameters["\\SET_POLARITY"] = set_polarity;
+ cell->parameters["\\CLR_POLARITY"] = clr_polarity;
+ cell->parameters["\\WIDTH"] = sig_q.size();
+ cell->setPort("\\CLK", sig_clk);
+ cell->setPort("\\SET", sig_set);
+ cell->setPort("\\CLR", sig_clr);
+ cell->setPort("\\D", sig_d);
+ cell->setPort("\\Q", sig_q);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addAdff(RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_arst, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q,
+ RTLIL::Const arst_value, bool clk_polarity, bool arst_polarity)
+{
+ RTLIL::Cell *cell = addCell(name, "$adff");
+ cell->parameters["\\CLK_POLARITY"] = clk_polarity;
+ cell->parameters["\\ARST_POLARITY"] = arst_polarity;
+ cell->parameters["\\ARST_VALUE"] = arst_value;
+ cell->parameters["\\WIDTH"] = sig_q.size();
+ cell->setPort("\\CLK", sig_clk);
+ cell->setPort("\\ARST", sig_arst);
+ cell->setPort("\\D", sig_d);
+ cell->setPort("\\Q", sig_q);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addDlatch(RTLIL::IdString name, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool en_polarity)
+{
+ RTLIL::Cell *cell = addCell(name, "$dlatch");
+ cell->parameters["\\EN_POLARITY"] = en_polarity;
+ cell->parameters["\\WIDTH"] = sig_q.size();
+ cell->setPort("\\EN", sig_en);
+ cell->setPort("\\D", sig_d);
+ cell->setPort("\\Q", sig_q);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addDlatchsr(RTLIL::IdString name, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr,
+ RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool en_polarity, bool set_polarity, bool clr_polarity)
+{
+ RTLIL::Cell *cell = addCell(name, "$dlatchsr");
+ cell->parameters["\\EN_POLARITY"] = en_polarity;
+ cell->parameters["\\SET_POLARITY"] = set_polarity;
+ cell->parameters["\\CLR_POLARITY"] = clr_polarity;
+ cell->parameters["\\WIDTH"] = sig_q.size();
+ cell->setPort("\\EN", sig_en);
+ cell->setPort("\\SET", sig_set);
+ cell->setPort("\\CLR", sig_clr);
+ cell->setPort("\\D", sig_d);
+ cell->setPort("\\Q", sig_q);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addFfGate(RTLIL::IdString name, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q)
+{
+ RTLIL::Cell *cell = addCell(name, "$_FF_");
+ cell->setPort("\\D", sig_d);
+ cell->setPort("\\Q", sig_q);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addDffGate(RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity)
+{
+ RTLIL::Cell *cell = addCell(name, stringf("$_DFF_%c_", clk_polarity ? 'P' : 'N'));
+ cell->setPort("\\C", sig_clk);
+ cell->setPort("\\D", sig_d);
+ cell->setPort("\\Q", sig_q);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addDffeGate(RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity, bool en_polarity)
+{
+ RTLIL::Cell *cell = addCell(name, stringf("$_DFFE_%c%c_", clk_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N'));
+ cell->setPort("\\C", sig_clk);
+ cell->setPort("\\E", sig_en);
+ cell->setPort("\\D", sig_d);
+ cell->setPort("\\Q", sig_q);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addDffsrGate(RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr,
+ RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity, bool set_polarity, bool clr_polarity)
+{
+ RTLIL::Cell *cell = addCell(name, stringf("$_DFFSR_%c%c%c_", clk_polarity ? 'P' : 'N', set_polarity ? 'P' : 'N', clr_polarity ? 'P' : 'N'));
+ cell->setPort("\\C", sig_clk);
+ cell->setPort("\\S", sig_set);
+ cell->setPort("\\R", sig_clr);
+ cell->setPort("\\D", sig_d);
+ cell->setPort("\\Q", sig_q);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addAdffGate(RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_arst, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q,
+ bool arst_value, bool clk_polarity, bool arst_polarity)
+{
+ RTLIL::Cell *cell = addCell(name, stringf("$_DFF_%c%c%c_", clk_polarity ? 'P' : 'N', arst_polarity ? 'P' : 'N', arst_value ? '1' : '0'));
+ cell->setPort("\\C", sig_clk);
+ cell->setPort("\\R", sig_arst);
+ cell->setPort("\\D", sig_d);
+ cell->setPort("\\Q", sig_q);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addDlatchGate(RTLIL::IdString name, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool en_polarity)
+{
+ RTLIL::Cell *cell = addCell(name, stringf("$_DLATCH_%c_", en_polarity ? 'P' : 'N'));
+ cell->setPort("\\E", sig_en);
+ cell->setPort("\\D", sig_d);
+ cell->setPort("\\Q", sig_q);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addDlatchsrGate(RTLIL::IdString name, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr,
+ RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool en_polarity, bool set_polarity, bool clr_polarity)
+{
+ RTLIL::Cell *cell = addCell(name, stringf("$_DLATCHSR_%c%c%c_", en_polarity ? 'P' : 'N', set_polarity ? 'P' : 'N', clr_polarity ? 'P' : 'N'));
+ cell->setPort("\\E", sig_en);
+ cell->setPort("\\S", sig_set);
+ cell->setPort("\\R", sig_clr);
+ cell->setPort("\\D", sig_d);
+ cell->setPort("\\Q", sig_q);
+ return cell;
+}
+
+RTLIL::SigSpec RTLIL::Module::Anyconst(RTLIL::IdString name, int width)
+{
+ RTLIL::SigSpec sig = addWire(NEW_ID, width);
+ Cell *cell = addCell(name, "$anyconst");
+ cell->setParam("\\WIDTH", width);
+ cell->setPort("\\Y", sig);
+ return sig;
+}
+
+RTLIL::SigSpec RTLIL::Module::Anyseq(RTLIL::IdString name, int width)
+{
+ RTLIL::SigSpec sig = addWire(NEW_ID, width);
+ Cell *cell = addCell(name, "$anyseq");
+ cell->setParam("\\WIDTH", width);
+ cell->setPort("\\Y", sig);
+ return sig;
+}
+
+RTLIL::SigSpec RTLIL::Module::Initstate(RTLIL::IdString name)
+{
+ RTLIL::SigSpec sig = addWire(NEW_ID);
+ Cell *cell = addCell(name, "$initstate");
+ cell->setPort("\\Y", sig);
+ return sig;
+}
+
+RTLIL::Wire::Wire()
+{
+ static unsigned int hashidx_count = 123456789;
+ hashidx_count = mkhash_xorshift(hashidx_count);
+ hashidx_ = hashidx_count;
+
+ module = nullptr;
+ width = 1;
+ start_offset = 0;
+ port_id = 0;
+ port_input = false;
+ port_output = false;
+ upto = false;
+}
+
+RTLIL::Memory::Memory()
+{
+ static unsigned int hashidx_count = 123456789;
+ hashidx_count = mkhash_xorshift(hashidx_count);
+ hashidx_ = hashidx_count;
+
+ width = 1;
+ size = 0;
+}
+
+RTLIL::Cell::Cell() : module(nullptr)
+{
+ static unsigned int hashidx_count = 123456789;
+ hashidx_count = mkhash_xorshift(hashidx_count);
+ hashidx_ = hashidx_count;
+
+ // log("#memtrace# %p\n", this);
+ memhasher();
+}
+
+bool RTLIL::Cell::hasPort(RTLIL::IdString portname) const
+{
+ return connections_.count(portname) != 0;
+}
+
+void RTLIL::Cell::unsetPort(RTLIL::IdString portname)
+{
+ RTLIL::SigSpec signal;
+ auto conn_it = connections_.find(portname);
+
+ if (conn_it != connections_.end())
+ {
+ for (auto mon : module->monitors)
+ mon->notify_connect(this, conn_it->first, conn_it->second, signal);
+
+ if (module->design)
+ for (auto mon : module->design->monitors)
+ mon->notify_connect(this, conn_it->first, conn_it->second, signal);
+
+ if (yosys_xtrace) {
+ log("#X# Unconnect %s.%s.%s\n", log_id(this->module), log_id(this), log_id(portname));
+ log_backtrace("-X- ", yosys_xtrace-1);
+ }
+
+ connections_.erase(conn_it);
+ }
+}
+
+void RTLIL::Cell::setPort(RTLIL::IdString portname, RTLIL::SigSpec signal)
+{
+ auto conn_it = connections_.find(portname);
+
+ if (conn_it == connections_.end()) {
+ connections_[portname] = RTLIL::SigSpec();
+ conn_it = connections_.find(portname);
+ log_assert(conn_it != connections_.end());
+ } else
+ if (conn_it->second == signal)
+ return;
+
+ for (auto mon : module->monitors)
+ mon->notify_connect(this, conn_it->first, conn_it->second, signal);
+
+ if (module->design)
+ for (auto mon : module->design->monitors)
+ mon->notify_connect(this, conn_it->first, conn_it->second, signal);
+
+ if (yosys_xtrace) {
+ log("#X# Connect %s.%s.%s = %s (%d)\n", log_id(this->module), log_id(this), log_id(portname), log_signal(signal), GetSize(signal));
+ log_backtrace("-X- ", yosys_xtrace-1);
+ }
+
+ conn_it->second = signal;
+}
+
+const RTLIL::SigSpec &RTLIL::Cell::getPort(RTLIL::IdString portname) const
+{
+ return connections_.at(portname);
+}
+
+const dict<RTLIL::IdString, RTLIL::SigSpec> &RTLIL::Cell::connections() const
+{
+ return connections_;
+}
+
+bool RTLIL::Cell::known() const
+{
+ if (yosys_celltypes.cell_known(type))
+ return true;
+ if (module && module->design && module->design->module(type))
+ return true;
+ return false;
+}
+
+bool RTLIL::Cell::input(RTLIL::IdString portname) const
+{
+ if (yosys_celltypes.cell_known(type))
+ return yosys_celltypes.cell_input(type, portname);
+ if (module && module->design) {
+ RTLIL::Module *m = module->design->module(type);
+ RTLIL::Wire *w = m ? m->wire(portname) : nullptr;
+ return w && w->port_input;
+ }
+ return false;
+}
+
+bool RTLIL::Cell::output(RTLIL::IdString portname) const
+{
+ if (yosys_celltypes.cell_known(type))
+ return yosys_celltypes.cell_output(type, portname);
+ if (module && module->design) {
+ RTLIL::Module *m = module->design->module(type);
+ RTLIL::Wire *w = m ? m->wire(portname) : nullptr;
+ return w && w->port_output;
+ }
+ return false;
+}
+
+bool RTLIL::Cell::hasParam(RTLIL::IdString paramname) const
+{
+ return parameters.count(paramname) != 0;
+}
+
+void RTLIL::Cell::unsetParam(RTLIL::IdString paramname)
+{
+ parameters.erase(paramname);
+}
+
+void RTLIL::Cell::setParam(RTLIL::IdString paramname, RTLIL::Const value)
+{
+ parameters[paramname] = value;
+}
+
+const RTLIL::Const &RTLIL::Cell::getParam(RTLIL::IdString paramname) const
+{
+ return parameters.at(paramname);
+}
+
+void RTLIL::Cell::sort()
+{
+ connections_.sort(sort_by_id_str());
+ parameters.sort(sort_by_id_str());
+ attributes.sort(sort_by_id_str());
+}
+
+void RTLIL::Cell::check()
+{
+#ifndef NDEBUG
+ InternalCellChecker checker(NULL, this);
+ checker.check();
+#endif
+}
+
+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" ||
+ type.substr(0, 9) == "$verific$" || type.substr(0, 7) == "$array:" || type.substr(0, 8) == "$extern:")
+ return;
+
+ if (type == "$mux" || type == "$pmux") {
+ parameters["\\WIDTH"] = GetSize(connections_["\\Y"]);
+ if (type == "$pmux")
+ parameters["\\S_WIDTH"] = GetSize(connections_["\\S"]);
+ check();
+ return;
+ }
+
+ if (type == "$lut" || type == "$sop") {
+ parameters["\\WIDTH"] = GetSize(connections_["\\A"]);
+ return;
+ }
+
+ if (type == "$fa") {
+ parameters["\\WIDTH"] = GetSize(connections_["\\Y"]);
+ return;
+ }
+
+ if (type == "$lcu") {
+ parameters["\\WIDTH"] = GetSize(connections_["\\CO"]);
+ return;
+ }
+
+ bool signedness_ab = !type.in("$slice", "$concat", "$macc");
+
+ if (connections_.count("\\A")) {
+ if (signedness_ab) {
+ if (set_a_signed)
+ parameters["\\A_SIGNED"] = true;
+ else if (parameters.count("\\A_SIGNED") == 0)
+ parameters["\\A_SIGNED"] = false;
+ }
+ parameters["\\A_WIDTH"] = GetSize(connections_["\\A"]);
+ }
+
+ if (connections_.count("\\B")) {
+ if (signedness_ab) {
+ if (set_b_signed)
+ parameters["\\B_SIGNED"] = true;
+ else if (parameters.count("\\B_SIGNED") == 0)
+ parameters["\\B_SIGNED"] = false;
+ }
+ parameters["\\B_WIDTH"] = GetSize(connections_["\\B"]);
+ }
+
+ if (connections_.count("\\Y"))
+ parameters["\\Y_WIDTH"] = GetSize(connections_["\\Y"]);
+
+ check();
+}
+
+RTLIL::SigChunk::SigChunk()
+{
+ wire = NULL;
+ width = 0;
+ offset = 0;
+}
+
+RTLIL::SigChunk::SigChunk(const RTLIL::Const &value)
+{
+ wire = NULL;
+ data = value.bits;
+ width = GetSize(data);
+ offset = 0;
+}
+
+RTLIL::SigChunk::SigChunk(RTLIL::Wire *wire)
+{
+ log_assert(wire != nullptr);
+ this->wire = wire;
+ this->width = wire->width;
+ this->offset = 0;
+}
+
+RTLIL::SigChunk::SigChunk(RTLIL::Wire *wire, int offset, int width)
+{
+ log_assert(wire != nullptr);
+ this->wire = wire;
+ this->width = width;
+ this->offset = offset;
+}
+
+RTLIL::SigChunk::SigChunk(const std::string &str)
+{
+ wire = NULL;
+ data = RTLIL::Const(str).bits;
+ width = GetSize(data);
+ offset = 0;
+}
+
+RTLIL::SigChunk::SigChunk(int val, int width)
+{
+ wire = NULL;
+ data = RTLIL::Const(val, width).bits;
+ this->width = GetSize(data);
+ offset = 0;
+}
+
+RTLIL::SigChunk::SigChunk(RTLIL::State bit, int width)
+{
+ wire = NULL;
+ data = RTLIL::Const(bit, width).bits;
+ this->width = GetSize(data);
+ offset = 0;
+}
+
+RTLIL::SigChunk::SigChunk(RTLIL::SigBit bit)
+{
+ wire = bit.wire;
+ offset = 0;
+ if (wire == NULL)
+ data = RTLIL::Const(bit.data).bits;
+ else
+ offset = bit.offset;
+ width = 1;
+}
+
+RTLIL::SigChunk RTLIL::SigChunk::extract(int offset, int length) const
+{
+ RTLIL::SigChunk ret;
+ if (wire) {
+ ret.wire = wire;
+ ret.offset = this->offset + offset;
+ ret.width = length;
+ } else {
+ for (int i = 0; i < length; i++)
+ ret.data.push_back(data[offset+i]);
+ ret.width = length;
+ }
+ return ret;
+}
+
+bool RTLIL::SigChunk::operator <(const RTLIL::SigChunk &other) const
+{
+ if (wire && other.wire)
+ if (wire->name != other.wire->name)
+ return wire->name < other.wire->name;
+
+ if (wire != other.wire)
+ return wire < other.wire;
+
+ if (offset != other.offset)
+ return offset < other.offset;
+
+ if (width != other.width)
+ return width < other.width;
+
+ return data < other.data;
+}
+
+bool RTLIL::SigChunk::operator ==(const RTLIL::SigChunk &other) const
+{
+ return wire == other.wire && width == other.width && offset == other.offset && data == other.data;
+}
+
+bool RTLIL::SigChunk::operator !=(const RTLIL::SigChunk &other) const
+{
+ if (*this == other)
+ return false;
+ return true;
+}
+
+RTLIL::SigSpec::SigSpec()
+{
+ width_ = 0;
+ hash_ = 0;
+}
+
+RTLIL::SigSpec::SigSpec(const RTLIL::SigSpec &other)
+{
+ *this = other;
+}
+
+RTLIL::SigSpec::SigSpec(std::initializer_list<RTLIL::SigSpec> parts)
+{
+ cover("kernel.rtlil.sigspec.init.list");
+
+ width_ = 0;
+ hash_ = 0;
+
+ std::vector<RTLIL::SigSpec> parts_vec(parts.begin(), parts.end());
+ for (auto it = parts_vec.rbegin(); it != parts_vec.rend(); it++)
+ append(*it);
+}
+
+const RTLIL::SigSpec &RTLIL::SigSpec::operator=(const RTLIL::SigSpec &other)
+{
+ cover("kernel.rtlil.sigspec.assign");
+
+ width_ = other.width_;
+ hash_ = other.hash_;
+ chunks_ = other.chunks_;
+ bits_.clear();
+
+ if (!other.bits_.empty())
+ {
+ RTLIL::SigChunk *last = NULL;
+ int last_end_offset = 0;
+
+ for (auto &bit : other.bits_) {
+ if (last && bit.wire == last->wire) {
+ if (bit.wire == NULL) {
+ last->data.push_back(bit.data);
+ last->width++;
+ continue;
+ } else if (last_end_offset == bit.offset) {
+ last_end_offset++;
+ last->width++;
+ continue;
+ }
+ }
+ chunks_.push_back(bit);
+ last = &chunks_.back();
+ last_end_offset = bit.offset + 1;
+ }
+
+ check();
+ }
+
+ return *this;
+}
+
+RTLIL::SigSpec::SigSpec(const RTLIL::Const &value)
+{
+ cover("kernel.rtlil.sigspec.init.const");
+
+ chunks_.push_back(RTLIL::SigChunk(value));
+ width_ = chunks_.back().width;
+ hash_ = 0;
+ check();
+}
+
+RTLIL::SigSpec::SigSpec(const RTLIL::SigChunk &chunk)
+{
+ cover("kernel.rtlil.sigspec.init.chunk");
+
+ chunks_.push_back(chunk);
+ width_ = chunks_.back().width;
+ hash_ = 0;
+ check();
+}
+
+RTLIL::SigSpec::SigSpec(RTLIL::Wire *wire)
+{
+ cover("kernel.rtlil.sigspec.init.wire");
+
+ chunks_.push_back(RTLIL::SigChunk(wire));
+ width_ = chunks_.back().width;
+ hash_ = 0;
+ check();
+}
+
+RTLIL::SigSpec::SigSpec(RTLIL::Wire *wire, int offset, int width)
+{
+ cover("kernel.rtlil.sigspec.init.wire_part");
+
+ chunks_.push_back(RTLIL::SigChunk(wire, offset, width));
+ width_ = chunks_.back().width;
+ hash_ = 0;
+ check();
+}
+
+RTLIL::SigSpec::SigSpec(const std::string &str)
+{
+ cover("kernel.rtlil.sigspec.init.str");
+
+ chunks_.push_back(RTLIL::SigChunk(str));
+ width_ = chunks_.back().width;
+ hash_ = 0;
+ check();
+}
+
+RTLIL::SigSpec::SigSpec(int val, int width)
+{
+ cover("kernel.rtlil.sigspec.init.int");
+
+ chunks_.push_back(RTLIL::SigChunk(val, width));
+ width_ = width;
+ hash_ = 0;
+ check();
+}
+
+RTLIL::SigSpec::SigSpec(RTLIL::State bit, int width)
+{
+ cover("kernel.rtlil.sigspec.init.state");
+
+ chunks_.push_back(RTLIL::SigChunk(bit, width));
+ width_ = width;
+ hash_ = 0;
+ check();
+}
+
+RTLIL::SigSpec::SigSpec(RTLIL::SigBit bit, int width)
+{
+ cover("kernel.rtlil.sigspec.init.bit");
+
+ if (bit.wire == NULL)
+ chunks_.push_back(RTLIL::SigChunk(bit.data, width));
+ else
+ for (int i = 0; i < width; i++)
+ chunks_.push_back(bit);
+ width_ = width;
+ hash_ = 0;
+ check();
+}
+
+RTLIL::SigSpec::SigSpec(std::vector<RTLIL::SigChunk> chunks)
+{
+ cover("kernel.rtlil.sigspec.init.stdvec_chunks");
+
+ width_ = 0;
+ hash_ = 0;
+ for (auto &c : chunks)
+ append(c);
+ check();
+}
+
+RTLIL::SigSpec::SigSpec(std::vector<RTLIL::SigBit> bits)
+{
+ cover("kernel.rtlil.sigspec.init.stdvec_bits");
+
+ width_ = 0;
+ hash_ = 0;
+ for (auto &bit : bits)
+ append_bit(bit);
+ check();
+}
+
+RTLIL::SigSpec::SigSpec(pool<RTLIL::SigBit> bits)
+{
+ cover("kernel.rtlil.sigspec.init.pool_bits");
+
+ width_ = 0;
+ hash_ = 0;
+ for (auto &bit : bits)
+ append_bit(bit);
+ check();
+}
+
+RTLIL::SigSpec::SigSpec(std::set<RTLIL::SigBit> bits)
+{
+ cover("kernel.rtlil.sigspec.init.stdset_bits");
+
+ width_ = 0;
+ hash_ = 0;
+ for (auto &bit : bits)
+ append_bit(bit);
+ check();
+}
+
+RTLIL::SigSpec::SigSpec(bool bit)
+{
+ cover("kernel.rtlil.sigspec.init.bool");
+
+ width_ = 0;
+ hash_ = 0;
+ append_bit(bit);
+ check();
+}
+
+void RTLIL::SigSpec::pack() const
+{
+ RTLIL::SigSpec *that = (RTLIL::SigSpec*)this;
+
+ if (that->bits_.empty())
+ return;
+
+ cover("kernel.rtlil.sigspec.convert.pack");
+ log_assert(that->chunks_.empty());
+
+ std::vector<RTLIL::SigBit> old_bits;
+ old_bits.swap(that->bits_);
+
+ RTLIL::SigChunk *last = NULL;
+ int last_end_offset = 0;
+
+ for (auto &bit : old_bits) {
+ if (last && bit.wire == last->wire) {
+ if (bit.wire == NULL) {
+ last->data.push_back(bit.data);
+ last->width++;
+ continue;
+ } else if (last_end_offset == bit.offset) {
+ last_end_offset++;
+ last->width++;
+ continue;
+ }
+ }
+ that->chunks_.push_back(bit);
+ last = &that->chunks_.back();
+ last_end_offset = bit.offset + 1;
+ }
+
+ check();
+}
+
+void RTLIL::SigSpec::unpack() const
+{
+ RTLIL::SigSpec *that = (RTLIL::SigSpec*)this;
+
+ if (that->chunks_.empty())
+ return;
+
+ cover("kernel.rtlil.sigspec.convert.unpack");
+ log_assert(that->bits_.empty());
+
+ that->bits_.reserve(that->width_);
+ for (auto &c : that->chunks_)
+ for (int i = 0; i < c.width; i++)
+ that->bits_.push_back(RTLIL::SigBit(c, i));
+
+ that->chunks_.clear();
+ that->hash_ = 0;
+}
+
+void RTLIL::SigSpec::updhash() const
+{
+ RTLIL::SigSpec *that = (RTLIL::SigSpec*)this;
+
+ if (that->hash_ != 0)
+ return;
+
+ cover("kernel.rtlil.sigspec.hash");
+ that->pack();
+
+ that->hash_ = mkhash_init;
+ for (auto &c : that->chunks_)
+ if (c.wire == NULL) {
+ for (auto &v : c.data)
+ that->hash_ = mkhash(that->hash_, v);
+ } else {
+ that->hash_ = mkhash(that->hash_, c.wire->name.index_);
+ that->hash_ = mkhash(that->hash_, c.offset);
+ that->hash_ = mkhash(that->hash_, c.width);
+ }
+
+ if (that->hash_ == 0)
+ that->hash_ = 1;
+}
+
+void RTLIL::SigSpec::sort()
+{
+ unpack();
+ cover("kernel.rtlil.sigspec.sort");
+ std::sort(bits_.begin(), bits_.end());
+}
+
+void RTLIL::SigSpec::sort_and_unify()
+{
+ unpack();
+ cover("kernel.rtlil.sigspec.sort_and_unify");
+
+ // A copy of the bits vector is used to prevent duplicating the logic from
+ // SigSpec::SigSpec(std::vector<SigBit>). This incurrs an extra copy but
+ // that isn't showing up as significant in profiles.
+ std::vector<SigBit> unique_bits = bits_;
+ std::sort(unique_bits.begin(), unique_bits.end());
+ auto last = std::unique(unique_bits.begin(), unique_bits.end());
+ unique_bits.erase(last, unique_bits.end());
+
+ *this = unique_bits;
+}
+
+void RTLIL::SigSpec::replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with)
+{
+ replace(pattern, with, this);
+}
+
+void RTLIL::SigSpec::replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with, RTLIL::SigSpec *other) const
+{
+ log_assert(other != NULL);
+ log_assert(width_ == other->width_);
+ log_assert(pattern.width_ == with.width_);
+
+ pattern.unpack();
+ with.unpack();
+ unpack();
+ other->unpack();
+
+ for (int i = 0; i < GetSize(pattern.bits_); i++) {
+ if (pattern.bits_[i].wire != NULL) {
+ for (int j = 0; j < GetSize(bits_); j++) {
+ if (bits_[j] == pattern.bits_[i]) {
+ other->bits_[j] = with.bits_[i];
+ }
+ }
+ }
+ }
+
+ other->check();
+}
+
+void RTLIL::SigSpec::replace(const dict<RTLIL::SigBit, RTLIL::SigBit> &rules)
+{
+ replace(rules, this);
+}
+
+void RTLIL::SigSpec::replace(const dict<RTLIL::SigBit, RTLIL::SigBit> &rules, RTLIL::SigSpec *other) const
+{
+ cover("kernel.rtlil.sigspec.replace_dict");
+
+ log_assert(other != NULL);
+ log_assert(width_ == other->width_);
+
+ unpack();
+ other->unpack();
+
+ for (int i = 0; i < GetSize(bits_); i++) {
+ auto it = rules.find(bits_[i]);
+ if (it != rules.end())
+ other->bits_[i] = it->second;
+ }
+
+ other->check();
+}
+
+void RTLIL::SigSpec::replace(const std::map<RTLIL::SigBit, RTLIL::SigBit> &rules)
+{
+ replace(rules, this);
+}
+
+void RTLIL::SigSpec::replace(const std::map<RTLIL::SigBit, RTLIL::SigBit> &rules, RTLIL::SigSpec *other) const
+{
+ cover("kernel.rtlil.sigspec.replace_map");
+
+ log_assert(other != NULL);
+ log_assert(width_ == other->width_);
+
+ unpack();
+ other->unpack();
+
+ for (int i = 0; i < GetSize(bits_); i++) {
+ auto it = rules.find(bits_[i]);
+ if (it != rules.end())
+ other->bits_[i] = it->second;
+ }
+
+ other->check();
+}
+
+void RTLIL::SigSpec::remove(const RTLIL::SigSpec &pattern)
+{
+ remove2(pattern, NULL);
+}
+
+void RTLIL::SigSpec::remove(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *other) const
+{
+ RTLIL::SigSpec tmp = *this;
+ tmp.remove2(pattern, other);
+}
+
+void RTLIL::SigSpec::remove2(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *other)
+{
+ if (other)
+ cover("kernel.rtlil.sigspec.remove_other");
+ else
+ cover("kernel.rtlil.sigspec.remove");
+
+ unpack();
+ if (other != NULL) {
+ log_assert(width_ == other->width_);
+ other->unpack();
+ }
+
+ for (int i = GetSize(bits_) - 1; i >= 0; i--) {
+ if (bits_[i].wire == NULL) continue;
+
+ for (auto &pattern_chunk : pattern.chunks()) {
+ if (bits_[i].wire == pattern_chunk.wire &&
+ bits_[i].offset >= pattern_chunk.offset &&
+ bits_[i].offset < pattern_chunk.offset + pattern_chunk.width) {
+ bits_.erase(bits_.begin() + i);
+ width_--;
+ if (other != NULL) {
+ other->bits_.erase(other->bits_.begin() + i);
+ other->width_--;
+ }
+ }
+ }
+ }
+
+ check();
+}
+
+void RTLIL::SigSpec::remove(const pool<RTLIL::SigBit> &pattern)
+{
+ remove2(pattern, NULL);
+}
+
+void RTLIL::SigSpec::remove(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other) const
+{
+ RTLIL::SigSpec tmp = *this;
+ tmp.remove2(pattern, other);
+}
+
+void RTLIL::SigSpec::remove2(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other)
+{
+ if (other)
+ cover("kernel.rtlil.sigspec.remove_other");
+ else
+ cover("kernel.rtlil.sigspec.remove");
+
+ unpack();
+
+ if (other != NULL) {
+ log_assert(width_ == other->width_);
+ other->unpack();
+ }
+
+ for (int i = GetSize(bits_) - 1; i >= 0; i--) {
+ if (bits_[i].wire != NULL && pattern.count(bits_[i])) {
+ bits_.erase(bits_.begin() + i);
+ width_--;
+ if (other != NULL) {
+ other->bits_.erase(other->bits_.begin() + i);
+ other->width_--;
+ }
+ }
+ }
+
+ check();
+}
+
+void RTLIL::SigSpec::remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other)
+{
+ if (other)
+ cover("kernel.rtlil.sigspec.remove_other");
+ else
+ cover("kernel.rtlil.sigspec.remove");
+
+ unpack();
+
+ if (other != NULL) {
+ log_assert(width_ == other->width_);
+ other->unpack();
+ }
+
+ for (int i = GetSize(bits_) - 1; i >= 0; i--) {
+ if (bits_[i].wire != NULL && pattern.count(bits_[i])) {
+ bits_.erase(bits_.begin() + i);
+ width_--;
+ if (other != NULL) {
+ other->bits_.erase(other->bits_.begin() + i);
+ other->width_--;
+ }
+ }
+ }
+
+ check();
+}
+
+RTLIL::SigSpec RTLIL::SigSpec::extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other) const
+{
+ if (other)
+ cover("kernel.rtlil.sigspec.extract_other");
+ else
+ cover("kernel.rtlil.sigspec.extract");
+
+ log_assert(other == NULL || width_ == other->width_);
+
+ RTLIL::SigSpec ret;
+ std::vector<RTLIL::SigBit> bits_match = to_sigbit_vector();
+
+ for (auto& pattern_chunk : pattern.chunks()) {
+ if (other) {
+ std::vector<RTLIL::SigBit> bits_other = other->to_sigbit_vector();
+ for (int i = 0; i < width_; i++)
+ if (bits_match[i].wire &&
+ bits_match[i].wire == pattern_chunk.wire &&
+ bits_match[i].offset >= pattern_chunk.offset &&
+ bits_match[i].offset < pattern_chunk.offset + pattern_chunk.width)
+ ret.append_bit(bits_other[i]);
+ } else {
+ for (int i = 0; i < width_; i++)
+ if (bits_match[i].wire &&
+ bits_match[i].wire == pattern_chunk.wire &&
+ bits_match[i].offset >= pattern_chunk.offset &&
+ bits_match[i].offset < pattern_chunk.offset + pattern_chunk.width)
+ ret.append_bit(bits_match[i]);
+ }
+ }
+
+ ret.check();
+ return ret;
+}
+
+RTLIL::SigSpec RTLIL::SigSpec::extract(const pool<RTLIL::SigBit> &pattern, const RTLIL::SigSpec *other) const
+{
+ if (other)
+ cover("kernel.rtlil.sigspec.extract_other");
+ else
+ cover("kernel.rtlil.sigspec.extract");
+
+ log_assert(other == NULL || width_ == other->width_);
+
+ std::vector<RTLIL::SigBit> bits_match = to_sigbit_vector();
+ RTLIL::SigSpec ret;
+
+ if (other) {
+ std::vector<RTLIL::SigBit> bits_other = other->to_sigbit_vector();
+ for (int i = 0; i < width_; i++)
+ if (bits_match[i].wire && pattern.count(bits_match[i]))
+ ret.append_bit(bits_other[i]);
+ } else {
+ for (int i = 0; i < width_; i++)
+ if (bits_match[i].wire && pattern.count(bits_match[i]))
+ ret.append_bit(bits_match[i]);
+ }
+
+ ret.check();
+ return ret;
+}
+
+void RTLIL::SigSpec::replace(int offset, const RTLIL::SigSpec &with)
+{
+ cover("kernel.rtlil.sigspec.replace_pos");
+
+ unpack();
+ with.unpack();
+
+ log_assert(offset >= 0);
+ log_assert(with.width_ >= 0);
+ log_assert(offset+with.width_ <= width_);
+
+ for (int i = 0; i < with.width_; i++)
+ bits_.at(offset + i) = with.bits_.at(i);
+
+ check();
+}
+
+void RTLIL::SigSpec::remove_const()
+{
+ if (packed())
+ {
+ cover("kernel.rtlil.sigspec.remove_const.packed");
+
+ std::vector<RTLIL::SigChunk> new_chunks;
+ new_chunks.reserve(GetSize(chunks_));
+
+ width_ = 0;
+ for (auto &chunk : chunks_)
+ if (chunk.wire != NULL) {
+ new_chunks.push_back(chunk);
+ width_ += chunk.width;
+ }
+
+ chunks_.swap(new_chunks);
+ }
+ else
+ {
+ cover("kernel.rtlil.sigspec.remove_const.unpacked");
+
+ std::vector<RTLIL::SigBit> new_bits;
+ new_bits.reserve(width_);
+
+ for (auto &bit : bits_)
+ if (bit.wire != NULL)
+ new_bits.push_back(bit);
+
+ bits_.swap(new_bits);
+ width_ = bits_.size();
+ }
+
+ check();
+}
+
+void RTLIL::SigSpec::remove(int offset, int length)
+{
+ cover("kernel.rtlil.sigspec.remove_pos");
+
+ unpack();
+
+ log_assert(offset >= 0);
+ log_assert(length >= 0);
+ log_assert(offset + length <= width_);
+
+ bits_.erase(bits_.begin() + offset, bits_.begin() + offset + length);
+ width_ = bits_.size();
+
+ check();
+}
+
+RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const
+{
+ unpack();
+ cover("kernel.rtlil.sigspec.extract_pos");
+ return std::vector<RTLIL::SigBit>(bits_.begin() + offset, bits_.begin() + offset + length);
+}
+
+void RTLIL::SigSpec::append(const RTLIL::SigSpec &signal)
+{
+ if (signal.width_ == 0)
+ return;
+
+ if (width_ == 0) {
+ *this = signal;
+ return;
+ }
+
+ cover("kernel.rtlil.sigspec.append");
+
+ if (packed() != signal.packed()) {
+ pack();
+ signal.pack();
+ }
+
+ if (packed())
+ for (auto &other_c : signal.chunks_)
+ {
+ auto &my_last_c = chunks_.back();
+ if (my_last_c.wire == NULL && other_c.wire == NULL) {
+ auto &this_data = my_last_c.data;
+ auto &other_data = other_c.data;
+ this_data.insert(this_data.end(), other_data.begin(), other_data.end());
+ my_last_c.width += other_c.width;
+ } else
+ if (my_last_c.wire == other_c.wire && my_last_c.offset + my_last_c.width == other_c.offset) {
+ my_last_c.width += other_c.width;
+ } else
+ chunks_.push_back(other_c);
+ }
+ else
+ bits_.insert(bits_.end(), signal.bits_.begin(), signal.bits_.end());
+
+ width_ += signal.width_;
+ check();
+}
+
+void RTLIL::SigSpec::append_bit(const RTLIL::SigBit &bit)
+{
+ if (packed())
+ {
+ cover("kernel.rtlil.sigspec.append_bit.packed");
+
+ if (chunks_.size() == 0)
+ chunks_.push_back(bit);
+ else
+ if (bit.wire == NULL)
+ if (chunks_.back().wire == NULL) {
+ chunks_.back().data.push_back(bit.data);
+ chunks_.back().width++;
+ } else
+ chunks_.push_back(bit);
+ else
+ if (chunks_.back().wire == bit.wire && chunks_.back().offset + chunks_.back().width == bit.offset)
+ chunks_.back().width++;
+ else
+ chunks_.push_back(bit);
+ }
+ else
+ {
+ cover("kernel.rtlil.sigspec.append_bit.unpacked");
+ bits_.push_back(bit);
+ }
+
+ width_++;
+ check();
+}
+
+void RTLIL::SigSpec::extend_u0(int width, bool is_signed)
+{
+ cover("kernel.rtlil.sigspec.extend_u0");
+
+ pack();
+
+ if (width_ > width)
+ remove(width, width_ - width);
+
+ if (width_ < width) {
+ RTLIL::SigBit padding = width_ > 0 ? (*this)[width_ - 1] : RTLIL::State::S0;
+ if (!is_signed)
+ padding = RTLIL::State::S0;
+ while (width_ < width)
+ append(padding);
+ }
+
+}
+
+RTLIL::SigSpec RTLIL::SigSpec::repeat(int num) const
+{
+ cover("kernel.rtlil.sigspec.repeat");
+
+ RTLIL::SigSpec sig;
+ for (int i = 0; i < num; i++)
+ sig.append(*this);
+ return sig;
+}
+
+#ifndef NDEBUG
+void RTLIL::SigSpec::check() const
+{
+ if (width_ > 64)
+ {
+ cover("kernel.rtlil.sigspec.check.skip");
+ }
+ else if (packed())
+ {
+ cover("kernel.rtlil.sigspec.check.packed");
+
+ int w = 0;
+ for (size_t i = 0; i < chunks_.size(); i++) {
+ const RTLIL::SigChunk chunk = chunks_[i];
+ if (chunk.wire == NULL) {
+ if (i > 0)
+ log_assert(chunks_[i-1].wire != NULL);
+ log_assert(chunk.offset == 0);
+ log_assert(chunk.data.size() == (size_t)chunk.width);
+ } else {
+ if (i > 0 && chunks_[i-1].wire == chunk.wire)
+ log_assert(chunk.offset != chunks_[i-1].offset + chunks_[i-1].width);
+ log_assert(chunk.offset >= 0);
+ log_assert(chunk.width >= 0);
+ log_assert(chunk.offset + chunk.width <= chunk.wire->width);
+ log_assert(chunk.data.size() == 0);
+ }
+ w += chunk.width;
+ }
+ log_assert(w == width_);
+ log_assert(bits_.empty());
+ }
+ else
+ {
+ cover("kernel.rtlil.sigspec.check.unpacked");
+
+ log_assert(width_ == GetSize(bits_));
+ log_assert(chunks_.empty());
+ }
+}
+#endif
+
+bool RTLIL::SigSpec::operator <(const RTLIL::SigSpec &other) const
+{
+ cover("kernel.rtlil.sigspec.comp_lt");
+
+ if (this == &other)
+ return false;
+
+ if (width_ != other.width_)
+ return width_ < other.width_;
+
+ pack();
+ other.pack();
+
+ if (chunks_.size() != other.chunks_.size())
+ return chunks_.size() < other.chunks_.size();
+
+ updhash();
+ other.updhash();
+
+ if (hash_ != other.hash_)
+ return hash_ < other.hash_;
+
+ for (size_t i = 0; i < chunks_.size(); i++)
+ if (chunks_[i] != other.chunks_[i]) {
+ cover("kernel.rtlil.sigspec.comp_lt.hash_collision");
+ return chunks_[i] < other.chunks_[i];
+ }
+
+ cover("kernel.rtlil.sigspec.comp_lt.equal");
+ return false;
+}
+
+bool RTLIL::SigSpec::operator ==(const RTLIL::SigSpec &other) const
+{
+ cover("kernel.rtlil.sigspec.comp_eq");
+
+ if (this == &other)
+ return true;
+
+ if (width_ != other.width_)
+ return false;
+
+ pack();
+ other.pack();
+
+ if (chunks_.size() != chunks_.size())
+ return false;
+
+ updhash();
+ other.updhash();
+
+ if (hash_ != other.hash_)
+ return false;
+
+ for (size_t i = 0; i < chunks_.size(); i++)
+ if (chunks_[i] != other.chunks_[i]) {
+ cover("kernel.rtlil.sigspec.comp_eq.hash_collision");
+ return false;
+ }
+
+ cover("kernel.rtlil.sigspec.comp_eq.equal");
+ return true;
+}
+
+bool RTLIL::SigSpec::is_wire() const
+{
+ cover("kernel.rtlil.sigspec.is_wire");
+
+ pack();
+ return GetSize(chunks_) == 1 && chunks_[0].wire && chunks_[0].wire->width == width_;
+}
+
+bool RTLIL::SigSpec::is_chunk() const
+{
+ cover("kernel.rtlil.sigspec.is_chunk");
+
+ pack();
+ return GetSize(chunks_) == 1;
+}
+
+bool RTLIL::SigSpec::is_fully_const() const
+{
+ cover("kernel.rtlil.sigspec.is_fully_const");
+
+ pack();
+ for (auto it = chunks_.begin(); it != chunks_.end(); it++)
+ if (it->width > 0 && it->wire != NULL)
+ return false;
+ return true;
+}
+
+bool RTLIL::SigSpec::is_fully_zero() const
+{
+ cover("kernel.rtlil.sigspec.is_fully_zero");
+
+ pack();
+ for (auto it = chunks_.begin(); it != chunks_.end(); it++) {
+ if (it->width > 0 && it->wire != NULL)
+ return false;
+ for (size_t i = 0; i < it->data.size(); i++)
+ if (it->data[i] != RTLIL::State::S0)
+ return false;
+ }
+ return true;
+}
+
+bool RTLIL::SigSpec::is_fully_def() const
+{
+ cover("kernel.rtlil.sigspec.is_fully_def");
+
+ pack();
+ for (auto it = chunks_.begin(); it != chunks_.end(); it++) {
+ if (it->width > 0 && it->wire != NULL)
+ return false;
+ for (size_t i = 0; i < it->data.size(); i++)
+ if (it->data[i] != RTLIL::State::S0 && it->data[i] != RTLIL::State::S1)
+ return false;
+ }
+ return true;
+}
+
+bool RTLIL::SigSpec::is_fully_undef() const
+{
+ cover("kernel.rtlil.sigspec.is_fully_undef");
+
+ pack();
+ for (auto it = chunks_.begin(); it != chunks_.end(); it++) {
+ if (it->width > 0 && it->wire != NULL)
+ return false;
+ for (size_t i = 0; i < it->data.size(); i++)
+ if (it->data[i] != RTLIL::State::Sx && it->data[i] != RTLIL::State::Sz)
+ return false;
+ }
+ return true;
+}
+
+bool RTLIL::SigSpec::has_const() const
+{
+ cover("kernel.rtlil.sigspec.has_const");
+
+ pack();
+ for (auto it = chunks_.begin(); it != chunks_.end(); it++)
+ if (it->width > 0 && it->wire == NULL)
+ return true;
+ return false;
+}
+
+bool RTLIL::SigSpec::has_marked_bits() const
+{
+ cover("kernel.rtlil.sigspec.has_marked_bits");
+
+ pack();
+ for (auto it = chunks_.begin(); it != chunks_.end(); it++)
+ if (it->width > 0 && it->wire == NULL) {
+ for (size_t i = 0; i < it->data.size(); i++)
+ if (it->data[i] == RTLIL::State::Sm)
+ return true;
+ }
+ return false;
+}
+
+bool RTLIL::SigSpec::as_bool() const
+{
+ cover("kernel.rtlil.sigspec.as_bool");
+
+ pack();
+ log_assert(is_fully_const() && GetSize(chunks_) <= 1);
+ if (width_)
+ return RTLIL::Const(chunks_[0].data).as_bool();
+ return false;
+}
+
+int RTLIL::SigSpec::as_int(bool is_signed) const
+{
+ cover("kernel.rtlil.sigspec.as_int");
+
+ pack();
+ log_assert(is_fully_const() && GetSize(chunks_) <= 1);
+ if (width_)
+ return RTLIL::Const(chunks_[0].data).as_int(is_signed);
+ return 0;
+}
+
+std::string RTLIL::SigSpec::as_string() const
+{
+ cover("kernel.rtlil.sigspec.as_string");
+
+ pack();
+ std::string str;
+ for (size_t i = chunks_.size(); i > 0; i--) {
+ const RTLIL::SigChunk &chunk = chunks_[i-1];
+ if (chunk.wire != NULL)
+ for (int j = 0; j < chunk.width; j++)
+ str += "?";
+ else
+ str += RTLIL::Const(chunk.data).as_string();
+ }
+ return str;
+}
+
+RTLIL::Const RTLIL::SigSpec::as_const() const
+{
+ cover("kernel.rtlil.sigspec.as_const");
+
+ pack();
+ log_assert(is_fully_const() && GetSize(chunks_) <= 1);
+ if (width_)
+ return chunks_[0].data;
+ return RTLIL::Const();
+}
+
+RTLIL::Wire *RTLIL::SigSpec::as_wire() const
+{
+ cover("kernel.rtlil.sigspec.as_wire");
+
+ pack();
+ log_assert(is_wire());
+ return chunks_[0].wire;
+}
+
+RTLIL::SigChunk RTLIL::SigSpec::as_chunk() const
+{
+ cover("kernel.rtlil.sigspec.as_chunk");
+
+ pack();
+ log_assert(is_chunk());
+ return chunks_[0];
+}
+
+RTLIL::SigBit RTLIL::SigSpec::as_bit() const
+{
+ cover("kernel.rtlil.sigspec.as_bit");
+
+ log_assert(width_ == 1);
+ if (packed())
+ return RTLIL::SigBit(*chunks_.begin());
+ else
+ return bits_[0];
+}
+
+bool RTLIL::SigSpec::match(std::string pattern) const
+{
+ cover("kernel.rtlil.sigspec.match");
+
+ pack();
+ std::string str = as_string();
+ log_assert(pattern.size() == str.size());
+
+ for (size_t i = 0; i < pattern.size(); i++) {
+ if (pattern[i] == ' ')
+ continue;
+ if (pattern[i] == '*') {
+ if (str[i] != 'z' && str[i] != 'x')
+ return false;
+ continue;
+ }
+ if (pattern[i] != str[i])
+ return false;
+ }
+
+ return true;
+}
+
+std::set<RTLIL::SigBit> RTLIL::SigSpec::to_sigbit_set() const
+{
+ cover("kernel.rtlil.sigspec.to_sigbit_set");
+
+ pack();
+ std::set<RTLIL::SigBit> sigbits;
+ for (auto &c : chunks_)
+ for (int i = 0; i < c.width; i++)
+ sigbits.insert(RTLIL::SigBit(c, i));
+ return sigbits;
+}
+
+pool<RTLIL::SigBit> RTLIL::SigSpec::to_sigbit_pool() const
+{
+ cover("kernel.rtlil.sigspec.to_sigbit_pool");
+
+ pack();
+ pool<RTLIL::SigBit> sigbits;
+ for (auto &c : chunks_)
+ for (int i = 0; i < c.width; i++)
+ sigbits.insert(RTLIL::SigBit(c, i));
+ return sigbits;
+}
+
+std::vector<RTLIL::SigBit> RTLIL::SigSpec::to_sigbit_vector() const
+{
+ cover("kernel.rtlil.sigspec.to_sigbit_vector");
+
+ unpack();
+ return bits_;
+}
+
+std::map<RTLIL::SigBit, RTLIL::SigBit> RTLIL::SigSpec::to_sigbit_map(const RTLIL::SigSpec &other) const
+{
+ cover("kernel.rtlil.sigspec.to_sigbit_map");
+
+ unpack();
+ other.unpack();
+
+ log_assert(width_ == other.width_);
+
+ std::map<RTLIL::SigBit, RTLIL::SigBit> new_map;
+ for (int i = 0; i < width_; i++)
+ new_map[bits_[i]] = other.bits_[i];
+
+ return new_map;
+}
+
+dict<RTLIL::SigBit, RTLIL::SigBit> RTLIL::SigSpec::to_sigbit_dict(const RTLIL::SigSpec &other) const
+{
+ cover("kernel.rtlil.sigspec.to_sigbit_dict");
+
+ unpack();
+ other.unpack();
+
+ log_assert(width_ == other.width_);
+
+ dict<RTLIL::SigBit, RTLIL::SigBit> new_map;
+ for (int i = 0; i < width_; i++)
+ new_map[bits_[i]] = other.bits_[i];
+
+ return new_map;
+}
+
+static void sigspec_parse_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));
+}
+
+static int sigspec_parse_get_dummy_line_num()
+{
+ return 0;
+}
+
+bool RTLIL::SigSpec::parse(RTLIL::SigSpec &sig, RTLIL::Module *module, std::string str)
+{
+ cover("kernel.rtlil.sigspec.parse");
+
+ AST::current_filename = "input";
+ AST::use_internal_line_num();
+ AST::set_line_num(0);
+
+ std::vector<std::string> tokens;
+ sigspec_parse_split(tokens, str, ',');
+
+ sig = RTLIL::SigSpec();
+ for (int tokidx = int(tokens.size())-1; tokidx >= 0; tokidx--)
+ {
+ std::string netname = tokens[tokidx];
+ std::string indices;
+
+ if (netname.size() == 0)
+ continue;
+
+ if (('0' <= netname[0] && netname[0] <= '9') || netname[0] == '\'') {
+ cover("kernel.rtlil.sigspec.parse.const");
+ AST::get_line_num = sigspec_parse_get_dummy_line_num;
+ AST::AstNode *ast = VERILOG_FRONTEND::const2ast(netname);
+ if (ast == NULL)
+ return false;
+ sig.append(RTLIL::Const(ast->bits));
+ delete ast;
+ continue;
+ }
+
+ if (module == NULL)
+ return false;
+
+ cover("kernel.rtlil.sigspec.parse.net");
+
+ if (netname[0] != '$' && netname[0] != '\\')
+ netname = "\\" + netname;
+
+ if (module->wires_.count(netname) == 0) {
+ size_t indices_pos = netname.size()-1;
+ if (indices_pos > 2 && netname[indices_pos] == ']')
+ {
+ indices_pos--;
+ while (indices_pos > 0 && ('0' <= netname[indices_pos] && netname[indices_pos] <= '9')) indices_pos--;
+ if (indices_pos > 0 && netname[indices_pos] == ':') {
+ indices_pos--;
+ while (indices_pos > 0 && ('0' <= netname[indices_pos] && netname[indices_pos] <= '9')) indices_pos--;
+ }
+ if (indices_pos > 0 && netname[indices_pos] == '[') {
+ indices = netname.substr(indices_pos);
+ netname = netname.substr(0, indices_pos);
+ }
+ }
+ }
+
+ if (module->wires_.count(netname) == 0)
+ return false;
+
+ RTLIL::Wire *wire = module->wires_.at(netname);
+ if (!indices.empty()) {
+ std::vector<std::string> index_tokens;
+ sigspec_parse_split(index_tokens, indices.substr(1, indices.size()-2), ':');
+ if (index_tokens.size() == 1) {
+ cover("kernel.rtlil.sigspec.parse.bit_sel");
+ int a = atoi(index_tokens.at(0).c_str());
+ if (a < 0 || a >= wire->width)
+ return false;
+ sig.append(RTLIL::SigSpec(wire, a));
+ } else {
+ cover("kernel.rtlil.sigspec.parse.part_sel");
+ int a = atoi(index_tokens.at(0).c_str());
+ int b = atoi(index_tokens.at(1).c_str());
+ if (a > b) {
+ int tmp = a;
+ a = b, b = tmp;
+ }
+ if (a < 0 || a >= wire->width)
+ return false;
+ if (b < 0 || b >= wire->width)
+ return false;
+ sig.append(RTLIL::SigSpec(wire, a, b-a+1));
+ }
+ } else
+ sig.append(wire);
+ }
+
+ return true;
+}
+
+bool RTLIL::SigSpec::parse_sel(RTLIL::SigSpec &sig, RTLIL::Design *design, RTLIL::Module *module, std::string str)
+{
+ if (str.empty() || str[0] != '@')
+ return parse(sig, module, str);
+
+ cover("kernel.rtlil.sigspec.parse.sel");
+
+ str = RTLIL::escape_id(str.substr(1));
+ if (design->selection_vars.count(str) == 0)
+ return false;
+
+ sig = RTLIL::SigSpec();
+ RTLIL::Selection &sel = design->selection_vars.at(str);
+ for (auto &it : module->wires_)
+ if (sel.selected_member(module->name, it.first))
+ sig.append(it.second);
+
+ return true;
+}
+
+bool RTLIL::SigSpec::parse_rhs(const RTLIL::SigSpec &lhs, RTLIL::SigSpec &sig, RTLIL::Module *module, std::string str)
+{
+ if (str == "0") {
+ cover("kernel.rtlil.sigspec.parse.rhs_zeros");
+ sig = RTLIL::SigSpec(RTLIL::State::S0, lhs.width_);
+ return true;
+ }
+
+ if (str == "~0") {
+ cover("kernel.rtlil.sigspec.parse.rhs_ones");
+ sig = RTLIL::SigSpec(RTLIL::State::S1, lhs.width_);
+ return true;
+ }
+
+ if (lhs.chunks_.size() == 1) {
+ char *p = (char*)str.c_str(), *endptr;
+ long int val = strtol(p, &endptr, 10);
+ if (endptr && endptr != p && *endptr == 0) {
+ sig = RTLIL::SigSpec(val, lhs.width_);
+ cover("kernel.rtlil.sigspec.parse.rhs_dec");
+ return true;
+ }
+ }
+
+ return parse(sig, module, str);
+}
+
+RTLIL::CaseRule::~CaseRule()
+{
+ for (auto it = switches.begin(); it != switches.end(); it++)
+ delete *it;
+}
+
+RTLIL::CaseRule *RTLIL::CaseRule::clone() const
+{
+ RTLIL::CaseRule *new_caserule = new RTLIL::CaseRule;
+ new_caserule->compare = compare;
+ new_caserule->actions = actions;
+ for (auto &it : switches)
+ new_caserule->switches.push_back(it->clone());
+ return new_caserule;
+}
+
+RTLIL::SwitchRule::~SwitchRule()
+{
+ for (auto it = cases.begin(); it != cases.end(); it++)
+ delete *it;
+}
+
+RTLIL::SwitchRule *RTLIL::SwitchRule::clone() const
+{
+ RTLIL::SwitchRule *new_switchrule = new RTLIL::SwitchRule;
+ new_switchrule->signal = signal;
+ new_switchrule->attributes = attributes;
+ for (auto &it : cases)
+ new_switchrule->cases.push_back(it->clone());
+ return new_switchrule;
+
+}
+
+RTLIL::SyncRule *RTLIL::SyncRule::clone() const
+{
+ RTLIL::SyncRule *new_syncrule = new RTLIL::SyncRule;
+ new_syncrule->type = type;
+ new_syncrule->signal = signal;
+ new_syncrule->actions = actions;
+ return new_syncrule;
+}
+
+RTLIL::Process::~Process()
+{
+ for (auto it = syncs.begin(); it != syncs.end(); it++)
+ delete *it;
+}
+
+RTLIL::Process *RTLIL::Process::clone() const
+{
+ RTLIL::Process *new_proc = new RTLIL::Process;
+
+ new_proc->name = name;
+ new_proc->attributes = attributes;
+
+ RTLIL::CaseRule *rc_ptr = root_case.clone();
+ new_proc->root_case = *rc_ptr;
+ rc_ptr->switches.clear();
+ delete rc_ptr;
+
+ for (auto &it : syncs)
+ new_proc->syncs.push_back(it->clone());
+
+ return new_proc;
+}
+
+YOSYS_NAMESPACE_END
+
diff --git a/kernel/rtlil.h b/kernel/rtlil.h
new file mode 100644
index 00000000..9430dcb3
--- /dev/null
+++ b/kernel/rtlil.h
@@ -0,0 +1,1348 @@
+/*
+ * 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"
+
+#ifndef RTLIL_H
+#define RTLIL_H
+
+YOSYS_NAMESPACE_BEGIN
+
+namespace RTLIL
+{
+ enum State : unsigned char {
+ S0 = 0,
+ S1 = 1,
+ Sx = 2, // undefined value or conflict
+ Sz = 3, // high-impedance / not-connected
+ Sa = 4, // don't care (used only in cases)
+ Sm = 5 // marker (used internally by some passes)
+ };
+
+ enum SyncType : unsigned char {
+ ST0 = 0, // level sensitive: 0
+ ST1 = 1, // level sensitive: 1
+ STp = 2, // edge sensitive: posedge
+ STn = 3, // edge sensitive: negedge
+ STe = 4, // edge sensitive: both edges
+ STa = 5, // always active
+ STg = 6, // global clock
+ STi = 7 // init
+ };
+
+ enum ConstFlags : unsigned char {
+ CONST_FLAG_NONE = 0,
+ CONST_FLAG_STRING = 1,
+ CONST_FLAG_SIGNED = 2, // only used for parameters
+ CONST_FLAG_REAL = 4 // unused -- to be used for parameters
+ };
+
+ struct Const;
+ struct AttrObject;
+ struct Selection;
+ struct Monitor;
+ struct Design;
+ struct Module;
+ struct Wire;
+ struct Memory;
+ struct Cell;
+ struct SigChunk;
+ struct SigBit;
+ struct SigSpecIterator;
+ struct SigSpecConstIterator;
+ struct SigSpec;
+ struct CaseRule;
+ struct SwitchRule;
+ struct SyncRule;
+ struct Process;
+
+ typedef std::pair<SigSpec, SigSpec> SigSig;
+
+ struct IdString
+ {
+ // the global id string cache
+
+ static struct destruct_guard_t {
+ bool ok; // POD, will be initialized to zero
+ destruct_guard_t() { ok = true; }
+ ~destruct_guard_t() { ok = false; }
+ } destruct_guard;
+
+ static std::vector<int> global_refcount_storage_;
+ static std::vector<char*> global_id_storage_;
+ static dict<char*, int, hash_cstr_ops> global_id_index_;
+ static std::vector<int> global_free_idx_list_;
+
+ static inline int get_reference(int idx)
+ {
+ global_refcount_storage_.at(idx)++;
+ return idx;
+ }
+
+ static inline int get_reference(const char *p)
+ {
+ log_assert(destruct_guard.ok);
+
+ if (p[0]) {
+ log_assert(p[1] != 0);
+ log_assert(p[0] == '$' || p[0] == '\\');
+ }
+
+ auto it = global_id_index_.find((char*)p);
+ if (it != global_id_index_.end()) {
+ global_refcount_storage_.at(it->second)++;
+ return it->second;
+ }
+
+ if (global_free_idx_list_.empty()) {
+ log_assert(global_id_storage_.size() < 0x40000000);
+ global_free_idx_list_.push_back(global_id_storage_.size());
+ global_id_storage_.push_back(nullptr);
+ global_refcount_storage_.push_back(0);
+ }
+
+ int idx = global_free_idx_list_.back();
+ global_free_idx_list_.pop_back();
+ global_id_storage_.at(idx) = strdup(p);
+ global_id_index_[global_id_storage_.at(idx)] = idx;
+ global_refcount_storage_.at(idx)++;
+
+ // Avoid Create->Delete->Create pattern
+ static IdString last_created_id;
+ put_reference(last_created_id.index_);
+ last_created_id.index_ = idx;
+ get_reference(last_created_id.index_);
+
+ if (yosys_xtrace) {
+ log("#X# New IdString '%s' with index %d.\n", p, idx);
+ log_backtrace("-X- ", yosys_xtrace-1);
+ }
+
+ return idx;
+ }
+
+ static inline void put_reference(int idx)
+ {
+ // put_reference() may be called from destructors after the destructor of
+ // global_refcount_storage_ has been run. in this case we simply do nothing.
+ if (!destruct_guard.ok)
+ return;
+
+ log_assert(global_refcount_storage_.at(idx) > 0);
+
+ if (--global_refcount_storage_.at(idx) != 0)
+ return;
+
+ if (yosys_xtrace) {
+ log("#X# Removed IdString '%s' with index %d.\n", global_id_storage_.at(idx), idx);
+ log_backtrace("-X- ", yosys_xtrace-1);
+ }
+
+ global_id_index_.erase(global_id_storage_.at(idx));
+ free(global_id_storage_.at(idx));
+ global_id_storage_.at(idx) = nullptr;
+ global_free_idx_list_.push_back(idx);
+ }
+
+ // the actual IdString object is just is a single int
+
+ int index_;
+
+ IdString() : index_(get_reference("")) { }
+ IdString(const char *str) : index_(get_reference(str)) { }
+ IdString(const IdString &str) : index_(get_reference(str.index_)) { }
+ IdString(const std::string &str) : index_(get_reference(str.c_str())) { }
+ ~IdString() { put_reference(index_); }
+
+ void operator=(const IdString &rhs) {
+ put_reference(index_);
+ index_ = get_reference(rhs.index_);
+ }
+
+ void operator=(const char *rhs) {
+ IdString id(rhs);
+ *this = id;
+ }
+
+ void operator=(const std::string &rhs) {
+ IdString id(rhs);
+ *this = id;
+ }
+
+ const char *c_str() const {
+ return global_id_storage_.at(index_);
+ }
+
+ std::string str() const {
+ return std::string(global_id_storage_.at(index_));
+ }
+
+ bool operator<(const IdString &rhs) const {
+ return index_ < rhs.index_;
+ }
+
+ bool operator==(const IdString &rhs) const { return index_ == rhs.index_; }
+ bool operator!=(const IdString &rhs) const { return index_ != rhs.index_; }
+
+ // The methods below are just convenience functions for better compatibility with std::string.
+
+ bool operator==(const std::string &rhs) const { return str() == rhs; }
+ bool operator!=(const std::string &rhs) const { return str() != rhs; }
+
+ bool operator==(const char *rhs) const { return strcmp(c_str(), rhs) == 0; }
+ bool operator!=(const char *rhs) const { return strcmp(c_str(), rhs) != 0; }
+
+ char operator[](size_t i) const {
+ const char *p = c_str();
+ for (; i != 0; i--, p++)
+ log_assert(*p != 0);
+ return *p;
+ }
+
+ std::string substr(size_t pos = 0, size_t len = std::string::npos) const {
+ if (len == std::string::npos || len >= strlen(c_str() + pos))
+ return std::string(c_str() + pos);
+ else
+ return std::string(c_str() + pos, len);
+ }
+
+ size_t size() const {
+ return str().size();
+ }
+
+ bool empty() const {
+ return c_str()[0] == 0;
+ }
+
+ void clear() {
+ *this = IdString();
+ }
+
+ unsigned int hash() const {
+ return index_;
+ }
+
+ // The following is a helper key_compare class. Instead of for example std::set<Cell*>
+ // use std::set<Cell*, IdString::compare_ptr_by_name<Cell>> if the order of cells in the
+ // set has an influence on the algorithm.
+
+ template<typename T> struct compare_ptr_by_name {
+ bool operator()(const T *a, const T *b) const {
+ return (a == nullptr || b == nullptr) ? (a < b) : (a->name < b->name);
+ }
+ };
+
+ // often one needs to check if a given IdString is part of a list (for example a list
+ // of cell types). the following functions helps with that.
+
+ template<typename T, typename... Args>
+ bool in(T first, Args... rest) const {
+ return in(first) || in(rest...);
+ }
+
+ bool in(IdString rhs) const { return *this == rhs; }
+ bool in(const char *rhs) const { return *this == rhs; }
+ bool in(const std::string &rhs) const { return *this == rhs; }
+ bool in(const pool<IdString> &rhs) const { return rhs.count(*this) != 0; }
+ };
+
+ static inline std::string escape_id(std::string str) {
+ if (str.size() > 0 && str[0] != '\\' && str[0] != '$')
+ return "\\" + str;
+ return str;
+ }
+
+ static inline std::string unescape_id(std::string str) {
+ if (str.size() < 2)
+ return str;
+ if (str[0] != '\\')
+ return str;
+ if (str[1] == '$' || str[1] == '\\')
+ return str;
+ if (str[1] >= '0' && str[1] <= '9')
+ return str;
+ return str.substr(1);
+ }
+
+ static inline std::string unescape_id(RTLIL::IdString str) {
+ return unescape_id(str.str());
+ }
+
+ static inline const char *id2cstr(const RTLIL::IdString &str) {
+ return log_id(str);
+ }
+
+ template <typename T> struct sort_by_name_id {
+ bool operator()(T *a, T *b) const {
+ return a->name < b->name;
+ }
+ };
+
+ template <typename T> struct sort_by_name_str {
+ bool operator()(T *a, T *b) const {
+ return strcmp(a->name.c_str(), b->name.c_str()) < 0;
+ }
+ };
+
+ struct sort_by_id_str {
+ bool operator()(RTLIL::IdString a, RTLIL::IdString b) const {
+ return strcmp(a.c_str(), b.c_str()) < 0;
+ }
+ };
+
+ // see calc.cc for the implementation of this functions
+ RTLIL::Const const_not (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_and (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_or (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_xor (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_xnor (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+
+ RTLIL::Const const_reduce_and (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_reduce_or (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_reduce_xor (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_reduce_xnor (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_reduce_bool (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+
+ RTLIL::Const const_logic_not (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_logic_and (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_logic_or (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+
+ RTLIL::Const const_shl (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_shr (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_sshl (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_sshr (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_shift (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_shiftx (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+
+ RTLIL::Const const_lt (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_le (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_eq (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_ne (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_eqx (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_nex (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_ge (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_gt (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+
+ RTLIL::Const const_add (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_sub (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_mul (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_div (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_mod (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_pow (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+
+ RTLIL::Const const_pos (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_neg (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+
+
+ // This iterator-range-pair is used for Design::modules(), Module::wires() and Module::cells().
+ // It maintains a reference counter that is used to make sure that the container is not modified while being iterated over.
+
+ template<typename T>
+ struct ObjIterator
+ {
+ typename dict<RTLIL::IdString, T>::iterator it;
+ dict<RTLIL::IdString, T> *list_p;
+ int *refcount_p;
+
+ ObjIterator() : list_p(nullptr), refcount_p(nullptr) {
+ }
+
+ ObjIterator(decltype(list_p) list_p, int *refcount_p) : list_p(list_p), refcount_p(refcount_p) {
+ if (list_p->empty()) {
+ this->list_p = nullptr;
+ this->refcount_p = nullptr;
+ } else {
+ it = list_p->begin();
+ (*refcount_p)++;
+ }
+ }
+
+ ObjIterator(const RTLIL::ObjIterator<T> &other) {
+ it = other.it;
+ list_p = other.list_p;
+ refcount_p = other.refcount_p;
+ if (refcount_p)
+ (*refcount_p)++;
+ }
+
+ ObjIterator &operator=(const RTLIL::ObjIterator<T> &other) {
+ if (refcount_p)
+ (*refcount_p)--;
+ it = other.it;
+ list_p = other.list_p;
+ refcount_p = other.refcount_p;
+ if (refcount_p)
+ (*refcount_p)++;
+ return *this;
+ }
+
+ ~ObjIterator() {
+ if (refcount_p)
+ (*refcount_p)--;
+ }
+
+ inline T operator*() const {
+ log_assert(list_p != nullptr);
+ return it->second;
+ }
+
+ inline bool operator!=(const RTLIL::ObjIterator<T> &other) const {
+ if (list_p == nullptr || other.list_p == nullptr)
+ return list_p != other.list_p;
+ return it != other.it;
+ }
+
+ inline void operator++() {
+ log_assert(list_p != nullptr);
+ if (++it == list_p->end()) {
+ (*refcount_p)--;
+ list_p = nullptr;
+ refcount_p = nullptr;
+ }
+ }
+ };
+
+ template<typename T>
+ struct ObjRange
+ {
+ dict<RTLIL::IdString, T> *list_p;
+ int *refcount_p;
+
+ ObjRange(decltype(list_p) list_p, int *refcount_p) : list_p(list_p), refcount_p(refcount_p) { }
+ RTLIL::ObjIterator<T> begin() { return RTLIL::ObjIterator<T>(list_p, refcount_p); }
+ RTLIL::ObjIterator<T> end() { return RTLIL::ObjIterator<T>(); }
+
+ size_t size() const {
+ return list_p->size();
+ }
+
+ operator pool<T>() const {
+ pool<T> result;
+ for (auto &it : *list_p)
+ result.insert(it.second);
+ return result;
+ }
+
+ operator std::vector<T>() const {
+ std::vector<T> result;
+ result.reserve(list_p->size());
+ for (auto &it : *list_p)
+ result.push_back(it.second);
+ return result;
+ }
+
+ pool<T> to_pool() const { return *this; }
+ std::vector<T> to_vector() const { return *this; }
+ };
+};
+
+struct RTLIL::Const
+{
+ int flags;
+ std::vector<RTLIL::State> bits;
+
+ Const();
+ Const(std::string str);
+ Const(int val, int width = 32);
+ Const(RTLIL::State bit, int width = 1);
+ Const(const std::vector<RTLIL::State> &bits) : bits(bits) { flags = CONST_FLAG_NONE; }
+ Const(const std::vector<bool> &bits);
+
+ bool operator <(const RTLIL::Const &other) const;
+ bool operator ==(const RTLIL::Const &other) const;
+ bool operator !=(const RTLIL::Const &other) const;
+
+ bool as_bool() const;
+ int as_int(bool is_signed = false) const;
+ std::string as_string() const;
+ static Const from_string(std::string str);
+
+ std::string decode_string() const;
+
+ inline int size() const { return bits.size(); }
+ inline RTLIL::State &operator[](int index) { return bits.at(index); }
+ inline const RTLIL::State &operator[](int index) const { return bits.at(index); }
+
+ inline RTLIL::Const extract(int offset, int len = 1, RTLIL::State padding = RTLIL::State::S0) const {
+ RTLIL::Const ret;
+ ret.bits.reserve(len);
+ for (int i = offset; i < offset + len; i++)
+ ret.bits.push_back(i < GetSize(bits) ? bits[i] : padding);
+ return ret;
+ }
+
+ inline unsigned int hash() const {
+ unsigned int h = mkhash_init;
+ for (auto b : bits)
+ mkhash(h, b);
+ return h;
+ }
+};
+
+struct RTLIL::AttrObject
+{
+ dict<RTLIL::IdString, RTLIL::Const> attributes;
+
+ void set_bool_attribute(RTLIL::IdString id);
+ bool get_bool_attribute(RTLIL::IdString id) const;
+ void set_strpool_attribute(RTLIL::IdString id, const pool<string> &data);
+ void add_strpool_attribute(RTLIL::IdString id, const pool<string> &data);
+ pool<string> get_strpool_attribute(RTLIL::IdString id) const;
+};
+
+struct RTLIL::SigChunk
+{
+ RTLIL::Wire *wire;
+ std::vector<RTLIL::State> data; // only used if wire == NULL, LSB at index 0
+ int width, offset;
+
+ SigChunk();
+ SigChunk(const RTLIL::Const &value);
+ SigChunk(RTLIL::Wire *wire);
+ SigChunk(RTLIL::Wire *wire, int offset, int width = 1);
+ SigChunk(const std::string &str);
+ SigChunk(int val, int width = 32);
+ SigChunk(RTLIL::State bit, int width = 1);
+ SigChunk(RTLIL::SigBit bit);
+
+ RTLIL::SigChunk extract(int offset, int length) const;
+
+ bool operator <(const RTLIL::SigChunk &other) const;
+ bool operator ==(const RTLIL::SigChunk &other) const;
+ bool operator !=(const RTLIL::SigChunk &other) const;
+};
+
+struct RTLIL::SigBit
+{
+ RTLIL::Wire *wire;
+ union {
+ RTLIL::State data; // used if wire == NULL
+ int offset; // used if wire != NULL
+ };
+
+ SigBit();
+ SigBit(RTLIL::State bit);
+ SigBit(bool bit);
+ SigBit(RTLIL::Wire *wire);
+ SigBit(RTLIL::Wire *wire, int offset);
+ SigBit(const RTLIL::SigChunk &chunk);
+ SigBit(const RTLIL::SigChunk &chunk, int index);
+ SigBit(const RTLIL::SigSpec &sig);
+
+ bool operator <(const RTLIL::SigBit &other) const;
+ bool operator ==(const RTLIL::SigBit &other) const;
+ bool operator !=(const RTLIL::SigBit &other) const;
+ unsigned int hash() const;
+};
+
+struct RTLIL::SigSpecIterator : public std::iterator<std::input_iterator_tag, RTLIL::SigSpec>
+{
+ RTLIL::SigSpec *sig_p;
+ int index;
+
+ inline RTLIL::SigBit &operator*() const;
+ inline bool operator!=(const RTLIL::SigSpecIterator &other) const { return index != other.index; }
+ inline bool operator==(const RTLIL::SigSpecIterator &other) const { return index == other.index; }
+ inline void operator++() { index++; }
+};
+
+struct RTLIL::SigSpecConstIterator : public std::iterator<std::input_iterator_tag, RTLIL::SigSpec>
+{
+ const RTLIL::SigSpec *sig_p;
+ int index;
+
+ inline const RTLIL::SigBit &operator*() const;
+ inline bool operator!=(const RTLIL::SigSpecConstIterator &other) const { return index != other.index; }
+ inline bool operator==(const RTLIL::SigSpecIterator &other) const { return index == other.index; }
+ inline void operator++() { index++; }
+};
+
+struct RTLIL::SigSpec
+{
+private:
+ int width_;
+ unsigned long hash_;
+ std::vector<RTLIL::SigChunk> chunks_; // LSB at index 0
+ std::vector<RTLIL::SigBit> bits_; // LSB at index 0
+
+ void pack() const;
+ void unpack() const;
+ void updhash() const;
+
+ inline bool packed() const {
+ return bits_.empty();
+ }
+
+ inline void inline_unpack() const {
+ if (!chunks_.empty())
+ unpack();
+ }
+
+public:
+ SigSpec();
+ SigSpec(const RTLIL::SigSpec &other);
+ SigSpec(std::initializer_list<RTLIL::SigSpec> parts);
+ const RTLIL::SigSpec &operator=(const RTLIL::SigSpec &other);
+
+ SigSpec(const RTLIL::Const &value);
+ SigSpec(const RTLIL::SigChunk &chunk);
+ SigSpec(RTLIL::Wire *wire);
+ SigSpec(RTLIL::Wire *wire, int offset, int width = 1);
+ SigSpec(const std::string &str);
+ SigSpec(int val, int width = 32);
+ SigSpec(RTLIL::State bit, int width = 1);
+ SigSpec(RTLIL::SigBit bit, int width = 1);
+ SigSpec(std::vector<RTLIL::SigChunk> chunks);
+ SigSpec(std::vector<RTLIL::SigBit> bits);
+ SigSpec(pool<RTLIL::SigBit> bits);
+ SigSpec(std::set<RTLIL::SigBit> bits);
+ SigSpec(bool bit);
+
+ SigSpec(RTLIL::SigSpec &&other) {
+ width_ = other.width_;
+ hash_ = other.hash_;
+ chunks_ = std::move(other.chunks_);
+ bits_ = std::move(other.bits_);
+ }
+
+ const RTLIL::SigSpec &operator=(RTLIL::SigSpec &&other) {
+ width_ = other.width_;
+ hash_ = other.hash_;
+ chunks_ = std::move(other.chunks_);
+ bits_ = std::move(other.bits_);
+ return *this;
+ }
+
+ size_t get_hash() const {
+ if (!hash_) hash();
+ return hash_;
+ }
+
+ inline const std::vector<RTLIL::SigChunk> &chunks() const { pack(); return chunks_; }
+ inline const std::vector<RTLIL::SigBit> &bits() const { inline_unpack(); return bits_; }
+
+ inline int size() const { return width_; }
+ inline bool empty() const { return width_ == 0; }
+
+ inline RTLIL::SigBit &operator[](int index) { inline_unpack(); return bits_.at(index); }
+ inline const RTLIL::SigBit &operator[](int index) const { inline_unpack(); return bits_.at(index); }
+
+ inline RTLIL::SigSpecIterator begin() { RTLIL::SigSpecIterator it; it.sig_p = this; it.index = 0; return it; }
+ inline RTLIL::SigSpecIterator end() { RTLIL::SigSpecIterator it; it.sig_p = this; it.index = width_; return it; }
+
+ inline RTLIL::SigSpecConstIterator begin() const { RTLIL::SigSpecConstIterator it; it.sig_p = this; it.index = 0; return it; }
+ inline RTLIL::SigSpecConstIterator end() const { RTLIL::SigSpecConstIterator it; it.sig_p = this; it.index = width_; return it; }
+
+ void sort();
+ void sort_and_unify();
+
+ void replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with);
+ void replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with, RTLIL::SigSpec *other) const;
+
+ void replace(const dict<RTLIL::SigBit, RTLIL::SigBit> &rules);
+ void replace(const dict<RTLIL::SigBit, RTLIL::SigBit> &rules, RTLIL::SigSpec *other) const;
+
+ void replace(const std::map<RTLIL::SigBit, RTLIL::SigBit> &rules);
+ void replace(const std::map<RTLIL::SigBit, RTLIL::SigBit> &rules, RTLIL::SigSpec *other) const;
+
+ void replace(int offset, const RTLIL::SigSpec &with);
+
+ void remove(const RTLIL::SigSpec &pattern);
+ void remove(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *other) const;
+ void remove2(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *other);
+
+ void remove(const pool<RTLIL::SigBit> &pattern);
+ void remove(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other) const;
+ void remove2(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other);
+ void remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other);
+
+ void remove(int offset, int length = 1);
+ void remove_const();
+
+ RTLIL::SigSpec extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other = NULL) const;
+ RTLIL::SigSpec extract(const pool<RTLIL::SigBit> &pattern, const RTLIL::SigSpec *other = NULL) const;
+ RTLIL::SigSpec extract(int offset, int length = 1) const;
+
+ void append(const RTLIL::SigSpec &signal);
+ void append_bit(const RTLIL::SigBit &bit);
+
+ void extend_u0(int width, bool is_signed = false);
+
+ RTLIL::SigSpec repeat(int num) const;
+
+ bool operator <(const RTLIL::SigSpec &other) const;
+ bool operator ==(const RTLIL::SigSpec &other) const;
+ inline bool operator !=(const RTLIL::SigSpec &other) const { return !(*this == other); }
+
+ bool is_wire() const;
+ bool is_chunk() const;
+ inline bool is_bit() const { return width_ == 1; }
+
+ bool is_fully_const() const;
+ bool is_fully_zero() const;
+ bool is_fully_def() const;
+ bool is_fully_undef() const;
+ bool has_const() const;
+ bool has_marked_bits() const;
+
+ bool as_bool() const;
+ int as_int(bool is_signed = false) const;
+ std::string as_string() const;
+ RTLIL::Const as_const() const;
+ RTLIL::Wire *as_wire() const;
+ RTLIL::SigChunk as_chunk() const;
+ RTLIL::SigBit as_bit() const;
+
+ bool match(std::string pattern) const;
+
+ std::set<RTLIL::SigBit> to_sigbit_set() const;
+ pool<RTLIL::SigBit> to_sigbit_pool() const;
+ std::vector<RTLIL::SigBit> to_sigbit_vector() const;
+ std::map<RTLIL::SigBit, RTLIL::SigBit> to_sigbit_map(const RTLIL::SigSpec &other) const;
+ dict<RTLIL::SigBit, RTLIL::SigBit> to_sigbit_dict(const RTLIL::SigSpec &other) const;
+
+ static bool parse(RTLIL::SigSpec &sig, RTLIL::Module *module, std::string str);
+ static bool parse_sel(RTLIL::SigSpec &sig, RTLIL::Design *design, RTLIL::Module *module, std::string str);
+ static bool parse_rhs(const RTLIL::SigSpec &lhs, RTLIL::SigSpec &sig, RTLIL::Module *module, std::string str);
+
+ operator std::vector<RTLIL::SigChunk>() const { return chunks(); }
+ operator std::vector<RTLIL::SigBit>() const { return bits(); }
+
+ unsigned int hash() const { if (!hash_) updhash(); return hash_; };
+
+#ifndef NDEBUG
+ void check() const;
+#else
+ void check() const { }
+#endif
+};
+
+struct RTLIL::Selection
+{
+ bool full_selection;
+ pool<RTLIL::IdString> selected_modules;
+ dict<RTLIL::IdString, pool<RTLIL::IdString>> selected_members;
+
+ Selection(bool full = true) : full_selection(full) { }
+
+ bool selected_module(RTLIL::IdString mod_name) const;
+ bool selected_whole_module(RTLIL::IdString mod_name) const;
+ bool selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) const;
+ void optimize(RTLIL::Design *design);
+
+ template<typename T1> void select(T1 *module) {
+ if (!full_selection && selected_modules.count(module->name) == 0) {
+ selected_modules.insert(module->name);
+ selected_members.erase(module->name);
+ }
+ }
+
+ template<typename T1, typename T2> void select(T1 *module, T2 *member) {
+ if (!full_selection && selected_modules.count(module->name) == 0)
+ selected_members[module->name].insert(member->name);
+ }
+
+ bool empty() const {
+ return !full_selection && selected_modules.empty() && selected_members.empty();
+ }
+};
+
+struct RTLIL::Monitor
+{
+ unsigned int hashidx_;
+ unsigned int hash() const { return hashidx_; }
+
+ Monitor() {
+ static unsigned int hashidx_count = 123456789;
+ hashidx_count = mkhash_xorshift(hashidx_count);
+ hashidx_ = hashidx_count;
+ }
+
+ virtual ~Monitor() { }
+ virtual void notify_module_add(RTLIL::Module*) { }
+ virtual void notify_module_del(RTLIL::Module*) { }
+ virtual void notify_connect(RTLIL::Cell*, const RTLIL::IdString&, const RTLIL::SigSpec&, RTLIL::SigSpec&) { }
+ virtual void notify_connect(RTLIL::Module*, const RTLIL::SigSig&) { }
+ virtual void notify_connect(RTLIL::Module*, const std::vector<RTLIL::SigSig>&) { }
+ virtual void notify_blackout(RTLIL::Module*) { }
+};
+
+struct RTLIL::Design
+{
+ unsigned int hashidx_;
+ unsigned int hash() const { return hashidx_; }
+
+ pool<RTLIL::Monitor*> monitors;
+ dict<std::string, std::string> scratchpad;
+
+ int refcount_modules_;
+ dict<RTLIL::IdString, RTLIL::Module*> modules_;
+ std::vector<AST::AstNode*> verilog_packages;
+
+ std::vector<RTLIL::Selection> selection_stack;
+ dict<RTLIL::IdString, RTLIL::Selection> selection_vars;
+ std::string selected_active_module;
+
+ Design();
+ ~Design();
+
+ RTLIL::ObjRange<RTLIL::Module*> modules();
+ RTLIL::Module *module(RTLIL::IdString name);
+ RTLIL::Module *top_module();
+
+ bool has(RTLIL::IdString id) const {
+ return modules_.count(id) != 0;
+ }
+
+ void add(RTLIL::Module *module);
+ RTLIL::Module *addModule(RTLIL::IdString name);
+ void remove(RTLIL::Module *module);
+ void rename(RTLIL::Module *module, RTLIL::IdString new_name);
+
+ void scratchpad_unset(std::string varname);
+
+ void scratchpad_set_int(std::string varname, int value);
+ void scratchpad_set_bool(std::string varname, bool value);
+ void scratchpad_set_string(std::string varname, std::string value);
+
+ int scratchpad_get_int(std::string varname, int default_value = 0) const;
+ bool scratchpad_get_bool(std::string varname, bool default_value = false) const;
+ std::string scratchpad_get_string(std::string varname, std::string default_value = std::string()) const;
+
+ void sort();
+ void check();
+ void optimize();
+
+ bool selected_module(RTLIL::IdString mod_name) const;
+ bool selected_whole_module(RTLIL::IdString mod_name) const;
+ bool selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) const;
+
+ bool selected_module(RTLIL::Module *mod) const;
+ bool selected_whole_module(RTLIL::Module *mod) const;
+
+ RTLIL::Selection &selection() {
+ return selection_stack.back();
+ }
+
+ const RTLIL::Selection &selection() const {
+ return selection_stack.back();
+ }
+
+ bool full_selection() const {
+ return selection_stack.back().full_selection;
+ }
+
+ template<typename T1> bool selected(T1 *module) const {
+ return selected_module(module->name);
+ }
+
+ template<typename T1, typename T2> bool selected(T1 *module, T2 *member) const {
+ return selected_member(module->name, member->name);
+ }
+
+ template<typename T1, typename T2> void select(T1 *module, T2 *member) {
+ if (selection_stack.size() > 0) {
+ RTLIL::Selection &sel = selection_stack.back();
+ sel.select(module, member);
+ }
+ }
+
+ std::vector<RTLIL::Module*> selected_modules() const;
+ std::vector<RTLIL::Module*> selected_whole_modules() const;
+ std::vector<RTLIL::Module*> selected_whole_modules_warn() const;
+};
+
+struct RTLIL::Module : public RTLIL::AttrObject
+{
+ unsigned int hashidx_;
+ unsigned int hash() const { return hashidx_; }
+
+protected:
+ void add(RTLIL::Wire *wire);
+ void add(RTLIL::Cell *cell);
+
+public:
+ RTLIL::Design *design;
+ pool<RTLIL::Monitor*> monitors;
+
+ int refcount_wires_;
+ int refcount_cells_;
+
+ dict<RTLIL::IdString, RTLIL::Wire*> wires_;
+ dict<RTLIL::IdString, RTLIL::Cell*> cells_;
+ std::vector<RTLIL::SigSig> connections_;
+
+ RTLIL::IdString name;
+ pool<RTLIL::IdString> avail_parameters;
+ dict<RTLIL::IdString, RTLIL::Memory*> memories;
+ dict<RTLIL::IdString, RTLIL::Process*> processes;
+
+ Module();
+ virtual ~Module();
+ virtual RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters);
+ virtual size_t count_id(RTLIL::IdString id);
+
+ virtual void sort();
+ virtual void check();
+ virtual void optimize();
+
+ void connect(const RTLIL::SigSig &conn);
+ void connect(const RTLIL::SigSpec &lhs, const RTLIL::SigSpec &rhs);
+ void new_connections(const std::vector<RTLIL::SigSig> &new_conn);
+ const std::vector<RTLIL::SigSig> &connections() const;
+
+ std::vector<RTLIL::IdString> ports;
+ void fixup_ports();
+
+ template<typename T> void rewrite_sigspecs(T functor);
+ void cloneInto(RTLIL::Module *new_mod) const;
+ virtual RTLIL::Module *clone() const;
+
+ bool has_memories() const;
+ bool has_processes() const;
+
+ bool has_memories_warn() const;
+ bool has_processes_warn() const;
+
+ std::vector<RTLIL::Wire*> selected_wires() const;
+ std::vector<RTLIL::Cell*> selected_cells() const;
+
+ template<typename T> bool selected(T *member) const {
+ return design->selected_member(name, member->name);
+ }
+
+ RTLIL::Wire* wire(RTLIL::IdString id) { return wires_.count(id) ? wires_.at(id) : nullptr; }
+ RTLIL::Cell* cell(RTLIL::IdString id) { return cells_.count(id) ? cells_.at(id) : nullptr; }
+
+ RTLIL::ObjRange<RTLIL::Wire*> wires() { return RTLIL::ObjRange<RTLIL::Wire*>(&wires_, &refcount_wires_); }
+ RTLIL::ObjRange<RTLIL::Cell*> cells() { return RTLIL::ObjRange<RTLIL::Cell*>(&cells_, &refcount_cells_); }
+
+ // Removing wires is expensive. If you have to remove wires, remove them all at once.
+ void remove(const pool<RTLIL::Wire*> &wires);
+ void remove(RTLIL::Cell *cell);
+
+ void rename(RTLIL::Wire *wire, RTLIL::IdString new_name);
+ void rename(RTLIL::Cell *cell, RTLIL::IdString new_name);
+ void rename(RTLIL::IdString old_name, RTLIL::IdString new_name);
+
+ void swap_names(RTLIL::Wire *w1, RTLIL::Wire *w2);
+ void swap_names(RTLIL::Cell *c1, RTLIL::Cell *c2);
+
+ RTLIL::IdString uniquify(RTLIL::IdString name);
+ RTLIL::IdString uniquify(RTLIL::IdString name, int &index);
+
+ RTLIL::Wire *addWire(RTLIL::IdString name, int width = 1);
+ RTLIL::Wire *addWire(RTLIL::IdString name, const RTLIL::Wire *other);
+
+ RTLIL::Cell *addCell(RTLIL::IdString name, RTLIL::IdString type);
+ RTLIL::Cell *addCell(RTLIL::IdString name, const RTLIL::Cell *other);
+
+ // The add* methods create a cell and return the created cell. All signals must exist in advance.
+
+ RTLIL::Cell* addNot (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addPos (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addNeg (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false);
+
+ RTLIL::Cell* addAnd (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addOr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addXor (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addXnor (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+
+ RTLIL::Cell* addReduceAnd (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addReduceOr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addReduceXor (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addReduceXnor (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addReduceBool (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false);
+
+ RTLIL::Cell* addShl (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addShr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addSshl (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addSshr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addShift (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addShiftx (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+
+ RTLIL::Cell* addLt (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addLe (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addEq (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addNe (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addEqx (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addNex (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addGe (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addGt (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+
+ RTLIL::Cell* addAdd (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addSub (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addMul (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addDiv (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addMod (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addPow (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool a_signed = false, bool b_signed = false);
+
+ RTLIL::Cell* addLogicNot (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addLogicAnd (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+ RTLIL::Cell* addLogicOr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false);
+
+ RTLIL::Cell* addMux (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_s, RTLIL::SigSpec sig_y);
+ RTLIL::Cell* addPmux (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_s, RTLIL::SigSpec sig_y);
+
+ RTLIL::Cell* addSlice (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, RTLIL::Const offset);
+ RTLIL::Cell* addConcat (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y);
+ RTLIL::Cell* addLut (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, RTLIL::Const lut);
+ RTLIL::Cell* addTribuf (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_y);
+ RTLIL::Cell* addAssert (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en);
+ RTLIL::Cell* addAssume (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en);
+ RTLIL::Cell* addEquiv (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y);
+
+ RTLIL::Cell* addSr (RTLIL::IdString name, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, RTLIL::SigSpec sig_q, bool set_polarity = true, bool clr_polarity = true);
+ RTLIL::Cell* addFf (RTLIL::IdString name, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q);
+ RTLIL::Cell* addDff (RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity = true);
+ RTLIL::Cell* addDffe (RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity = true, bool en_polarity = true);
+ RTLIL::Cell* addDffsr (RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr,
+ RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity = true, bool set_polarity = true, bool clr_polarity = true);
+ RTLIL::Cell* addAdff (RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_arst, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q,
+ RTLIL::Const arst_value, bool clk_polarity = true, bool arst_polarity = true);
+ RTLIL::Cell* addDlatch (RTLIL::IdString name, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool en_polarity = true);
+ RTLIL::Cell* addDlatchsr (RTLIL::IdString name, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr,
+ RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool en_polarity = true, bool set_polarity = true, bool clr_polarity = true);
+
+ RTLIL::Cell* addBufGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_y);
+ RTLIL::Cell* addNotGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_y);
+ RTLIL::Cell* addAndGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_y);
+ RTLIL::Cell* addNandGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_y);
+ RTLIL::Cell* addOrGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_y);
+ RTLIL::Cell* addNorGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_y);
+ RTLIL::Cell* addXorGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_y);
+ RTLIL::Cell* addXnorGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_y);
+ RTLIL::Cell* addMuxGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_s, RTLIL::SigBit sig_y);
+ RTLIL::Cell* addAoi3Gate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_c, RTLIL::SigBit sig_y);
+ RTLIL::Cell* addOai3Gate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_c, RTLIL::SigBit sig_y);
+ RTLIL::Cell* addAoi4Gate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_c, RTLIL::SigBit sig_d, RTLIL::SigBit sig_y);
+ RTLIL::Cell* addOai4Gate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_c, RTLIL::SigBit sig_d, RTLIL::SigBit sig_y);
+
+ RTLIL::Cell* addFfGate (RTLIL::IdString name, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q);
+ RTLIL::Cell* addDffGate (RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity = true);
+ RTLIL::Cell* addDffeGate (RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity = true, bool en_polarity = true);
+ RTLIL::Cell* addDffsrGate (RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr,
+ RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity = true, bool set_polarity = true, bool clr_polarity = true);
+ RTLIL::Cell* addAdffGate (RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_arst, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q,
+ bool arst_value = false, bool clk_polarity = true, bool arst_polarity = true);
+ RTLIL::Cell* addDlatchGate (RTLIL::IdString name, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool en_polarity = true);
+ RTLIL::Cell* addDlatchsrGate (RTLIL::IdString name, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr,
+ RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool en_polarity = true, bool set_polarity = true, bool clr_polarity = true);
+
+ // The methods without the add* prefix create a cell and an output signal. They return the newly created output signal.
+
+ RTLIL::SigSpec Not (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false);
+ RTLIL::SigSpec Pos (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false);
+ RTLIL::SigSpec Bu0 (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false);
+ RTLIL::SigSpec Neg (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false);
+
+ RTLIL::SigSpec And (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Or (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Xor (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Xnor (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+
+ RTLIL::SigSpec ReduceAnd (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false);
+ RTLIL::SigSpec ReduceOr (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false);
+ RTLIL::SigSpec ReduceXor (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false);
+ RTLIL::SigSpec ReduceXnor (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false);
+ RTLIL::SigSpec ReduceBool (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false);
+
+ RTLIL::SigSpec Shl (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Shr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Sshl (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Sshr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Shift (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Shiftx (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+
+ RTLIL::SigSpec Lt (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Le (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Eq (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Ne (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Eqx (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Nex (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Ge (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Gt (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+
+ RTLIL::SigSpec Add (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Sub (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Mul (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Div (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Mod (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec Pow (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool a_signed = false, bool b_signed = false);
+
+ RTLIL::SigSpec LogicNot (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false);
+ RTLIL::SigSpec LogicAnd (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+ RTLIL::SigSpec LogicOr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false);
+
+ RTLIL::SigSpec Mux (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_s);
+ RTLIL::SigSpec Pmux (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_s);
+
+ RTLIL::SigBit BufGate (RTLIL::IdString name, RTLIL::SigBit sig_a);
+ RTLIL::SigBit NotGate (RTLIL::IdString name, RTLIL::SigBit sig_a);
+ RTLIL::SigBit AndGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b);
+ RTLIL::SigBit NandGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b);
+ RTLIL::SigBit OrGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b);
+ RTLIL::SigBit NorGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b);
+ RTLIL::SigBit XorGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b);
+ RTLIL::SigBit XnorGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b);
+ RTLIL::SigBit MuxGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_s);
+ RTLIL::SigBit Aoi3Gate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_c);
+ RTLIL::SigBit Oai3Gate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_c);
+ RTLIL::SigBit Aoi4Gate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_c, RTLIL::SigBit sig_d);
+ RTLIL::SigBit Oai4Gate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_c, RTLIL::SigBit sig_d);
+
+ RTLIL::SigSpec Anyconst (RTLIL::IdString name, int width = 1);
+ RTLIL::SigSpec Anyseq (RTLIL::IdString name, int width = 1);
+ RTLIL::SigSpec Initstate (RTLIL::IdString name);
+};
+
+struct RTLIL::Wire : public RTLIL::AttrObject
+{
+ unsigned int hashidx_;
+ unsigned int hash() const { return hashidx_; }
+
+protected:
+ // use module->addWire() and module->remove() to create or destroy wires
+ friend struct RTLIL::Module;
+ Wire();
+ ~Wire() { };
+
+public:
+ // do not simply copy wires
+ Wire(RTLIL::Wire &other) = delete;
+ void operator=(RTLIL::Wire &other) = delete;
+
+ RTLIL::Module *module;
+ RTLIL::IdString name;
+ int width, start_offset, port_id;
+ bool port_input, port_output, upto;
+};
+
+struct RTLIL::Memory : public RTLIL::AttrObject
+{
+ unsigned int hashidx_;
+ unsigned int hash() const { return hashidx_; }
+
+ Memory();
+
+ RTLIL::IdString name;
+ int width, start_offset, size;
+};
+
+struct RTLIL::Cell : public RTLIL::AttrObject
+{
+ unsigned int hashidx_;
+ unsigned int hash() const { return hashidx_; }
+
+protected:
+ // use module->addCell() and module->remove() to create or destroy cells
+ friend struct RTLIL::Module;
+ Cell();
+
+public:
+ // do not simply copy cells
+ Cell(RTLIL::Cell &other) = delete;
+ void operator=(RTLIL::Cell &other) = delete;
+
+ RTLIL::Module *module;
+ RTLIL::IdString name;
+ RTLIL::IdString type;
+ dict<RTLIL::IdString, RTLIL::SigSpec> connections_;
+ dict<RTLIL::IdString, RTLIL::Const> parameters;
+
+ // access cell ports
+ bool hasPort(RTLIL::IdString portname) const;
+ void unsetPort(RTLIL::IdString portname);
+ void setPort(RTLIL::IdString portname, RTLIL::SigSpec signal);
+ const RTLIL::SigSpec &getPort(RTLIL::IdString portname) const;
+ const dict<RTLIL::IdString, RTLIL::SigSpec> &connections() const;
+
+ // information about cell ports
+ bool known() const;
+ bool input(RTLIL::IdString portname) const;
+ bool output(RTLIL::IdString portname) const;
+
+ // access cell parameters
+ bool hasParam(RTLIL::IdString paramname) const;
+ void unsetParam(RTLIL::IdString paramname);
+ void setParam(RTLIL::IdString paramname, RTLIL::Const value);
+ const RTLIL::Const &getParam(RTLIL::IdString paramname) const;
+
+ void sort();
+ void check();
+ void fixup_parameters(bool set_a_signed = false, bool set_b_signed = false);
+
+ bool has_keep_attr() const {
+ return get_bool_attribute("\\keep") || (module && module->design && module->design->module(type) &&
+ module->design->module(type)->get_bool_attribute("\\keep"));
+ }
+
+ template<typename T> void rewrite_sigspecs(T functor);
+};
+
+struct RTLIL::CaseRule
+{
+ std::vector<RTLIL::SigSpec> compare;
+ std::vector<RTLIL::SigSig> actions;
+ std::vector<RTLIL::SwitchRule*> switches;
+
+ ~CaseRule();
+ void optimize();
+
+ template<typename T> void rewrite_sigspecs(T functor);
+ RTLIL::CaseRule *clone() const;
+};
+
+struct RTLIL::SwitchRule : public RTLIL::AttrObject
+{
+ RTLIL::SigSpec signal;
+ std::vector<RTLIL::CaseRule*> cases;
+
+ ~SwitchRule();
+
+ template<typename T> void rewrite_sigspecs(T functor);
+ RTLIL::SwitchRule *clone() const;
+};
+
+struct RTLIL::SyncRule
+{
+ RTLIL::SyncType type;
+ RTLIL::SigSpec signal;
+ std::vector<RTLIL::SigSig> actions;
+
+ template<typename T> void rewrite_sigspecs(T functor);
+ RTLIL::SyncRule *clone() const;
+};
+
+struct RTLIL::Process : public RTLIL::AttrObject
+{
+ RTLIL::IdString name;
+ RTLIL::CaseRule root_case;
+ std::vector<RTLIL::SyncRule*> syncs;
+
+ ~Process();
+
+ template<typename T> void rewrite_sigspecs(T functor);
+ RTLIL::Process *clone() const;
+};
+
+
+inline RTLIL::SigBit::SigBit() : wire(NULL), data(RTLIL::State::S0) { }
+inline RTLIL::SigBit::SigBit(RTLIL::State bit) : wire(NULL), data(bit) { }
+inline RTLIL::SigBit::SigBit(bool bit) : wire(NULL), data(bit ? RTLIL::S1 : RTLIL::S0) { }
+inline RTLIL::SigBit::SigBit(RTLIL::Wire *wire) : wire(wire), offset(0) { log_assert(wire && wire->width == 1); }
+inline RTLIL::SigBit::SigBit(RTLIL::Wire *wire, int offset) : wire(wire), offset(offset) { log_assert(wire != nullptr); }
+inline RTLIL::SigBit::SigBit(const RTLIL::SigChunk &chunk) : wire(chunk.wire) { log_assert(chunk.width == 1); if (wire) offset = chunk.offset; else data = chunk.data[0]; }
+inline RTLIL::SigBit::SigBit(const RTLIL::SigChunk &chunk, int index) : wire(chunk.wire) { if (wire) offset = chunk.offset + index; else data = chunk.data[index]; }
+
+inline bool RTLIL::SigBit::operator<(const RTLIL::SigBit &other) const {
+ if (wire == other.wire)
+ return wire ? (offset < other.offset) : (data < other.data);
+ if (wire != nullptr && other.wire != nullptr)
+ return wire->name < other.wire->name;
+ return wire < other.wire;
+}
+
+inline bool RTLIL::SigBit::operator==(const RTLIL::SigBit &other) const {
+ return (wire == other.wire) && (wire ? (offset == other.offset) : (data == other.data));
+}
+
+inline bool RTLIL::SigBit::operator!=(const RTLIL::SigBit &other) const {
+ return (wire != other.wire) || (wire ? (offset != other.offset) : (data != other.data));
+}
+
+inline unsigned int RTLIL::SigBit::hash() const {
+ if (wire)
+ return mkhash_add(wire->name.hash(), offset);
+ return data;
+}
+
+inline RTLIL::SigBit &RTLIL::SigSpecIterator::operator*() const {
+ return (*sig_p)[index];
+}
+
+inline const RTLIL::SigBit &RTLIL::SigSpecConstIterator::operator*() const {
+ return (*sig_p)[index];
+}
+
+inline RTLIL::SigBit::SigBit(const RTLIL::SigSpec &sig) {
+ log_assert(sig.size() == 1 && sig.chunks().size() == 1);
+ *this = SigBit(sig.chunks().front());
+}
+
+template<typename T>
+void RTLIL::Module::rewrite_sigspecs(T functor)
+{
+ for (auto &it : cells_)
+ it.second->rewrite_sigspecs(functor);
+ for (auto &it : processes)
+ it.second->rewrite_sigspecs(functor);
+ for (auto &it : connections_) {
+ functor(it.first);
+ functor(it.second);
+ }
+}
+
+template<typename T>
+void RTLIL::Cell::rewrite_sigspecs(T functor) {
+ for (auto &it : connections_)
+ functor(it.second);
+}
+
+template<typename T>
+void RTLIL::CaseRule::rewrite_sigspecs(T functor) {
+ for (auto &it : compare)
+ functor(it);
+ for (auto &it : actions) {
+ functor(it.first);
+ functor(it.second);
+ }
+ for (auto it : switches)
+ it->rewrite_sigspecs(functor);
+}
+
+template<typename T>
+void RTLIL::SwitchRule::rewrite_sigspecs(T functor)
+{
+ functor(signal);
+ for (auto it : cases)
+ it->rewrite_sigspecs(functor);
+}
+
+template<typename T>
+void RTLIL::SyncRule::rewrite_sigspecs(T functor)
+{
+ functor(signal);
+ for (auto &it : actions) {
+ functor(it.first);
+ functor(it.second);
+ }
+}
+
+template<typename T>
+void RTLIL::Process::rewrite_sigspecs(T functor)
+{
+ root_case.rewrite_sigspecs(functor);
+ for (auto it : syncs)
+ it->rewrite_sigspecs(functor);
+}
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/kernel/satgen.h b/kernel/satgen.h
new file mode 100644
index 00000000..690f8e33
--- /dev/null
+++ b/kernel/satgen.h
@@ -0,0 +1,1411 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef SATGEN_H
+#define SATGEN_H
+
+#include "kernel/rtlil.h"
+#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
+#include "kernel/macc.h"
+
+#include "libs/ezsat/ezminisat.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+// defined in kernel/register.cc
+extern struct SatSolver *yosys_satsolver_list;
+extern struct SatSolver *yosys_satsolver;
+
+struct SatSolver
+{
+ string name;
+ SatSolver *next;
+ virtual ezSAT *create() = 0;
+
+ SatSolver(string name) : name(name) {
+ next = yosys_satsolver_list;
+ yosys_satsolver_list = this;
+ }
+
+ virtual ~SatSolver() {
+ auto p = &yosys_satsolver_list;
+ while (*p) {
+ if (*p == this)
+ *p = next;
+ else
+ p = &(*p)->next;
+ }
+ if (yosys_satsolver == this)
+ yosys_satsolver = yosys_satsolver_list;
+ }
+};
+
+struct ezSatPtr : public std::unique_ptr<ezSAT> {
+ ezSatPtr() : unique_ptr<ezSAT>(yosys_satsolver->create()) { }
+};
+
+struct SatGen
+{
+ ezSAT *ez;
+ SigMap *sigmap;
+ std::string prefix;
+ SigPool initial_state;
+ std::map<std::string, RTLIL::SigSpec> asserts_a, asserts_en;
+ std::map<std::string, RTLIL::SigSpec> assumes_a, assumes_en;
+ std::map<std::string, std::map<RTLIL::SigBit, int>> imported_signals;
+ std::map<std::pair<std::string, int>, bool> initstates;
+ bool ignore_div_by_zero;
+ bool model_undef;
+
+ SatGen(ezSAT *ez, SigMap *sigmap, std::string prefix = std::string()) :
+ ez(ez), sigmap(sigmap), prefix(prefix), ignore_div_by_zero(false), model_undef(false)
+ {
+ }
+
+ void setContext(SigMap *sigmap, std::string prefix = std::string())
+ {
+ this->sigmap = sigmap;
+ this->prefix = prefix;
+ }
+
+ std::vector<int> importSigSpecWorker(RTLIL::SigSpec sig, std::string &pf, bool undef_mode, bool dup_undef)
+ {
+ log_assert(!undef_mode || model_undef);
+ sigmap->apply(sig);
+
+ std::vector<int> vec;
+ vec.reserve(GetSize(sig));
+
+ for (auto &bit : sig)
+ if (bit.wire == NULL) {
+ if (model_undef && dup_undef && bit == RTLIL::State::Sx)
+ vec.push_back(ez->frozen_literal());
+ else
+ vec.push_back(bit == (undef_mode ? RTLIL::State::Sx : RTLIL::State::S1) ? ez->CONST_TRUE : ez->CONST_FALSE);
+ } else {
+ std::string name = pf + (bit.wire->width == 1 ? stringf("%s", log_id(bit.wire)) : stringf("%s [%d]", log_id(bit.wire->name), bit.offset));
+ vec.push_back(ez->frozen_literal(name));
+ imported_signals[pf][bit] = vec.back();
+ }
+ return vec;
+ }
+
+ std::vector<int> importSigSpec(RTLIL::SigSpec sig, int timestep = -1)
+ {
+ log_assert(timestep != 0);
+ std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep));
+ return importSigSpecWorker(sig, pf, false, false);
+ }
+
+ std::vector<int> importDefSigSpec(RTLIL::SigSpec sig, int timestep = -1)
+ {
+ log_assert(timestep != 0);
+ std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep));
+ return importSigSpecWorker(sig, pf, false, true);
+ }
+
+ std::vector<int> importUndefSigSpec(RTLIL::SigSpec sig, int timestep = -1)
+ {
+ log_assert(timestep != 0);
+ std::string pf = "undef:" + prefix + (timestep == -1 ? "" : stringf("@%d:", timestep));
+ return importSigSpecWorker(sig, pf, true, false);
+ }
+
+ int importSigBit(RTLIL::SigBit bit, int timestep = -1)
+ {
+ log_assert(timestep != 0);
+ std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep));
+ return importSigSpecWorker(bit, pf, false, false).front();
+ }
+
+ int importDefSigBit(RTLIL::SigBit bit, int timestep = -1)
+ {
+ log_assert(timestep != 0);
+ std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep));
+ return importSigSpecWorker(bit, pf, false, true).front();
+ }
+
+ int importUndefSigBit(RTLIL::SigBit bit, int timestep = -1)
+ {
+ log_assert(timestep != 0);
+ std::string pf = "undef:" + prefix + (timestep == -1 ? "" : stringf("@%d:", timestep));
+ return importSigSpecWorker(bit, pf, true, false).front();
+ }
+
+ bool importedSigBit(RTLIL::SigBit bit, int timestep = -1)
+ {
+ log_assert(timestep != 0);
+ std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep));
+ return imported_signals[pf].count(bit) != 0;
+ }
+
+ void getAsserts(RTLIL::SigSpec &sig_a, RTLIL::SigSpec &sig_en, int timestep = -1)
+ {
+ std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep));
+ sig_a = asserts_a[pf];
+ sig_en = asserts_en[pf];
+ }
+
+ void getAssumes(RTLIL::SigSpec &sig_a, RTLIL::SigSpec &sig_en, int timestep = -1)
+ {
+ std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep));
+ sig_a = assumes_a[pf];
+ sig_en = assumes_en[pf];
+ }
+
+ int importAsserts(int timestep = -1)
+ {
+ std::vector<int> check_bits, enable_bits;
+ std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep));
+ if (model_undef) {
+ check_bits = ez->vec_and(ez->vec_not(importUndefSigSpec(asserts_a[pf], timestep)), importDefSigSpec(asserts_a[pf], timestep));
+ enable_bits = ez->vec_and(ez->vec_not(importUndefSigSpec(asserts_en[pf], timestep)), importDefSigSpec(asserts_en[pf], timestep));
+ } else {
+ check_bits = importDefSigSpec(asserts_a[pf], timestep);
+ enable_bits = importDefSigSpec(asserts_en[pf], timestep);
+ }
+ return ez->vec_reduce_and(ez->vec_or(check_bits, ez->vec_not(enable_bits)));
+ }
+
+ int importAssumes(int timestep = -1)
+ {
+ std::vector<int> check_bits, enable_bits;
+ std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep));
+ if (model_undef) {
+ check_bits = ez->vec_and(ez->vec_not(importUndefSigSpec(assumes_a[pf], timestep)), importDefSigSpec(assumes_a[pf], timestep));
+ enable_bits = ez->vec_and(ez->vec_not(importUndefSigSpec(assumes_en[pf], timestep)), importDefSigSpec(assumes_en[pf], timestep));
+ } else {
+ check_bits = importDefSigSpec(assumes_a[pf], timestep);
+ enable_bits = importDefSigSpec(assumes_en[pf], timestep);
+ }
+ return ez->vec_reduce_and(ez->vec_or(check_bits, ez->vec_not(enable_bits)));
+ }
+
+ int signals_eq(RTLIL::SigSpec lhs, RTLIL::SigSpec rhs, int timestep_lhs = -1, int timestep_rhs = -1)
+ {
+ if (timestep_rhs < 0)
+ timestep_rhs = timestep_lhs;
+
+ log_assert(lhs.size() == rhs.size());
+
+ std::vector<int> vec_lhs = importSigSpec(lhs, timestep_lhs);
+ std::vector<int> vec_rhs = importSigSpec(rhs, timestep_rhs);
+
+ if (!model_undef)
+ return ez->vec_eq(vec_lhs, vec_rhs);
+
+ std::vector<int> undef_lhs = importUndefSigSpec(lhs, timestep_lhs);
+ std::vector<int> undef_rhs = importUndefSigSpec(rhs, timestep_rhs);
+
+ std::vector<int> eq_bits;
+ for (int i = 0; i < lhs.size(); i++)
+ eq_bits.push_back(ez->AND(ez->IFF(undef_lhs.at(i), undef_rhs.at(i)),
+ ez->IFF(ez->OR(vec_lhs.at(i), undef_lhs.at(i)), ez->OR(vec_rhs.at(i), undef_rhs.at(i)))));
+ return ez->expression(ezSAT::OpAnd, eq_bits);
+ }
+
+ void extendSignalWidth(std::vector<int> &vec_a, std::vector<int> &vec_b, RTLIL::Cell *cell, size_t y_width = 0, bool forced_signed = false)
+ {
+ bool is_signed = forced_signed;
+ if (!forced_signed && cell->parameters.count("\\A_SIGNED") > 0 && cell->parameters.count("\\B_SIGNED") > 0)
+ is_signed = cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool();
+ while (vec_a.size() < vec_b.size() || vec_a.size() < y_width)
+ vec_a.push_back(is_signed && vec_a.size() > 0 ? vec_a.back() : ez->CONST_FALSE);
+ while (vec_b.size() < vec_a.size() || vec_b.size() < y_width)
+ vec_b.push_back(is_signed && vec_b.size() > 0 ? vec_b.back() : ez->CONST_FALSE);
+ }
+
+ void extendSignalWidth(std::vector<int> &vec_a, std::vector<int> &vec_b, std::vector<int> &vec_y, RTLIL::Cell *cell, bool forced_signed = false)
+ {
+ extendSignalWidth(vec_a, vec_b, cell, vec_y.size(), forced_signed);
+ while (vec_y.size() < vec_a.size())
+ vec_y.push_back(ez->literal());
+ }
+
+ void extendSignalWidthUnary(std::vector<int> &vec_a, std::vector<int> &vec_y, RTLIL::Cell *cell, bool forced_signed = false)
+ {
+ bool is_signed = forced_signed || (cell->parameters.count("\\A_SIGNED") > 0 && cell->parameters["\\A_SIGNED"].as_bool());
+ while (vec_a.size() < vec_y.size())
+ vec_a.push_back(is_signed && vec_a.size() > 0 ? vec_a.back() : ez->CONST_FALSE);
+ while (vec_y.size() < vec_a.size())
+ vec_y.push_back(ez->literal());
+ }
+
+ void undefGating(std::vector<int> &vec_y, std::vector<int> &vec_yy, std::vector<int> &vec_undef)
+ {
+ log_assert(model_undef);
+ log_assert(vec_y.size() == vec_yy.size());
+ if (vec_y.size() > vec_undef.size()) {
+ std::vector<int> trunc_y(vec_y.begin(), vec_y.begin() + vec_undef.size());
+ std::vector<int> trunc_yy(vec_yy.begin(), vec_yy.begin() + vec_undef.size());
+ ez->assume(ez->expression(ezSAT::OpAnd, ez->vec_or(vec_undef, ez->vec_iff(trunc_y, trunc_yy))));
+ } else {
+ log_assert(vec_y.size() == vec_undef.size());
+ ez->assume(ez->expression(ezSAT::OpAnd, ez->vec_or(vec_undef, ez->vec_iff(vec_y, vec_yy))));
+ }
+ }
+
+ void undefGating(int y, int yy, int undef)
+ {
+ ez->assume(ez->OR(undef, ez->IFF(y, yy)));
+ }
+
+ void setInitState(int timestep)
+ {
+ auto key = make_pair(prefix, timestep);
+ log_assert(initstates.count(key) == 0 || initstates.at(key) == true);
+ initstates[key] = true;
+ }
+
+ bool importCell(RTLIL::Cell *cell, int timestep = -1)
+ {
+ bool arith_undef_handled = false;
+ bool is_arith_compare = cell->type.in("$lt", "$le", "$ge", "$gt");
+
+ if (model_undef && (cell->type.in("$add", "$sub", "$mul", "$div", "$mod") || is_arith_compare))
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+ if (is_arith_compare)
+ extendSignalWidth(undef_a, undef_b, cell, true);
+ else
+ extendSignalWidth(undef_a, undef_b, undef_y, cell, true);
+
+ int undef_any_a = ez->expression(ezSAT::OpOr, undef_a);
+ int undef_any_b = ez->expression(ezSAT::OpOr, undef_b);
+ int undef_y_bit = ez->OR(undef_any_a, undef_any_b);
+
+ if (cell->type == "$div" || cell->type == "$mod") {
+ std::vector<int> b = importSigSpec(cell->getPort("\\B"), timestep);
+ undef_y_bit = ez->OR(undef_y_bit, ez->NOT(ez->expression(ezSAT::OpOr, b)));
+ }
+
+ if (is_arith_compare) {
+ for (size_t i = 1; i < undef_y.size(); i++)
+ ez->SET(ez->CONST_FALSE, undef_y.at(i));
+ ez->SET(undef_y_bit, undef_y.at(0));
+ } else {
+ std::vector<int> undef_y_bits(undef_y.size(), undef_y_bit);
+ ez->assume(ez->vec_eq(undef_y_bits, undef_y));
+ }
+
+ arith_undef_handled = true;
+ }
+
+ if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_",
+ "$and", "$or", "$xor", "$xnor", "$add", "$sub"))
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort("\\Y"), timestep);
+ extendSignalWidth(a, b, y, cell);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+
+ if (cell->type == "$and" || cell->type == "$_AND_")
+ ez->assume(ez->vec_eq(ez->vec_and(a, b), yy));
+ if (cell->type == "$_NAND_")
+ ez->assume(ez->vec_eq(ez->vec_not(ez->vec_and(a, b)), yy));
+ if (cell->type == "$or" || cell->type == "$_OR_")
+ ez->assume(ez->vec_eq(ez->vec_or(a, b), yy));
+ if (cell->type == "$_NOR_")
+ ez->assume(ez->vec_eq(ez->vec_not(ez->vec_or(a, b)), yy));
+ if (cell->type == "$xor" || cell->type == "$_XOR_")
+ ez->assume(ez->vec_eq(ez->vec_xor(a, b), yy));
+ if (cell->type == "$xnor" || cell->type == "$_XNOR_")
+ ez->assume(ez->vec_eq(ez->vec_not(ez->vec_xor(a, b)), yy));
+ if (cell->type == "$add")
+ ez->assume(ez->vec_eq(ez->vec_add(a, b), yy));
+ if (cell->type == "$sub")
+ ez->assume(ez->vec_eq(ez->vec_sub(a, b), yy));
+
+ if (model_undef && !arith_undef_handled)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+ extendSignalWidth(undef_a, undef_b, undef_y, cell, false);
+
+ if (cell->type.in("$and", "$_AND_", "$_NAND_")) {
+ std::vector<int> a0 = ez->vec_and(ez->vec_not(a), ez->vec_not(undef_a));
+ std::vector<int> b0 = ez->vec_and(ez->vec_not(b), ez->vec_not(undef_b));
+ std::vector<int> yX = ez->vec_and(ez->vec_or(undef_a, undef_b), ez->vec_not(ez->vec_or(a0, b0)));
+ ez->assume(ez->vec_eq(yX, undef_y));
+ }
+ else if (cell->type.in("$or", "$_OR_", "$_NOR_")) {
+ std::vector<int> a1 = ez->vec_and(a, ez->vec_not(undef_a));
+ std::vector<int> b1 = ez->vec_and(b, ez->vec_not(undef_b));
+ std::vector<int> yX = ez->vec_and(ez->vec_or(undef_a, undef_b), ez->vec_not(ez->vec_or(a1, b1)));
+ ez->assume(ez->vec_eq(yX, undef_y));
+ }
+ else if (cell->type.in("$xor", "$xnor", "$_XOR_", "$_XNOR_")) {
+ std::vector<int> yX = ez->vec_or(undef_a, undef_b);
+ ez->assume(ez->vec_eq(yX, undef_y));
+ }
+ else
+ log_abort();
+
+ undefGating(y, yy, undef_y);
+ }
+ else if (model_undef)
+ {
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type.in("$_AOI3_", "$_OAI3_", "$_AOI4_", "$_OAI4_"))
+ {
+ bool aoi_mode = cell->type.in("$_AOI3_", "$_AOI4_");
+ bool three_mode = cell->type.in("$_AOI3_", "$_OAI3_");
+
+ int a = importDefSigSpec(cell->getPort("\\A"), timestep).at(0);
+ int b = importDefSigSpec(cell->getPort("\\B"), timestep).at(0);
+ int c = importDefSigSpec(cell->getPort("\\C"), timestep).at(0);
+ int d = three_mode ? (aoi_mode ? ez->CONST_TRUE : ez->CONST_FALSE) : importDefSigSpec(cell->getPort("\\D"), timestep).at(0);
+ int y = importDefSigSpec(cell->getPort("\\Y"), timestep).at(0);
+ int yy = model_undef ? ez->literal() : y;
+
+ if (cell->type.in("$_AOI3_", "$_AOI4_"))
+ ez->assume(ez->IFF(ez->NOT(ez->OR(ez->AND(a, b), ez->AND(c, d))), yy));
+ else
+ ez->assume(ez->IFF(ez->NOT(ez->AND(ez->OR(a, b), ez->OR(c, d))), yy));
+
+ if (model_undef)
+ {
+ int undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep).at(0);
+ int undef_b = importUndefSigSpec(cell->getPort("\\B"), timestep).at(0);
+ int undef_c = importUndefSigSpec(cell->getPort("\\C"), timestep).at(0);
+ int undef_d = three_mode ? ez->CONST_FALSE : importUndefSigSpec(cell->getPort("\\D"), timestep).at(0);
+ int undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep).at(0);
+
+ if (aoi_mode)
+ {
+ int a0 = ez->AND(ez->NOT(a), ez->NOT(undef_a));
+ int b0 = ez->AND(ez->NOT(b), ez->NOT(undef_b));
+ int c0 = ez->AND(ez->NOT(c), ez->NOT(undef_c));
+ int d0 = ez->AND(ez->NOT(d), ez->NOT(undef_d));
+
+ int ab = ez->AND(a, b), cd = ez->AND(c, d);
+ int undef_ab = ez->AND(ez->OR(undef_a, undef_b), ez->NOT(ez->OR(a0, b0)));
+ int undef_cd = ez->AND(ez->OR(undef_c, undef_d), ez->NOT(ez->OR(c0, d0)));
+
+ int ab1 = ez->AND(ab, ez->NOT(undef_ab));
+ int cd1 = ez->AND(cd, ez->NOT(undef_cd));
+ int yX = ez->AND(ez->OR(undef_ab, undef_cd), ez->NOT(ez->OR(ab1, cd1)));
+
+ ez->assume(ez->IFF(yX, undef_y));
+ }
+ else
+ {
+ int a1 = ez->AND(a, ez->NOT(undef_a));
+ int b1 = ez->AND(b, ez->NOT(undef_b));
+ int c1 = ez->AND(c, ez->NOT(undef_c));
+ int d1 = ez->AND(d, ez->NOT(undef_d));
+
+ int ab = ez->OR(a, b), cd = ez->OR(c, d);
+ int undef_ab = ez->AND(ez->OR(undef_a, undef_b), ez->NOT(ez->OR(a1, b1)));
+ int undef_cd = ez->AND(ez->OR(undef_c, undef_d), ez->NOT(ez->OR(c1, d1)));
+
+ int ab0 = ez->AND(ez->NOT(ab), ez->NOT(undef_ab));
+ int cd0 = ez->AND(ez->NOT(cd), ez->NOT(undef_cd));
+ int yX = ez->AND(ez->OR(undef_ab, undef_cd), ez->NOT(ez->OR(ab0, cd0)));
+
+ ez->assume(ez->IFF(yX, undef_y));
+ }
+
+ undefGating(y, yy, undef_y);
+ }
+
+ return true;
+ }
+
+ if (cell->type == "$_NOT_" || cell->type == "$not")
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort("\\Y"), timestep);
+ extendSignalWidthUnary(a, y, cell);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+ ez->assume(ez->vec_eq(ez->vec_not(a), yy));
+
+ if (model_undef) {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+ extendSignalWidthUnary(undef_a, undef_y, cell, false);
+ ez->assume(ez->vec_eq(undef_a, undef_y));
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type == "$_MUX_" || cell->type == "$mux")
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> s = importDefSigSpec(cell->getPort("\\S"), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort("\\Y"), timestep);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+ ez->assume(ez->vec_eq(ez->vec_ite(s.at(0), b, a), yy));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> undef_s = importUndefSigSpec(cell->getPort("\\S"), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+
+ std::vector<int> unequal_ab = ez->vec_not(ez->vec_iff(a, b));
+ std::vector<int> undef_ab = ez->vec_or(unequal_ab, ez->vec_or(undef_a, undef_b));
+ std::vector<int> yX = ez->vec_ite(undef_s.at(0), undef_ab, ez->vec_ite(s.at(0), undef_b, undef_a));
+ ez->assume(ez->vec_eq(yX, undef_y));
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type == "$pmux")
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> s = importDefSigSpec(cell->getPort("\\S"), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort("\\Y"), timestep);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+
+ std::vector<int> tmp = a;
+ for (size_t i = 0; i < s.size(); i++) {
+ std::vector<int> part_of_b(b.begin()+i*a.size(), b.begin()+(i+1)*a.size());
+ tmp = ez->vec_ite(s.at(i), part_of_b, tmp);
+ }
+ ez->assume(ez->vec_eq(tmp, yy));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> undef_s = importUndefSigSpec(cell->getPort("\\S"), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+
+ int maybe_one_hot = ez->CONST_FALSE;
+ int maybe_many_hot = ez->CONST_FALSE;
+
+ int sure_one_hot = ez->CONST_FALSE;
+ int sure_many_hot = ez->CONST_FALSE;
+
+ std::vector<int> bits_set = std::vector<int>(undef_y.size(), ez->CONST_FALSE);
+ std::vector<int> bits_clr = std::vector<int>(undef_y.size(), ez->CONST_FALSE);
+
+ for (size_t i = 0; i < s.size(); i++)
+ {
+ std::vector<int> part_of_b(b.begin()+i*a.size(), b.begin()+(i+1)*a.size());
+ std::vector<int> part_of_undef_b(undef_b.begin()+i*a.size(), undef_b.begin()+(i+1)*a.size());
+
+ int maybe_s = ez->OR(s.at(i), undef_s.at(i));
+ int sure_s = ez->AND(s.at(i), ez->NOT(undef_s.at(i)));
+
+ maybe_one_hot = ez->OR(maybe_one_hot, maybe_s);
+ maybe_many_hot = ez->OR(maybe_many_hot, ez->AND(maybe_one_hot, maybe_s));
+
+ sure_one_hot = ez->OR(sure_one_hot, sure_s);
+ sure_many_hot = ez->OR(sure_many_hot, ez->AND(sure_one_hot, sure_s));
+
+ bits_set = ez->vec_ite(maybe_s, ez->vec_or(bits_set, ez->vec_or(bits_set, ez->vec_or(part_of_b, part_of_undef_b))), bits_set);
+ bits_clr = ez->vec_ite(maybe_s, ez->vec_or(bits_clr, ez->vec_or(bits_clr, ez->vec_or(ez->vec_not(part_of_b), part_of_undef_b))), bits_clr);
+ }
+
+ int maybe_a = ez->NOT(maybe_one_hot);
+
+ bits_set = ez->vec_ite(maybe_a, ez->vec_or(bits_set, ez->vec_or(bits_set, ez->vec_or(a, undef_a))), bits_set);
+ bits_clr = ez->vec_ite(maybe_a, ez->vec_or(bits_clr, ez->vec_or(bits_clr, ez->vec_or(ez->vec_not(a), undef_a))), bits_clr);
+
+ ez->assume(ez->vec_eq(ez->vec_not(ez->vec_xor(bits_set, bits_clr)), undef_y));
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type == "$pos" || cell->type == "$neg")
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort("\\Y"), timestep);
+ extendSignalWidthUnary(a, y, cell);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+
+ if (cell->type == "$pos") {
+ ez->assume(ez->vec_eq(a, yy));
+ } else {
+ std::vector<int> zero(a.size(), ez->CONST_FALSE);
+ ez->assume(ez->vec_eq(ez->vec_sub(zero, a), yy));
+ }
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+ extendSignalWidthUnary(undef_a, undef_y, cell);
+
+ if (cell->type == "$pos") {
+ ez->assume(ez->vec_eq(undef_a, undef_y));
+ } else {
+ int undef_any_a = ez->expression(ezSAT::OpOr, undef_a);
+ std::vector<int> undef_y_bits(undef_y.size(), undef_any_a);
+ ez->assume(ez->vec_eq(undef_y_bits, undef_y));
+ }
+
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type == "$reduce_and" || cell->type == "$reduce_or" || cell->type == "$reduce_xor" ||
+ cell->type == "$reduce_xnor" || cell->type == "$reduce_bool" || cell->type == "$logic_not")
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort("\\Y"), timestep);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+
+ if (cell->type == "$reduce_and")
+ ez->SET(ez->expression(ez->OpAnd, a), yy.at(0));
+ if (cell->type == "$reduce_or" || cell->type == "$reduce_bool")
+ ez->SET(ez->expression(ez->OpOr, a), yy.at(0));
+ if (cell->type == "$reduce_xor")
+ ez->SET(ez->expression(ez->OpXor, a), yy.at(0));
+ if (cell->type == "$reduce_xnor")
+ ez->SET(ez->NOT(ez->expression(ez->OpXor, a)), yy.at(0));
+ if (cell->type == "$logic_not")
+ ez->SET(ez->NOT(ez->expression(ez->OpOr, a)), yy.at(0));
+ for (size_t i = 1; i < y.size(); i++)
+ ez->SET(ez->CONST_FALSE, yy.at(i));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+ int aX = ez->expression(ezSAT::OpOr, undef_a);
+
+ if (cell->type == "$reduce_and") {
+ int a0 = ez->expression(ezSAT::OpOr, ez->vec_and(ez->vec_not(a), ez->vec_not(undef_a)));
+ ez->assume(ez->IFF(ez->AND(ez->NOT(a0), aX), undef_y.at(0)));
+ }
+ else if (cell->type == "$reduce_or" || cell->type == "$reduce_bool" || cell->type == "$logic_not") {
+ int a1 = ez->expression(ezSAT::OpOr, ez->vec_and(a, ez->vec_not(undef_a)));
+ ez->assume(ez->IFF(ez->AND(ez->NOT(a1), aX), undef_y.at(0)));
+ }
+ else if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor") {
+ ez->assume(ez->IFF(aX, undef_y.at(0)));
+ } else
+ log_abort();
+
+ for (size_t i = 1; i < undef_y.size(); i++)
+ ez->SET(ez->CONST_FALSE, undef_y.at(i));
+
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type == "$logic_and" || cell->type == "$logic_or")
+ {
+ std::vector<int> vec_a = importDefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> vec_b = importDefSigSpec(cell->getPort("\\B"), timestep);
+
+ int a = ez->expression(ez->OpOr, vec_a);
+ int b = ez->expression(ez->OpOr, vec_b);
+ std::vector<int> y = importDefSigSpec(cell->getPort("\\Y"), timestep);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+
+ if (cell->type == "$logic_and")
+ ez->SET(ez->expression(ez->OpAnd, a, b), yy.at(0));
+ else
+ ez->SET(ez->expression(ez->OpOr, a, b), yy.at(0));
+ for (size_t i = 1; i < y.size(); i++)
+ ez->SET(ez->CONST_FALSE, yy.at(i));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+
+ int a0 = ez->NOT(ez->OR(ez->expression(ezSAT::OpOr, vec_a), ez->expression(ezSAT::OpOr, undef_a)));
+ int b0 = ez->NOT(ez->OR(ez->expression(ezSAT::OpOr, vec_b), ez->expression(ezSAT::OpOr, undef_b)));
+ int a1 = ez->expression(ezSAT::OpOr, ez->vec_and(vec_a, ez->vec_not(undef_a)));
+ int b1 = ez->expression(ezSAT::OpOr, ez->vec_and(vec_b, ez->vec_not(undef_b)));
+ int aX = ez->expression(ezSAT::OpOr, undef_a);
+ int bX = ez->expression(ezSAT::OpOr, undef_b);
+
+ if (cell->type == "$logic_and")
+ ez->SET(ez->AND(ez->OR(aX, bX), ez->NOT(ez->AND(a1, b1)), ez->NOT(a0), ez->NOT(b0)), undef_y.at(0));
+ else if (cell->type == "$logic_or")
+ ez->SET(ez->AND(ez->OR(aX, bX), ez->NOT(ez->AND(a0, b0)), ez->NOT(a1), ez->NOT(b1)), undef_y.at(0));
+ else
+ log_abort();
+
+ for (size_t i = 1; i < undef_y.size(); i++)
+ ez->SET(ez->CONST_FALSE, undef_y.at(i));
+
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type == "$lt" || cell->type == "$le" || cell->type == "$eq" || cell->type == "$ne" || cell->type == "$eqx" || cell->type == "$nex" || cell->type == "$ge" || cell->type == "$gt")
+ {
+ bool is_signed = cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool();
+ std::vector<int> a = importDefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort("\\Y"), timestep);
+ extendSignalWidth(a, b, cell);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+
+ if (model_undef && (cell->type == "$eqx" || cell->type == "$nex")) {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort("\\B"), timestep);
+ extendSignalWidth(undef_a, undef_b, cell, true);
+ a = ez->vec_or(a, undef_a);
+ b = ez->vec_or(b, undef_b);
+ }
+
+ if (cell->type == "$lt")
+ ez->SET(is_signed ? ez->vec_lt_signed(a, b) : ez->vec_lt_unsigned(a, b), yy.at(0));
+ if (cell->type == "$le")
+ ez->SET(is_signed ? ez->vec_le_signed(a, b) : ez->vec_le_unsigned(a, b), yy.at(0));
+ if (cell->type == "$eq" || cell->type == "$eqx")
+ ez->SET(ez->vec_eq(a, b), yy.at(0));
+ if (cell->type == "$ne" || cell->type == "$nex")
+ ez->SET(ez->vec_ne(a, b), yy.at(0));
+ if (cell->type == "$ge")
+ ez->SET(is_signed ? ez->vec_ge_signed(a, b) : ez->vec_ge_unsigned(a, b), yy.at(0));
+ if (cell->type == "$gt")
+ ez->SET(is_signed ? ez->vec_gt_signed(a, b) : ez->vec_gt_unsigned(a, b), yy.at(0));
+ for (size_t i = 1; i < y.size(); i++)
+ ez->SET(ez->CONST_FALSE, yy.at(i));
+
+ if (model_undef && (cell->type == "$eqx" || cell->type == "$nex"))
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+ extendSignalWidth(undef_a, undef_b, cell, true);
+
+ if (cell->type == "$eqx")
+ yy.at(0) = ez->AND(yy.at(0), ez->vec_eq(undef_a, undef_b));
+ else
+ yy.at(0) = ez->OR(yy.at(0), ez->vec_ne(undef_a, undef_b));
+
+ for (size_t i = 0; i < y.size(); i++)
+ ez->SET(ez->CONST_FALSE, undef_y.at(i));
+
+ ez->assume(ez->vec_eq(y, yy));
+ }
+ else if (model_undef && (cell->type == "$eq" || cell->type == "$ne"))
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+ extendSignalWidth(undef_a, undef_b, cell, true);
+
+ int undef_any_a = ez->expression(ezSAT::OpOr, undef_a);
+ int undef_any_b = ez->expression(ezSAT::OpOr, undef_b);
+ int undef_any = ez->OR(undef_any_a, undef_any_b);
+
+ std::vector<int> masked_a_bits = ez->vec_or(a, ez->vec_or(undef_a, undef_b));
+ std::vector<int> masked_b_bits = ez->vec_or(b, ez->vec_or(undef_a, undef_b));
+
+ int masked_ne = ez->vec_ne(masked_a_bits, masked_b_bits);
+ int undef_y_bit = ez->AND(undef_any, ez->NOT(masked_ne));
+
+ for (size_t i = 1; i < undef_y.size(); i++)
+ ez->SET(ez->CONST_FALSE, undef_y.at(i));
+ ez->SET(undef_y_bit, undef_y.at(0));
+
+ undefGating(y, yy, undef_y);
+ }
+ else
+ {
+ if (model_undef) {
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+ undefGating(y, yy, undef_y);
+ }
+ log_assert(!model_undef || arith_undef_handled);
+ }
+ return true;
+ }
+
+ if (cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr" || cell->type == "$shift" || cell->type == "$shiftx")
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort("\\Y"), timestep);
+
+ int extend_bit = ez->CONST_FALSE;
+
+ if (!cell->type.in("$shift", "$shiftx") && cell->parameters["\\A_SIGNED"].as_bool())
+ extend_bit = a.back();
+
+ while (y.size() < a.size())
+ y.push_back(ez->literal());
+ while (y.size() > a.size())
+ a.push_back(extend_bit);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+ std::vector<int> shifted_a;
+
+ if (cell->type == "$shl" || cell->type == "$sshl")
+ shifted_a = ez->vec_shift_left(a, b, false, ez->CONST_FALSE, ez->CONST_FALSE);
+
+ if (cell->type == "$shr")
+ shifted_a = ez->vec_shift_right(a, b, false, ez->CONST_FALSE, ez->CONST_FALSE);
+
+ if (cell->type == "$sshr")
+ shifted_a = ez->vec_shift_right(a, b, false, cell->parameters["\\A_SIGNED"].as_bool() ? a.back() : ez->CONST_FALSE, ez->CONST_FALSE);
+
+ if (cell->type == "$shift" || cell->type == "$shiftx")
+ shifted_a = ez->vec_shift_right(a, b, cell->parameters["\\B_SIGNED"].as_bool(), ez->CONST_FALSE, ez->CONST_FALSE);
+
+ ez->assume(ez->vec_eq(shifted_a, yy));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+ std::vector<int> undef_a_shifted;
+
+ extend_bit = cell->type == "$shiftx" ? ez->CONST_TRUE : ez->CONST_FALSE;
+ if (!cell->type.in("$shift", "$shiftx") && cell->parameters["\\A_SIGNED"].as_bool())
+ extend_bit = undef_a.back();
+
+ while (undef_y.size() < undef_a.size())
+ undef_y.push_back(ez->literal());
+ while (undef_y.size() > undef_a.size())
+ undef_a.push_back(extend_bit);
+
+ if (cell->type == "$shl" || cell->type == "$sshl")
+ undef_a_shifted = ez->vec_shift_left(undef_a, b, false, ez->CONST_FALSE, ez->CONST_FALSE);
+
+ if (cell->type == "$shr")
+ undef_a_shifted = ez->vec_shift_right(undef_a, b, false, ez->CONST_FALSE, ez->CONST_FALSE);
+
+ if (cell->type == "$sshr")
+ undef_a_shifted = ez->vec_shift_right(undef_a, b, false, cell->parameters["\\A_SIGNED"].as_bool() ? undef_a.back() : ez->CONST_FALSE, ez->CONST_FALSE);
+
+ if (cell->type == "$shift")
+ undef_a_shifted = ez->vec_shift_right(undef_a, b, cell->parameters["\\B_SIGNED"].as_bool(), ez->CONST_FALSE, ez->CONST_FALSE);
+
+ if (cell->type == "$shiftx")
+ undef_a_shifted = ez->vec_shift_right(undef_a, b, cell->parameters["\\B_SIGNED"].as_bool(), ez->CONST_TRUE, ez->CONST_TRUE);
+
+ int undef_any_b = ez->expression(ezSAT::OpOr, undef_b);
+ std::vector<int> undef_all_y_bits(undef_y.size(), undef_any_b);
+ ez->assume(ez->vec_eq(ez->vec_or(undef_a_shifted, undef_all_y_bits), undef_y));
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type == "$mul")
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort("\\Y"), timestep);
+ extendSignalWidth(a, b, y, cell);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+
+ std::vector<int> tmp(a.size(), ez->CONST_FALSE);
+ for (int i = 0; i < int(a.size()); i++)
+ {
+ std::vector<int> shifted_a(a.size(), ez->CONST_FALSE);
+ for (int j = i; j < int(a.size()); j++)
+ shifted_a.at(j) = a.at(j-i);
+ tmp = ez->vec_ite(b.at(i), ez->vec_add(tmp, shifted_a), tmp);
+ }
+ ez->assume(ez->vec_eq(tmp, yy));
+
+ if (model_undef) {
+ log_assert(arith_undef_handled);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type == "$macc")
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort("\\Y"), timestep);
+
+ Macc macc;
+ macc.from_cell(cell);
+
+ std::vector<int> tmp(GetSize(y), ez->CONST_FALSE);
+
+ for (auto &port : macc.ports)
+ {
+ std::vector<int> in_a = importDefSigSpec(port.in_a, timestep);
+ std::vector<int> in_b = importDefSigSpec(port.in_b, timestep);
+
+ while (GetSize(in_a) < GetSize(y))
+ in_a.push_back(port.is_signed && !in_a.empty() ? in_a.back() : ez->CONST_FALSE);
+ in_a.resize(GetSize(y));
+
+ if (GetSize(in_b))
+ {
+ while (GetSize(in_b) < GetSize(y))
+ in_b.push_back(port.is_signed && !in_b.empty() ? in_b.back() : ez->CONST_FALSE);
+ in_b.resize(GetSize(y));
+
+ for (int i = 0; i < GetSize(in_b); i++) {
+ std::vector<int> shifted_a(in_a.size(), ez->CONST_FALSE);
+ for (int j = i; j < int(in_a.size()); j++)
+ shifted_a.at(j) = in_a.at(j-i);
+ if (port.do_subtract)
+ tmp = ez->vec_ite(in_b.at(i), ez->vec_sub(tmp, shifted_a), tmp);
+ else
+ tmp = ez->vec_ite(in_b.at(i), ez->vec_add(tmp, shifted_a), tmp);
+ }
+ }
+ else
+ {
+ if (port.do_subtract)
+ tmp = ez->vec_sub(tmp, in_a);
+ else
+ tmp = ez->vec_add(tmp, in_a);
+ }
+ }
+
+ for (int i = 0; i < GetSize(b); i++) {
+ std::vector<int> val(GetSize(y), ez->CONST_FALSE);
+ val.at(0) = b.at(i);
+ tmp = ez->vec_add(tmp, val);
+ }
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort("\\B"), timestep);
+
+ int undef_any_a = ez->expression(ezSAT::OpOr, undef_a);
+ int undef_any_b = ez->expression(ezSAT::OpOr, undef_b);
+
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+ ez->assume(ez->vec_eq(undef_y, std::vector<int>(GetSize(y), ez->OR(undef_any_a, undef_any_b))));
+
+ undefGating(y, tmp, undef_y);
+ }
+ else
+ ez->assume(ez->vec_eq(y, tmp));
+
+ return true;
+ }
+
+ if (cell->type == "$div" || cell->type == "$mod")
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort("\\Y"), timestep);
+ extendSignalWidth(a, b, y, cell);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+
+ std::vector<int> a_u, b_u;
+ if (cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool()) {
+ a_u = ez->vec_ite(a.back(), ez->vec_neg(a), a);
+ b_u = ez->vec_ite(b.back(), ez->vec_neg(b), b);
+ } else {
+ a_u = a;
+ b_u = b;
+ }
+
+ std::vector<int> chain_buf = a_u;
+ std::vector<int> y_u(a_u.size(), ez->CONST_FALSE);
+ for (int i = int(a.size())-1; i >= 0; i--)
+ {
+ chain_buf.insert(chain_buf.end(), chain_buf.size(), ez->CONST_FALSE);
+
+ std::vector<int> b_shl(i, ez->CONST_FALSE);
+ b_shl.insert(b_shl.end(), b_u.begin(), b_u.end());
+ b_shl.insert(b_shl.end(), chain_buf.size()-b_shl.size(), ez->CONST_FALSE);
+
+ y_u.at(i) = ez->vec_ge_unsigned(chain_buf, b_shl);
+ chain_buf = ez->vec_ite(y_u.at(i), ez->vec_sub(chain_buf, b_shl), chain_buf);
+
+ chain_buf.erase(chain_buf.begin() + a_u.size(), chain_buf.end());
+ }
+
+ std::vector<int> y_tmp = ignore_div_by_zero ? yy : ez->vec_var(y.size());
+ if (cell->type == "$div") {
+ if (cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool())
+ ez->assume(ez->vec_eq(y_tmp, ez->vec_ite(ez->XOR(a.back(), b.back()), ez->vec_neg(y_u), y_u)));
+ else
+ ez->assume(ez->vec_eq(y_tmp, y_u));
+ } else {
+ if (cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool())
+ ez->assume(ez->vec_eq(y_tmp, ez->vec_ite(a.back(), ez->vec_neg(chain_buf), chain_buf)));
+ else
+ ez->assume(ez->vec_eq(y_tmp, chain_buf));
+ }
+
+ if (ignore_div_by_zero) {
+ ez->assume(ez->expression(ezSAT::OpOr, b));
+ } else {
+ std::vector<int> div_zero_result;
+ if (cell->type == "$div") {
+ if (cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool()) {
+ std::vector<int> all_ones(y.size(), ez->CONST_TRUE);
+ std::vector<int> only_first_one(y.size(), ez->CONST_FALSE);
+ only_first_one.at(0) = ez->CONST_TRUE;
+ div_zero_result = ez->vec_ite(a.back(), only_first_one, all_ones);
+ } else {
+ div_zero_result.insert(div_zero_result.end(), cell->getPort("\\A").size(), ez->CONST_TRUE);
+ div_zero_result.insert(div_zero_result.end(), y.size() - div_zero_result.size(), ez->CONST_FALSE);
+ }
+ } else {
+ int copy_a_bits = min(cell->getPort("\\A").size(), cell->getPort("\\B").size());
+ div_zero_result.insert(div_zero_result.end(), a.begin(), a.begin() + copy_a_bits);
+ if (cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool())
+ div_zero_result.insert(div_zero_result.end(), y.size() - div_zero_result.size(), div_zero_result.back());
+ else
+ div_zero_result.insert(div_zero_result.end(), y.size() - div_zero_result.size(), ez->CONST_FALSE);
+ }
+ ez->assume(ez->vec_eq(yy, ez->vec_ite(ez->expression(ezSAT::OpOr, b), y_tmp, div_zero_result)));
+ }
+
+ if (model_undef) {
+ log_assert(arith_undef_handled);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type == "$lut")
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort("\\Y"), timestep);
+
+ std::vector<int> lut;
+ for (auto bit : cell->getParam("\\LUT").bits)
+ lut.push_back(bit == RTLIL::S1 ? ez->CONST_TRUE : ez->CONST_FALSE);
+ while (GetSize(lut) < (1 << GetSize(a)))
+ lut.push_back(ez->CONST_FALSE);
+ lut.resize(1 << GetSize(a));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> t(lut), u(GetSize(t), ez->CONST_FALSE);
+
+ for (int i = GetSize(a)-1; i >= 0; i--)
+ {
+ std::vector<int> t0(t.begin(), t.begin() + GetSize(t)/2);
+ std::vector<int> t1(t.begin() + GetSize(t)/2, t.end());
+
+ std::vector<int> u0(u.begin(), u.begin() + GetSize(u)/2);
+ std::vector<int> u1(u.begin() + GetSize(u)/2, u.end());
+
+ t = ez->vec_ite(a[i], t1, t0);
+ u = ez->vec_ite(undef_a[i], ez->vec_or(ez->vec_xor(t0, t1), ez->vec_or(u0, u1)), ez->vec_ite(a[i], u1, u0));
+ }
+
+ log_assert(GetSize(t) == 1);
+ log_assert(GetSize(u) == 1);
+ undefGating(y, t, u);
+ ez->assume(ez->vec_eq(importUndefSigSpec(cell->getPort("\\Y"), timestep), u));
+ }
+ else
+ {
+ std::vector<int> t = lut;
+ for (int i = GetSize(a)-1; i >= 0; i--)
+ {
+ std::vector<int> t0(t.begin(), t.begin() + GetSize(t)/2);
+ std::vector<int> t1(t.begin() + GetSize(t)/2, t.end());
+ t = ez->vec_ite(a[i], t1, t0);
+ }
+
+ log_assert(GetSize(t) == 1);
+ ez->assume(ez->vec_eq(y, t));
+ }
+ return true;
+ }
+
+ if (cell->type == "$sop")
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort("\\A"), timestep);
+ int y = importDefSigSpec(cell->getPort("\\Y"), timestep).at(0);
+
+ int width = cell->getParam("\\WIDTH").as_int();
+ int depth = cell->getParam("\\DEPTH").as_int();
+
+ vector<State> table_raw = cell->getParam("\\TABLE").bits;
+ while (GetSize(table_raw) < 2*width*depth)
+ table_raw.push_back(State::S0);
+
+ vector<vector<int>> table(depth);
+
+ for (int i = 0; i < depth; i++)
+ for (int j = 0; j < width; j++)
+ {
+ bool pat0 = (table_raw[2*width*i + 2*j + 0] == State::S1);
+ bool pat1 = (table_raw[2*width*i + 2*j + 1] == State::S1);
+
+ if (pat0 && !pat1)
+ table.at(i).push_back(0);
+ else if (!pat0 && pat1)
+ table.at(i).push_back(1);
+ else
+ table.at(i).push_back(-1);
+ }
+
+ if (model_undef)
+ {
+ std::vector<int> products, undef_products;
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep);
+ int undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep).at(0);
+
+ for (int i = 0; i < depth; i++)
+ {
+ std::vector<int> cmp_a, cmp_ua, cmp_b;
+
+ for (int j = 0; j < width; j++)
+ if (table.at(i).at(j) >= 0) {
+ cmp_a.push_back(a.at(j));
+ cmp_ua.push_back(undef_a.at(j));
+ cmp_b.push_back(table.at(i).at(j) ? ez->CONST_TRUE : ez->CONST_FALSE);
+ }
+
+ std::vector<int> masked_a = ez->vec_or(cmp_a, cmp_ua);
+ std::vector<int> masked_b = ez->vec_or(cmp_b, cmp_ua);
+
+ int masked_eq = ez->vec_eq(masked_a, masked_b);
+ int any_undef = ez->expression(ezSAT::OpOr, cmp_ua);
+
+ undef_products.push_back(ez->AND(any_undef, masked_eq));
+ products.push_back(ez->AND(ez->NOT(any_undef), masked_eq));
+ }
+
+ int yy = ez->expression(ezSAT::OpOr, products);
+ ez->SET(undef_y, ez->AND(ez->NOT(yy), ez->expression(ezSAT::OpOr, undef_products)));
+ undefGating(y, yy, undef_y);
+ }
+ else
+ {
+ std::vector<int> products;
+
+ for (int i = 0; i < depth; i++)
+ {
+ std::vector<int> cmp_a, cmp_b;
+
+ for (int j = 0; j < width; j++)
+ if (table.at(i).at(j) >= 0) {
+ cmp_a.push_back(a.at(j));
+ cmp_b.push_back(table.at(i).at(j) ? ez->CONST_TRUE : ez->CONST_FALSE);
+ }
+
+ products.push_back(ez->vec_eq(cmp_a, cmp_b));
+ }
+
+ ez->SET(y, ez->expression(ezSAT::OpOr, products));
+ }
+
+ return true;
+ }
+
+ if (cell->type == "$fa")
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> c = importDefSigSpec(cell->getPort("\\C"), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort("\\Y"), timestep);
+ std::vector<int> x = importDefSigSpec(cell->getPort("\\X"), timestep);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+ std::vector<int> xx = model_undef ? ez->vec_var(x.size()) : x;
+
+ std::vector<int> t1 = ez->vec_xor(a, b);
+ ez->assume(ez->vec_eq(yy, ez->vec_xor(t1, c)));
+
+ std::vector<int> t2 = ez->vec_and(a, b);
+ std::vector<int> t3 = ez->vec_and(c, t1);
+ ez->assume(ez->vec_eq(xx, ez->vec_or(t2, t3)));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> undef_c = importUndefSigSpec(cell->getPort("\\C"), timestep);
+
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+ std::vector<int> undef_x = importUndefSigSpec(cell->getPort("\\X"), timestep);
+
+ ez->assume(ez->vec_eq(undef_y, ez->vec_or(ez->vec_or(undef_a, undef_b), undef_c)));
+ ez->assume(ez->vec_eq(undef_x, undef_y));
+
+ undefGating(y, yy, undef_y);
+ undefGating(x, xx, undef_x);
+ }
+ return true;
+ }
+
+ if (cell->type == "$lcu")
+ {
+ std::vector<int> p = importDefSigSpec(cell->getPort("\\P"), timestep);
+ std::vector<int> g = importDefSigSpec(cell->getPort("\\G"), timestep);
+ std::vector<int> ci = importDefSigSpec(cell->getPort("\\CI"), timestep);
+ std::vector<int> co = importDefSigSpec(cell->getPort("\\CO"), timestep);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(co.size()) : co;
+
+ for (int i = 0; i < GetSize(co); i++)
+ ez->SET(yy[i], ez->OR(g[i], ez->AND(p[i], i ? yy[i-1] : ci[0])));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_p = importUndefSigSpec(cell->getPort("\\P"), timestep);
+ std::vector<int> undef_g = importUndefSigSpec(cell->getPort("\\G"), timestep);
+ std::vector<int> undef_ci = importUndefSigSpec(cell->getPort("\\CI"), timestep);
+ std::vector<int> undef_co = importUndefSigSpec(cell->getPort("\\CO"), timestep);
+
+ int undef_any_p = ez->expression(ezSAT::OpOr, undef_p);
+ int undef_any_g = ez->expression(ezSAT::OpOr, undef_g);
+ int undef_any_ci = ez->expression(ezSAT::OpOr, undef_ci);
+ int undef_co_bit = ez->OR(undef_any_p, undef_any_g, undef_any_ci);
+
+ std::vector<int> undef_co_bits(undef_co.size(), undef_co_bit);
+ ez->assume(ez->vec_eq(undef_co_bits, undef_co));
+
+ undefGating(co, yy, undef_co);
+ }
+ return true;
+ }
+
+ if (cell->type == "$alu")
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort("\\Y"), timestep);
+ std::vector<int> x = importDefSigSpec(cell->getPort("\\X"), timestep);
+ std::vector<int> ci = importDefSigSpec(cell->getPort("\\CI"), timestep);
+ std::vector<int> bi = importDefSigSpec(cell->getPort("\\BI"), timestep);
+ std::vector<int> co = importDefSigSpec(cell->getPort("\\CO"), timestep);
+
+ extendSignalWidth(a, b, y, cell);
+ extendSignalWidth(a, b, x, cell);
+ extendSignalWidth(a, b, co, cell);
+
+ std::vector<int> def_y = model_undef ? ez->vec_var(y.size()) : y;
+ std::vector<int> def_x = model_undef ? ez->vec_var(x.size()) : x;
+ std::vector<int> def_co = model_undef ? ez->vec_var(co.size()) : co;
+
+ log_assert(GetSize(y) == GetSize(x));
+ log_assert(GetSize(y) == GetSize(co));
+ log_assert(GetSize(ci) == 1);
+ log_assert(GetSize(bi) == 1);
+
+ for (int i = 0; i < GetSize(y); i++)
+ {
+ int s1 = a.at(i), s2 = ez->XOR(b.at(i), bi.at(0)), s3 = i ? co.at(i-1) : ci.at(0);
+ ez->SET(def_x.at(i), ez->XOR(s1, s2));
+ ez->SET(def_y.at(i), ez->XOR(def_x.at(i), s3));
+ ez->SET(def_co.at(i), ez->OR(ez->AND(s1, s2), ez->AND(s1, s3), ez->AND(s2, s3)));
+ }
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort("\\B"), timestep);
+ std::vector<int> undef_ci = importUndefSigSpec(cell->getPort("\\CI"), timestep);
+ std::vector<int> undef_bi = importUndefSigSpec(cell->getPort("\\BI"), timestep);
+
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+ std::vector<int> undef_x = importUndefSigSpec(cell->getPort("\\X"), timestep);
+ std::vector<int> undef_co = importUndefSigSpec(cell->getPort("\\CO"), timestep);
+
+ extendSignalWidth(undef_a, undef_b, undef_y, cell);
+ extendSignalWidth(undef_a, undef_b, undef_x, cell);
+ extendSignalWidth(undef_a, undef_b, undef_co, cell);
+
+ std::vector<int> all_inputs_undef;
+ all_inputs_undef.insert(all_inputs_undef.end(), undef_a.begin(), undef_a.end());
+ all_inputs_undef.insert(all_inputs_undef.end(), undef_b.begin(), undef_b.end());
+ all_inputs_undef.insert(all_inputs_undef.end(), undef_ci.begin(), undef_ci.end());
+ all_inputs_undef.insert(all_inputs_undef.end(), undef_bi.begin(), undef_bi.end());
+ int undef_any = ez->expression(ezSAT::OpOr, all_inputs_undef);
+
+ for (int i = 0; i < GetSize(undef_y); i++) {
+ ez->SET(undef_y.at(i), undef_any);
+ ez->SET(undef_x.at(i), ez->OR(undef_a.at(i), undef_b.at(i), undef_bi.at(0)));
+ ez->SET(undef_co.at(i), undef_any);
+ }
+
+ undefGating(y, def_y, undef_y);
+ undefGating(x, def_x, undef_x);
+ undefGating(co, def_co, undef_co);
+ }
+ return true;
+ }
+
+ if (cell->type == "$slice")
+ {
+ RTLIL::SigSpec a = cell->getPort("\\A");
+ RTLIL::SigSpec y = cell->getPort("\\Y");
+ ez->assume(signals_eq(a.extract(cell->parameters.at("\\OFFSET").as_int(), y.size()), y, timestep));
+ return true;
+ }
+
+ if (cell->type == "$concat")
+ {
+ RTLIL::SigSpec a = cell->getPort("\\A");
+ RTLIL::SigSpec b = cell->getPort("\\B");
+ RTLIL::SigSpec y = cell->getPort("\\Y");
+
+ RTLIL::SigSpec ab = a;
+ ab.append(b);
+
+ ez->assume(signals_eq(ab, y, timestep));
+ return true;
+ }
+
+ if (timestep > 0 && cell->type.in("$ff", "$dff", "$_FF_", "$_DFF_N_", "$_DFF_P_"))
+ {
+ if (timestep == 1)
+ {
+ initial_state.add((*sigmap)(cell->getPort("\\Q")));
+ }
+ else
+ {
+ std::vector<int> d = importDefSigSpec(cell->getPort("\\D"), timestep-1);
+ std::vector<int> q = importDefSigSpec(cell->getPort("\\Q"), timestep);
+
+ std::vector<int> qq = model_undef ? ez->vec_var(q.size()) : q;
+ ez->assume(ez->vec_eq(d, qq));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_d = importUndefSigSpec(cell->getPort("\\D"), timestep-1);
+ std::vector<int> undef_q = importUndefSigSpec(cell->getPort("\\Q"), timestep);
+
+ ez->assume(ez->vec_eq(undef_d, undef_q));
+ undefGating(q, qq, undef_q);
+ }
+ }
+ return true;
+ }
+
+ if (cell->type == "$anyconst")
+ {
+ if (timestep < 2)
+ return true;
+
+ std::vector<int> d = importDefSigSpec(cell->getPort("\\Y"), timestep-1);
+ std::vector<int> q = importDefSigSpec(cell->getPort("\\Y"), timestep);
+
+ std::vector<int> qq = model_undef ? ez->vec_var(q.size()) : q;
+ ez->assume(ez->vec_eq(d, qq));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_d = importUndefSigSpec(cell->getPort("\\Y"), timestep-1);
+ std::vector<int> undef_q = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+
+ ez->assume(ez->vec_eq(undef_d, undef_q));
+ undefGating(q, qq, undef_q);
+ }
+ return true;
+ }
+
+ if (cell->type == "$anyseq")
+ {
+ return true;
+ }
+
+ if (cell->type == "$_BUF_" || cell->type == "$equiv")
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort("\\Y"), timestep);
+ extendSignalWidthUnary(a, y, cell);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+ ez->assume(ez->vec_eq(a, yy));
+
+ if (model_undef) {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+ extendSignalWidthUnary(undef_a, undef_y, cell, false);
+ ez->assume(ez->vec_eq(undef_a, undef_y));
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type == "$initstate")
+ {
+ auto key = make_pair(prefix, timestep);
+ if (initstates.count(key) == 0)
+ initstates[key] = false;
+
+ std::vector<int> y = importDefSigSpec(cell->getPort("\\Y"), timestep);
+ log_assert(GetSize(y) == 1);
+ ez->SET(y[0], initstates[key] ? ez->CONST_TRUE : ez->CONST_FALSE);
+
+ if (model_undef) {
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep);
+ log_assert(GetSize(undef_y) == 1);
+ ez->SET(undef_y[0], ez->CONST_FALSE);
+ }
+
+ return true;
+ }
+
+ if (cell->type == "$assert")
+ {
+ std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep));
+ asserts_a[pf].append((*sigmap)(cell->getPort("\\A")));
+ asserts_en[pf].append((*sigmap)(cell->getPort("\\EN")));
+ return true;
+ }
+
+ if (cell->type == "$assume")
+ {
+ std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep));
+ assumes_a[pf].append((*sigmap)(cell->getPort("\\A")));
+ assumes_en[pf].append((*sigmap)(cell->getPort("\\EN")));
+ return true;
+ }
+
+ // Unsupported internal cell types: $pow $lut
+ // .. and all sequential cells except $dff and $_DFF_[NP]_
+ return false;
+ }
+};
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/kernel/sigtools.h b/kernel/sigtools.h
new file mode 100644
index 00000000..4e97bb77
--- /dev/null
+++ b/kernel/sigtools.h
@@ -0,0 +1,332 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef SIGTOOLS_H
+#define SIGTOOLS_H
+
+#include "kernel/yosys.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+struct SigPool
+{
+ struct bitDef_t : public std::pair<RTLIL::Wire*, int> {
+ bitDef_t() : std::pair<RTLIL::Wire*, int>(NULL, 0) { }
+ bitDef_t(const RTLIL::SigBit &bit) : std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset) { }
+ unsigned int hash() const { return first->name.hash() + second; }
+ };
+
+ pool<bitDef_t> bits;
+
+ void clear()
+ {
+ bits.clear();
+ }
+
+ void add(RTLIL::SigSpec sig)
+ {
+ for (auto &bit : sig)
+ if (bit.wire != NULL)
+ bits.insert(bit);
+ }
+
+ void add(const SigPool &other)
+ {
+ for (auto &bit : other.bits)
+ bits.insert(bit);
+ }
+
+ void del(RTLIL::SigSpec sig)
+ {
+ for (auto &bit : sig)
+ if (bit.wire != NULL)
+ bits.erase(bit);
+ }
+
+ void del(const SigPool &other)
+ {
+ for (auto &bit : other.bits)
+ bits.erase(bit);
+ }
+
+ void expand(RTLIL::SigSpec from, RTLIL::SigSpec to)
+ {
+ log_assert(GetSize(from) == GetSize(to));
+ for (int i = 0; i < GetSize(from); i++) {
+ bitDef_t bit_from(from[i]), bit_to(to[i]);
+ if (bit_from.first != NULL && bit_to.first != NULL && bits.count(bit_from) > 0)
+ bits.insert(bit_to);
+ }
+ }
+
+ RTLIL::SigSpec extract(RTLIL::SigSpec sig)
+ {
+ RTLIL::SigSpec result;
+ for (auto &bit : sig)
+ if (bit.wire != NULL && bits.count(bit))
+ result.append_bit(bit);
+ return result;
+ }
+
+ RTLIL::SigSpec remove(RTLIL::SigSpec sig)
+ {
+ RTLIL::SigSpec result;
+ for (auto &bit : sig)
+ if (bit.wire != NULL && bits.count(bit) == 0)
+ result.append(bit);
+ return result;
+ }
+
+ bool check(RTLIL::SigBit bit)
+ {
+ return bit.wire != NULL && bits.count(bit);
+ }
+
+ bool check_any(RTLIL::SigSpec sig)
+ {
+ for (auto &bit : sig)
+ if (bit.wire != NULL && bits.count(bit))
+ return true;
+ return false;
+ }
+
+ bool check_all(RTLIL::SigSpec sig)
+ {
+ for (auto &bit : sig)
+ if (bit.wire != NULL && bits.count(bit) == 0)
+ return false;
+ return true;
+ }
+
+ RTLIL::SigSpec export_one()
+ {
+ for (auto &bit : bits)
+ return RTLIL::SigSpec(bit.first, bit.second);
+ return RTLIL::SigSpec();
+ }
+
+ RTLIL::SigSpec export_all()
+ {
+ pool<RTLIL::SigBit> sig;
+ for (auto &bit : bits)
+ sig.insert(RTLIL::SigBit(bit.first, bit.second));
+ return sig;
+ }
+
+ size_t size() const
+ {
+ return bits.size();
+ }
+};
+
+template <typename T, class Compare = std::less<T>>
+struct SigSet
+{
+ struct bitDef_t : public std::pair<RTLIL::Wire*, int> {
+ bitDef_t() : std::pair<RTLIL::Wire*, int>(NULL, 0) { }
+ bitDef_t(const RTLIL::SigBit &bit) : std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset) { }
+ unsigned int hash() const { return first->name.hash() + second; }
+ };
+
+ dict<bitDef_t, std::set<T, Compare>> bits;
+
+ void clear()
+ {
+ bits.clear();
+ }
+
+ void insert(RTLIL::SigSpec sig, T data)
+ {
+ for (auto &bit : sig)
+ if (bit.wire != NULL)
+ bits[bit].insert(data);
+ }
+
+ void insert(RTLIL::SigSpec sig, const std::set<T> &data)
+ {
+ for (auto &bit : sig)
+ if (bit.wire != NULL)
+ bits[bit].insert(data.begin(), data.end());
+ }
+
+ void erase(RTLIL::SigSpec sig)
+ {
+ for (auto &bit : sig)
+ if (bit.wire != NULL)
+ bits[bit].clear();
+ }
+
+ void erase(RTLIL::SigSpec sig, T data)
+ {
+ for (auto &bit : sig)
+ if (bit.wire != NULL)
+ bits[bit].erase(data);
+ }
+
+ void erase(RTLIL::SigSpec sig, const std::set<T> &data)
+ {
+ for (auto &bit : sig)
+ if (bit.wire != NULL)
+ bits[bit].erase(data.begin(), data.end());
+ }
+
+ void find(RTLIL::SigSpec sig, std::set<T> &result)
+ {
+ for (auto &bit : sig)
+ if (bit.wire != NULL) {
+ auto &data = bits[bit];
+ result.insert(data.begin(), data.end());
+ }
+ }
+
+ void find(RTLIL::SigSpec sig, pool<T> &result)
+ {
+ for (auto &bit : sig)
+ if (bit.wire != NULL) {
+ auto &data = bits[bit];
+ result.insert(data.begin(), data.end());
+ }
+ }
+
+ std::set<T> find(RTLIL::SigSpec sig)
+ {
+ std::set<T> result;
+ find(sig, result);
+ return result;
+ }
+
+ bool has(RTLIL::SigSpec sig)
+ {
+ for (auto &bit : sig)
+ if (bit.wire != NULL && bits.count(bit))
+ return true;
+ return false;
+ }
+};
+
+struct SigMap
+{
+ mfp<SigBit> database;
+
+ SigMap(RTLIL::Module *module = NULL)
+ {
+ if (module != NULL)
+ set(module);
+ }
+
+ void swap(SigMap &other)
+ {
+ database.swap(other.database);
+ }
+
+ void clear()
+ {
+ database.clear();
+ }
+
+ void set(RTLIL::Module *module)
+ {
+ int bitcount = 0;
+ for (auto &it : module->connections())
+ bitcount += it.first.size();
+
+ database.clear();
+ database.reserve(bitcount);
+
+ for (auto &it : module->connections())
+ add(it.first, it.second);
+ }
+
+ void add(RTLIL::SigSpec from, RTLIL::SigSpec to)
+ {
+ log_assert(GetSize(from) == GetSize(to));
+
+ for (int i = 0; i < GetSize(from); i++)
+ {
+ int bfi = database.lookup(from[i]);
+ int bti = database.lookup(to[i]);
+
+ const RTLIL::SigBit &bf = database[bfi];
+ const RTLIL::SigBit &bt = database[bti];
+
+ if (bf.wire || bt.wire)
+ {
+ database.imerge(bfi, bti);
+
+ if (bf.wire == nullptr)
+ database.ipromote(bfi);
+
+ if (bt.wire == nullptr)
+ database.ipromote(bti);
+ }
+ }
+ }
+
+ void add(RTLIL::SigSpec sig)
+ {
+ for (auto &bit : sig) {
+ RTLIL::SigBit b = database.find(bit);
+ if (b.wire != nullptr)
+ database.promote(bit);
+ }
+ }
+
+ void apply(RTLIL::SigBit &bit) const
+ {
+ bit = database.find(bit);
+ }
+
+ void apply(RTLIL::SigSpec &sig) const
+ {
+ for (auto &bit : sig)
+ apply(bit);
+ }
+
+ RTLIL::SigBit operator()(RTLIL::SigBit bit) const
+ {
+ apply(bit);
+ return bit;
+ }
+
+ RTLIL::SigSpec operator()(RTLIL::SigSpec sig) const
+ {
+ apply(sig);
+ return sig;
+ }
+
+ RTLIL::SigSpec operator()(RTLIL::Wire *wire) const
+ {
+ SigSpec sig(wire);
+ apply(sig);
+ return sig;
+ }
+
+ RTLIL::SigSpec allbits() const
+ {
+ RTLIL::SigSpec sig;
+ for (auto &bit : database)
+ if (bit.wire != nullptr)
+ sig.append(bit);
+ return sig;
+ }
+};
+
+YOSYS_NAMESPACE_END
+
+#endif /* SIGTOOLS_H */
diff --git a/kernel/utils.h b/kernel/utils.h
new file mode 100644
index 00000000..8942905f
--- /dev/null
+++ b/kernel/utils.h
@@ -0,0 +1,214 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+// This file contains various c++ utility routines and helper classes that
+// do not depend on any other components of yosys (except stuff like log_*).
+
+#include "kernel/yosys.h"
+
+#ifndef UTILS_H
+#define UTILS_H
+
+YOSYS_NAMESPACE_BEGIN
+
+// ------------------------------------------------
+// A map-like container, but you can save and restore the state
+// ------------------------------------------------
+
+template<typename Key, typename T, typename OPS = hash_ops<Key>>
+struct stackmap
+{
+private:
+ std::vector<dict<Key, T*, OPS>> backup_state;
+ dict<Key, T, OPS> current_state;
+ static T empty_tuple;
+
+public:
+ stackmap() { }
+ stackmap(const dict<Key, T, OPS> &other) : current_state(other) { }
+
+ template<typename Other>
+ void operator=(const Other &other)
+ {
+ for (auto &it : current_state)
+ if (!backup_state.empty() && backup_state.back().count(it.first) == 0)
+ backup_state.back()[it.first] = new T(it.second);
+ current_state.clear();
+
+ for (auto &it : other)
+ set(it.first, it.second);
+ }
+
+ bool has(const Key &k)
+ {
+ return current_state.count(k) != 0;
+ }
+
+ void set(const Key &k, const T &v)
+ {
+ if (!backup_state.empty() && backup_state.back().count(k) == 0)
+ backup_state.back()[k] = current_state.count(k) ? new T(current_state.at(k)) : nullptr;
+ current_state[k] = v;
+ }
+
+ void unset(const Key &k)
+ {
+ if (!backup_state.empty() && backup_state.back().count(k) == 0)
+ backup_state.back()[k] = current_state.count(k) ? new T(current_state.at(k)) : nullptr;
+ current_state.erase(k);
+ }
+
+ const T &get(const Key &k)
+ {
+ if (current_state.count(k) == 0)
+ return empty_tuple;
+ return current_state.at(k);
+ }
+
+ void reset(const Key &k)
+ {
+ for (int i = GetSize(backup_state)-1; i >= 0; i--)
+ if (backup_state[i].count(k) != 0) {
+ if (backup_state[i].at(k) == nullptr)
+ current_state.erase(k);
+ else
+ current_state[k] = *backup_state[i].at(k);
+ return;
+ }
+ current_state.erase(k);
+ }
+
+ const dict<Key, T, OPS> &stdmap()
+ {
+ return current_state;
+ }
+
+ void save()
+ {
+ backup_state.resize(backup_state.size()+1);
+ }
+
+ void restore()
+ {
+ log_assert(!backup_state.empty());
+ for (auto &it : backup_state.back())
+ if (it.second != nullptr) {
+ current_state[it.first] = *it.second;
+ delete it.second;
+ } else
+ current_state.erase(it.first);
+ backup_state.pop_back();
+ }
+
+ ~stackmap()
+ {
+ while (!backup_state.empty())
+ restore();
+ }
+};
+
+
+// ------------------------------------------------
+// A simple class for topological sorting
+// ------------------------------------------------
+
+template<typename T, typename C = std::less<T>>
+struct TopoSort
+{
+ bool analyze_loops, found_loops;
+ std::map<T, std::set<T, C>, C> database;
+ std::set<std::set<T, C>> loops;
+ std::vector<T> sorted;
+
+ TopoSort()
+ {
+ analyze_loops = true;
+ found_loops = false;
+ }
+
+ void node(T n)
+ {
+ if (database.count(n) == 0)
+ database[n] = std::set<T, C>();
+ }
+
+ void edge(T left, T right)
+ {
+ node(left);
+ database[right].insert(left);
+ }
+
+ void sort_worker(const T &n, std::set<T, C> &marked_cells, std::set<T, C> &active_cells, std::vector<T> &active_stack)
+ {
+ if (active_cells.count(n)) {
+ found_loops = true;
+ if (analyze_loops) {
+ std::set<T, C> loop;
+ for (int i = GetSize(active_stack)-1; i >= 0; i--) {
+ loop.insert(active_stack[i]);
+ if (active_stack[i] == n)
+ break;
+ }
+ loops.insert(loop);
+ }
+ return;
+ }
+
+ if (marked_cells.count(n))
+ return;
+
+ if (!database.at(n).empty())
+ {
+ if (analyze_loops)
+ active_stack.push_back(n);
+ active_cells.insert(n);
+
+ for (auto &left_n : database.at(n))
+ sort_worker(left_n, marked_cells, active_cells, active_stack);
+
+ if (analyze_loops)
+ active_stack.pop_back();
+ active_cells.erase(n);
+ }
+
+ marked_cells.insert(n);
+ sorted.push_back(n);
+ }
+
+ bool sort()
+ {
+ loops.clear();
+ sorted.clear();
+ found_loops = false;
+
+ std::set<T, C> marked_cells;
+ std::set<T, C> active_cells;
+ std::vector<T> active_stack;
+
+ for (auto &it : database)
+ sort_worker(it.first, marked_cells, active_cells, active_stack);
+
+ log_assert(GetSize(sorted) == GetSize(database));
+ return !found_loops;
+ }
+};
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/kernel/yosys.cc b/kernel/yosys.cc
new file mode 100644
index 00000000..08fee974
--- /dev/null
+++ b/kernel/yosys.cc
@@ -0,0 +1,1157 @@
+/*
+ * 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/celltypes.h"
+
+#ifdef YOSYS_ENABLE_READLINE
+# include <readline/readline.h>
+# include <readline/history.h>
+#endif
+
+#ifdef YOSYS_ENABLE_PLUGINS
+# include <dlfcn.h>
+#endif
+
+#ifdef _WIN32
+# include <windows.h>
+# include <io.h>
+#elif defined(__APPLE__)
+# include <mach-o/dyld.h>
+# 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/stat.h>
+# include <glob.h>
+#endif
+
+#include <limits.h>
+#include <errno.h>
+
+YOSYS_NAMESPACE_BEGIN
+
+int autoidx = 1;
+int yosys_xtrace = 0;
+RTLIL::Design *yosys_design = NULL;
+CellTypes yosys_celltypes;
+
+#ifdef YOSYS_ENABLE_TCL
+Tcl_Interp *yosys_tcl_interp = NULL;
+#endif
+
+bool memhasher_active = false;
+uint32_t memhasher_rng = 123456;
+std::vector<void*> memhasher_store;
+
+void memhasher_on()
+{
+#ifdef __linux__
+ memhasher_rng += time(NULL) << 16 ^ getpid();
+#endif
+ memhasher_store.resize(0x10000);
+ memhasher_active = true;
+}
+
+void memhasher_off()
+{
+ for (auto p : memhasher_store)
+ if (p) free(p);
+ memhasher_store.clear();
+ memhasher_active = false;
+}
+
+void memhasher_do()
+{
+ memhasher_rng ^= memhasher_rng << 13;
+ memhasher_rng ^= memhasher_rng >> 17;
+ memhasher_rng ^= memhasher_rng << 5;
+
+ int size, index = (memhasher_rng >> 4) & 0xffff;
+ switch (memhasher_rng & 7) {
+ case 0: size = 16; break;
+ case 1: size = 256; break;
+ case 2: size = 1024; break;
+ case 3: size = 4096; break;
+ default: size = 0;
+ }
+ if (index < 16) size *= 16;
+ memhasher_store[index] = realloc(memhasher_store[index], size);
+}
+
+void yosys_banner()
+{
+ log("\n");
+ log(" /----------------------------------------------------------------------------\\\n");
+ log(" | |\n");
+ log(" | yosys -- Yosys Open SYnthesis Suite |\n");
+ log(" | |\n");
+ log(" | Copyright (C) 2012 - 2016 Clifford Wolf <clifford@clifford.at> |\n");
+ log(" | |\n");
+ log(" | Permission to use, copy, modify, and/or distribute this software for any |\n");
+ log(" | purpose with or without fee is hereby granted, provided that the above |\n");
+ log(" | copyright notice and this permission notice appear in all copies. |\n");
+ log(" | |\n");
+ log(" | THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |\n");
+ log(" | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |\n");
+ log(" | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |\n");
+ log(" | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |\n");
+ log(" | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |\n");
+ log(" | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |\n");
+ log(" | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |\n");
+ log(" | |\n");
+ log(" \\----------------------------------------------------------------------------/\n");
+ log("\n");
+ log(" %s\n", yosys_version_str);
+ log("\n");
+}
+
+int ceil_log2(int x)
+{
+ if (x <= 0)
+ return 0;
+
+ for (int i = 0; i < 32; i++)
+ if (((x-1) >> i) == 0)
+ return i;
+
+ log_abort();
+}
+
+std::string stringf(const char *fmt, ...)
+{
+ std::string string;
+ va_list ap;
+
+ va_start(ap, fmt);
+ string = vstringf(fmt, ap);
+ va_end(ap);
+
+ return string;
+}
+
+std::string vstringf(const char *fmt, va_list ap)
+{
+ std::string string;
+ char *str = NULL;
+
+#ifdef _WIN32
+ int sz = 64, rc;
+ while (1) {
+ va_list apc;
+ va_copy(apc, ap);
+ str = (char*)realloc(str, sz);
+ rc = vsnprintf(str, sz, fmt, apc);
+ va_end(apc);
+ if (rc >= 0 && rc < sz)
+ break;
+ sz *= 2;
+ }
+#else
+ if (vasprintf(&str, fmt, ap) < 0)
+ str = NULL;
+#endif
+
+ if (str != NULL) {
+ string = str;
+ free(str);
+ }
+
+ return string;
+}
+
+int readsome(std::istream &f, char *s, int n)
+{
+ int rc = int(f.readsome(s, n));
+
+ // f.readsome() sometimes returns 0 on a non-empty stream..
+ if (rc == 0) {
+ int c = f.get();
+ if (c != EOF) {
+ *s = c;
+ rc = 1;
+ }
+ }
+
+ return rc;
+}
+
+std::string next_token(std::string &text, const char *sep, bool long_strings)
+{
+ size_t pos_begin = text.find_first_not_of(sep);
+
+ if (pos_begin == std::string::npos)
+ pos_begin = text.size();
+
+ 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++)
+ 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;
+ }
+ }
+
+ size_t pos_end = text.find_first_of(sep, pos_begin);
+
+ if (pos_end == std::string::npos)
+ pos_end = text.size();
+
+ std::string token = text.substr(pos_begin, pos_end-pos_begin);
+ text = text.substr(pos_end);
+ return token;
+}
+
+std::vector<std::string> split_tokens(const std::string &text, const char *sep)
+{
+ std::vector<std::string> tokens;
+ std::string current_token;
+ for (char c : text) {
+ if (strchr(sep, c)) {
+ if (!current_token.empty()) {
+ tokens.push_back(current_token);
+ current_token.clear();
+ }
+ } else
+ current_token += c;
+ }
+ if (!current_token.empty()) {
+ tokens.push_back(current_token);
+ current_token.clear();
+ }
+ return tokens;
+}
+
+// this is very similar to fnmatch(). the exact rules used by this
+// function are:
+//
+// ? matches any character except
+// * matches any sequence of characters
+// [...] matches any of the characters in the list
+// [!..] matches any of the characters not in the list
+//
+// a backslash may be used to escape the next characters in the
+// pattern. each special character can also simply match itself.
+//
+bool patmatch(const char *pattern, const char *string)
+{
+ if (*pattern == 0)
+ return *string == 0;
+
+ if (*pattern == '\\') {
+ if (pattern[1] == string[0] && patmatch(pattern+2, string+1))
+ return true;
+ }
+
+ if (*pattern == '?') {
+ if (*string == 0)
+ return false;
+ return patmatch(pattern+1, string+1);
+ }
+
+ if (*pattern == '*') {
+ while (*string) {
+ if (patmatch(pattern+1, string++))
+ return true;
+ }
+ return pattern[1] == 0;
+ }
+
+ if (*pattern == '[') {
+ bool found_match = false;
+ bool inverted_list = pattern[1] == '!';
+ const char *p = pattern + (inverted_list ? 1 : 0);
+
+ while (*++p) {
+ if (*p == ']') {
+ if (found_match != inverted_list && patmatch(p+1, string+1))
+ return true;
+ break;
+ }
+
+ if (*p == '\\') {
+ if (*++p == *string)
+ found_match = true;
+ } else
+ if (*p == *string)
+ found_match = true;
+ }
+ }
+
+ if (*pattern == *string)
+ return patmatch(pattern+1, string+1);
+
+ return false;
+}
+
+int run_command(const std::string &command, std::function<void(const std::string&)> process_line)
+{
+ if (!process_line)
+ return system(command.c_str());
+
+ FILE *f = popen(command.c_str(), "r");
+ if (f == nullptr)
+ return -1;
+
+ std::string line;
+ char logbuf[128];
+ while (fgets(logbuf, 128, f) != NULL) {
+ line += logbuf;
+ if (!line.empty() && line.back() == '\n')
+ process_line(line), line.clear();
+ }
+ if (!line.empty())
+ process_line(line);
+
+ int ret = pclose(f);
+ if (ret < 0)
+ return -1;
+#ifdef _WIN32
+ return ret;
+#else
+ return WEXITSTATUS(ret);
+#endif
+}
+
+std::string make_temp_file(std::string template_str)
+{
+#ifdef _WIN32
+ if (template_str.rfind("/tmp/", 0) == 0) {
+# ifdef __MINGW32__
+ char longpath[MAX_PATH + 1];
+ char shortpath[MAX_PATH + 1];
+# else
+ WCHAR longpath[MAX_PATH + 1];
+ TCHAR shortpath[MAX_PATH + 1];
+# endif
+ if (!GetTempPath(MAX_PATH+1, longpath))
+ log_error("GetTempPath() failed.\n");
+ if (!GetShortPathName(longpath, shortpath, MAX_PATH + 1))
+ log_error("GetShortPathName() failed.\n");
+ std::string path;
+ for (int i = 0; shortpath[i]; i++)
+ path += char(shortpath[i]);
+ template_str = stringf("%s\\%s", path.c_str(), template_str.c_str() + 5);
+ }
+
+ size_t pos = template_str.rfind("XXXXXX");
+ log_assert(pos != std::string::npos);
+
+ while (1) {
+ for (int i = 0; i < 6; i++) {
+ static std::string y = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ static uint32_t x = 314159265 ^ uint32_t(time(NULL));
+ x ^= x << 13, x ^= x >> 17, x ^= x << 5;
+ template_str[pos+i] = y[x % y.size()];
+ }
+ if (_access(template_str.c_str(), 0) != 0)
+ break;
+ }
+#else
+ size_t pos = template_str.rfind("XXXXXX");
+ log_assert(pos != std::string::npos);
+
+ int suffixlen = GetSize(template_str) - pos - 6;
+
+ char *p = strdup(template_str.c_str());
+ close(mkstemps(p, suffixlen));
+ template_str = p;
+ free(p);
+#endif
+
+ return template_str;
+}
+
+std::string make_temp_dir(std::string template_str)
+{
+#ifdef _WIN32
+ template_str = make_temp_file(template_str);
+ mkdir(template_str.c_str());
+ return template_str;
+#else
+# ifndef NDEBUG
+ size_t pos = template_str.rfind("XXXXXX");
+ log_assert(pos != std::string::npos);
+
+ int suffixlen = GetSize(template_str) - pos - 6;
+ log_assert(suffixlen == 0);
+# endif
+
+ char *p = strdup(template_str.c_str());
+ p = mkdtemp(p);
+ log_assert(p != NULL);
+ template_str = p;
+ free(p);
+
+ return template_str;
+#endif
+}
+
+#ifdef _WIN32
+bool check_file_exists(std::string filename, bool)
+{
+ return _access(filename.c_str(), 0) == 0;
+}
+#else
+bool check_file_exists(std::string filename, bool is_exec)
+{
+ return access(filename.c_str(), is_exec ? X_OK : F_OK) == 0;
+}
+#endif
+
+bool is_absolute_path(std::string filename)
+{
+#ifdef _WIN32
+ return filename[0] == '/' || filename[0] == '\\' || (filename[0] != 0 && filename[1] == ':');
+#else
+ return filename[0] == '/';
+#endif
+}
+
+void remove_directory(std::string dirname)
+{
+#ifdef _WIN32
+ run_command(stringf("rmdir /s /q \"%s\"", dirname.c_str()));
+#else
+ struct stat stbuf;
+ struct dirent **namelist;
+ int n = scandir(dirname.c_str(), &namelist, nullptr, alphasort);
+ log_assert(n >= 0);
+ for (int i = 0; i < n; i++) {
+ if (strcmp(namelist[i]->d_name, ".") && strcmp(namelist[i]->d_name, "..")) {
+ std::string buffer = stringf("%s/%s", dirname.c_str(), namelist[i]->d_name);
+ if (!stat(buffer.c_str(), &stbuf) && S_ISREG(stbuf.st_mode)) {
+ remove(buffer.c_str());
+ } else
+ remove_directory(buffer);
+ }
+ free(namelist[i]);
+ }
+ free(namelist);
+ rmdir(dirname.c_str());
+#endif
+}
+
+int GetSize(RTLIL::Wire *wire)
+{
+ return wire->width;
+}
+
+void yosys_setup()
+{
+ // if there are already IdString objects then we have a global initialization order bug
+ IdString empty_id;
+ log_assert(empty_id.index_ == 0);
+ IdString::get_reference(empty_id.index_);
+
+ Pass::init_register();
+ yosys_design = new RTLIL::Design;
+ yosys_celltypes.setup();
+ log_push();
+}
+
+void yosys_shutdown()
+{
+ log_pop();
+
+ delete yosys_design;
+ yosys_design = NULL;
+
+ for (auto f : log_files)
+ if (f != stderr)
+ fclose(f);
+ log_errfile = NULL;
+ log_files.clear();
+
+ Pass::done_register();
+ yosys_celltypes.clear();
+
+#ifdef YOSYS_ENABLE_TCL
+ if (yosys_tcl_interp != NULL) {
+ Tcl_DeleteInterp(yosys_tcl_interp);
+ Tcl_Finalize();
+ yosys_tcl_interp = NULL;
+ }
+#endif
+
+#ifdef YOSYS_ENABLE_PLUGINS
+ for (auto &it : loaded_plugins)
+ dlclose(it.second);
+
+ loaded_plugins.clear();
+ loaded_plugin_aliases.clear();
+#endif
+
+ IdString empty_id;
+ IdString::put_reference(empty_id.index_);
+}
+
+RTLIL::IdString new_id(std::string file, int line, std::string func)
+{
+#ifdef _WIN32
+ size_t pos = file.find_last_of("/\\");
+#else
+ size_t pos = file.find_last_of('/');
+#endif
+ if (pos != std::string::npos)
+ file = file.substr(pos+1);
+
+ pos = func.find_last_of(':');
+ if (pos != std::string::npos)
+ func = func.substr(pos+1);
+
+ return stringf("$auto$%s:%d:%s$%d", file.c_str(), line, func.c_str(), autoidx++);
+}
+
+RTLIL::Design *yosys_get_design()
+{
+ return yosys_design;
+}
+
+const char *create_prompt(RTLIL::Design *design, int recursion_counter)
+{
+ static char buffer[100];
+ std::string str = "\n";
+ if (recursion_counter > 1)
+ str += stringf("(%d) ", recursion_counter);
+ str += "yosys";
+ if (!design->selected_active_module.empty())
+ str += stringf(" [%s]", RTLIL::unescape_id(design->selected_active_module).c_str());
+ if (!design->selection_stack.empty() && !design->selection_stack.back().full_selection) {
+ if (design->selected_active_module.empty())
+ str += "*";
+ else if (design->selection_stack.back().selected_modules.size() != 1 || design->selection_stack.back().selected_members.size() != 0 ||
+ design->selection_stack.back().selected_modules.count(design->selected_active_module) == 0)
+ str += "*";
+ }
+ snprintf(buffer, 100, "%s> ", str.c_str());
+ return buffer;
+}
+
+std::vector<std::string> glob_filename(const std::string &filename_pattern)
+{
+ std::vector<std::string> results;
+
+#ifdef _WIN32
+ results.push_back(filename_pattern);
+#else
+ glob_t globbuf;
+
+ int err = glob(filename_pattern.c_str(), 0, NULL, &globbuf);
+
+ if(err == 0) {
+ for (size_t i = 0; i < globbuf.gl_pathc; i++)
+ results.push_back(globbuf.gl_pathv[i]);
+ globfree(&globbuf);
+ } else {
+ results.push_back(filename_pattern);
+ }
+#endif
+
+ return results;
+}
+
+void rewrite_filename(std::string &filename)
+{
+ if (filename.substr(0, 1) == "\"" && filename.substr(GetSize(filename)-1) == "\"")
+ filename = filename.substr(1, GetSize(filename)-2);
+ if (filename.substr(0, 2) == "+/")
+ filename = proc_share_dirname() + filename.substr(2);
+}
+
+#ifdef YOSYS_ENABLE_TCL
+static int tcl_yosys_cmd(ClientData, Tcl_Interp *interp, int argc, const char *argv[])
+{
+ std::vector<std::string> args;
+ for (int i = 1; i < argc; i++)
+ args.push_back(argv[i]);
+
+ if (args.size() >= 1 && args[0] == "-import") {
+ for (auto &it : pass_register) {
+ std::string tcl_command_name = it.first;
+ if (tcl_command_name == "proc")
+ tcl_command_name = "procs";
+ Tcl_CmdInfo info;
+ if (Tcl_GetCommandInfo(interp, tcl_command_name.c_str(), &info) != 0) {
+ log("[TCL: yosys -import] Command name collision: found pre-existing command `%s' -> skip.\n", it.first.c_str());
+ } else {
+ std::string tcl_script = stringf("proc %s args { yosys %s {*}$args }", tcl_command_name.c_str(), it.first.c_str());
+ Tcl_Eval(interp, tcl_script.c_str());
+ }
+ }
+ return TCL_OK;
+ }
+
+ if (args.size() == 1) {
+ Pass::call(yosys_get_design(), args[0]);
+ return TCL_OK;
+ }
+
+ Pass::call(yosys_get_design(), args);
+ return TCL_OK;
+}
+
+extern Tcl_Interp *yosys_get_tcl_interp()
+{
+ if (yosys_tcl_interp == NULL) {
+ yosys_tcl_interp = Tcl_CreateInterp();
+ Tcl_CreateCommand(yosys_tcl_interp, "yosys", tcl_yosys_cmd, NULL, NULL);
+ }
+ return yosys_tcl_interp;
+}
+
+struct TclPass : public Pass {
+ TclPass() : Pass("tcl", "execute a TCL script file") { }
+ virtual void help() {
+ log("\n");
+ log(" tcl <filename>\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");
+ log("\n");
+ log("The tcl command 'yosys -import' can be used to import all yosys\n");
+ log("commands directly as tcl commands to the tcl shell. The yosys\n");
+ log("command 'proc' is wrapped using the tcl command 'procs' in order\n");
+ log("to avoid a name collision with the tcl builtin command 'proc'.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
+ 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()));
+ }
+} TclPass;
+#endif
+
+#if defined(__linux__) || defined(__CYGWIN__)
+std::string proc_self_dirname()
+{
+ char path[PATH_MAX];
+ ssize_t buflen = readlink("/proc/self/exe", path, sizeof(path));
+ if (buflen < 0) {
+ log_error("readlink(\"/proc/self/exe\") failed: %s\n", strerror(errno));
+ }
+ while (buflen > 0 && path[buflen-1] != '/')
+ buflen--;
+ return std::string(path, buflen);
+}
+#elif defined(__APPLE__)
+std::string proc_self_dirname()
+{
+ char *path = NULL;
+ uint32_t buflen = 0;
+ while (_NSGetExecutablePath(path, &buflen) != 0)
+ path = (char *) realloc((void *) path, buflen);
+ while (buflen > 0 && path[buflen-1] != '/')
+ buflen--;
+ return std::string(path, buflen);
+}
+#elif defined(_WIN32)
+std::string proc_self_dirname()
+{
+ int i = 0;
+# ifdef __MINGW32__
+ char longpath[MAX_PATH + 1];
+ char shortpath[MAX_PATH + 1];
+# else
+ WCHAR longpath[MAX_PATH + 1];
+ TCHAR shortpath[MAX_PATH + 1];
+# endif
+ if (!GetModuleFileName(0, longpath, MAX_PATH+1))
+ log_error("GetModuleFileName() failed.\n");
+ if (!GetShortPathName(longpath, shortpath, MAX_PATH+1))
+ log_error("GetShortPathName() failed.\n");
+ while (shortpath[i] != 0)
+ i++;
+ while (i > 0 && shortpath[i-1] != '/' && shortpath[i-1] != '\\')
+ shortpath[--i] = 0;
+ std::string path;
+ for (i = 0; shortpath[i]; i++)
+ path += char(shortpath[i]);
+ return path;
+}
+#elif defined(EMSCRIPTEN)
+std::string proc_self_dirname()
+{
+ return "/";
+}
+#else
+ #error Dont know how to determine process executable base path!
+#endif
+
+#ifdef EMSCRIPTEN
+std::string proc_share_dirname()
+{
+ return "/share";
+}
+#else
+std::string proc_share_dirname()
+{
+ std::string proc_self_path = proc_self_dirname();
+# if defined(_WIN32) && !defined(YOSYS_WIN32_UNIX_DIR)
+ std::string proc_share_path = proc_self_path + "share\\";
+ if (check_file_exists(proc_share_path, true))
+ return proc_share_path;
+ proc_share_path = proc_self_path + "..\\share\\";
+ if (check_file_exists(proc_share_path, true))
+ return proc_share_path;
+# else
+ std::string proc_share_path = proc_self_path + "share/";
+ if (check_file_exists(proc_share_path, true))
+ return proc_share_path;
+ proc_share_path = proc_self_path + "../share/yosys/";
+ if (check_file_exists(proc_share_path, true))
+ return proc_share_path;
+# ifdef YOSYS_DATDIR
+ proc_share_path = YOSYS_DATDIR "/";
+ if (check_file_exists(proc_share_path, true))
+ return proc_share_path;
+# endif
+# endif
+ log_error("proc_share_dirname: unable to determine share/ directory!\n");
+}
+#endif
+
+bool fgetline(FILE *f, std::string &buffer)
+{
+ buffer = "";
+ char block[4096];
+ while (1) {
+ if (fgets(block, 4096, f) == NULL)
+ return false;
+ buffer += block;
+ if (buffer.size() > 0 && (buffer[buffer.size()-1] == '\n' || buffer[buffer.size()-1] == '\r')) {
+ while (buffer.size() > 0 && (buffer[buffer.size()-1] == '\n' || buffer[buffer.size()-1] == '\r'))
+ buffer.resize(buffer.size()-1);
+ return true;
+ }
+ }
+}
+
+static void handle_label(std::string &command, bool &from_to_active, const std::string &run_from, const std::string &run_to)
+{
+ int pos = 0;
+ std::string label;
+
+ while (pos < GetSize(command) && (command[pos] == ' ' || command[pos] == '\t'))
+ pos++;
+
+ if (pos < GetSize(command) && command[pos] == '#')
+ return;
+
+ while (pos < GetSize(command) && command[pos] != ' ' && command[pos] != '\t' && command[pos] != '\r' && command[pos] != '\n')
+ label += command[pos++];
+
+ if (label.back() == ':' && GetSize(label) > 1)
+ {
+ label = label.substr(0, GetSize(label)-1);
+ command = command.substr(pos);
+
+ if (label == run_from)
+ from_to_active = true;
+ else if (label == run_to || (run_from == run_to && !run_from.empty()))
+ from_to_active = false;
+ }
+}
+
+void run_frontend(std::string filename, std::string command, std::string *backend_command, std::string *from_to_label, RTLIL::Design *design)
+{
+ if (design == nullptr)
+ design = yosys_design;
+
+ if (command == "auto") {
+ if (filename.size() > 2 && filename.substr(filename.size()-2) == ".v")
+ command = "verilog";
+ 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")
+ command = "vhdl";
+ else if (filename.size() > 4 && filename.substr(filename.size()-5) == ".blif")
+ command = "blif";
+ else if (filename.size() > 3 && filename.substr(filename.size()-3) == ".il")
+ command = "ilang";
+ else if (filename.size() > 3 && filename.substr(filename.size()-3) == ".ys")
+ command = "script";
+ else if (filename == "-")
+ command = "script";
+ else
+ log_error("Can't guess frontend for input file `%s' (missing -f option)!\n", filename.c_str());
+ }
+
+ if (command == "script")
+ {
+ std::string run_from, run_to;
+ bool from_to_active = true;
+
+ if (from_to_label != NULL) {
+ size_t pos = from_to_label->find(':');
+ if (pos == std::string::npos) {
+ run_from = *from_to_label;
+ run_to = *from_to_label;
+ } else {
+ run_from = from_to_label->substr(0, pos);
+ run_to = from_to_label->substr(pos+1);
+ }
+ from_to_active = run_from.empty();
+ }
+
+ log("\n-- Executing script file `%s' --\n", filename.c_str());
+
+ FILE *f = stdin;
+
+ if (filename != "-")
+ f = fopen(filename.c_str(), "r");
+
+ if (f == NULL)
+ log_error("Can't open script file `%s' for reading: %s\n", filename.c_str(), strerror(errno));
+
+ FILE *backup_script_file = Frontend::current_script_file;
+ Frontend::current_script_file = f;
+
+ try {
+ std::string command;
+ while (fgetline(f, command)) {
+ while (!command.empty() && command[command.size()-1] == '\\') {
+ std::string next_line;
+ if (!fgetline(f, next_line))
+ break;
+ command.resize(command.size()-1);
+ command += next_line;
+ }
+ handle_label(command, from_to_active, run_from, run_to);
+ if (from_to_active)
+ Pass::call(design, command);
+ }
+
+ if (!command.empty()) {
+ handle_label(command, from_to_active, run_from, run_to);
+ if (from_to_active)
+ Pass::call(design, command);
+ }
+ }
+ catch (...) {
+ Frontend::current_script_file = backup_script_file;
+ throw;
+ }
+
+ Frontend::current_script_file = backup_script_file;
+
+ if (filename != "-")
+ fclose(f);
+
+ if (backend_command != NULL && *backend_command == "auto")
+ *backend_command = "";
+
+ return;
+ }
+
+ if (filename == "-") {
+ log("\n-- Parsing stdin using frontend `%s' --\n", command.c_str());
+ } else {
+ log("\n-- Parsing `%s' using frontend `%s' --\n", filename.c_str(), command.c_str());
+ }
+
+ Frontend::frontend_call(design, NULL, filename, command);
+}
+
+void run_frontend(std::string filename, std::string command, RTLIL::Design *design)
+{
+ run_frontend(filename, command, nullptr, nullptr, design);
+}
+
+void run_pass(std::string command, RTLIL::Design *design)
+{
+ if (design == nullptr)
+ design = yosys_design;
+
+ log("\n-- Running command `%s' --\n", command.c_str());
+
+ Pass::call(design, command);
+}
+
+void run_backend(std::string filename, std::string command, RTLIL::Design *design)
+{
+ if (design == nullptr)
+ design = yosys_design;
+
+ if (command == "auto") {
+ if (filename.size() > 2 && filename.substr(filename.size()-2) == ".v")
+ command = "verilog";
+ else if (filename.size() > 3 && filename.substr(filename.size()-3) == ".il")
+ command = "ilang";
+ else if (filename.size() > 5 && filename.substr(filename.size()-5) == ".blif")
+ command = "blif";
+ else if (filename.size() > 5 && filename.substr(filename.size()-5) == ".edif")
+ command = "edif";
+ else if (filename.size() > 5 && filename.substr(filename.size()-5) == ".json")
+ command = "json";
+ else if (filename == "-")
+ command = "ilang";
+ else if (filename.empty())
+ return;
+ else
+ log_error("Can't guess backend for output file `%s' (missing -b option)!\n", filename.c_str());
+ }
+
+ if (filename.empty())
+ filename = "-";
+
+ if (filename == "-") {
+ log("\n-- Writing to stdout using backend `%s' --\n", command.c_str());
+ } else {
+ log("\n-- Writing to `%s' using backend `%s' --\n", filename.c_str(), command.c_str());
+ }
+
+ Backend::backend_call(design, NULL, filename, command);
+}
+
+#ifdef YOSYS_ENABLE_READLINE
+static char *readline_cmd_generator(const char *text, int state)
+{
+ static std::map<std::string, Pass*>::iterator it;
+ static int len;
+
+ if (!state) {
+ it = pass_register.begin();
+ len = strlen(text);
+ }
+
+ for (; it != pass_register.end(); it++) {
+ if (it->first.substr(0, len) == text)
+ return strdup((it++)->first.c_str());
+ }
+ return NULL;
+}
+
+static char *readline_obj_generator(const char *text, int state)
+{
+ static std::vector<char*> obj_names;
+ static size_t idx;
+
+ if (!state)
+ {
+ idx = 0;
+ obj_names.clear();
+
+ RTLIL::Design *design = yosys_get_design();
+ int len = strlen(text);
+
+ if (design->selected_active_module.empty())
+ {
+ for (auto &it : design->modules_)
+ if (RTLIL::unescape_id(it.first).substr(0, len) == text)
+ obj_names.push_back(strdup(RTLIL::id2cstr(it.first)));
+ }
+ else
+ if (design->modules_.count(design->selected_active_module) > 0)
+ {
+ RTLIL::Module *module = design->modules_.at(design->selected_active_module);
+
+ for (auto &it : module->wires_)
+ if (RTLIL::unescape_id(it.first).substr(0, len) == text)
+ obj_names.push_back(strdup(RTLIL::id2cstr(it.first)));
+
+ for (auto &it : module->memories)
+ if (RTLIL::unescape_id(it.first).substr(0, len) == text)
+ obj_names.push_back(strdup(RTLIL::id2cstr(it.first)));
+
+ for (auto &it : module->cells_)
+ if (RTLIL::unescape_id(it.first).substr(0, len) == text)
+ obj_names.push_back(strdup(RTLIL::id2cstr(it.first)));
+
+ for (auto &it : module->processes)
+ if (RTLIL::unescape_id(it.first).substr(0, len) == text)
+ obj_names.push_back(strdup(RTLIL::id2cstr(it.first)));
+ }
+
+ std::sort(obj_names.begin(), obj_names.end());
+ }
+
+ if (idx < obj_names.size())
+ return strdup(obj_names[idx++]);
+
+ idx = 0;
+ obj_names.clear();
+ return NULL;
+}
+
+static char **readline_completion(const char *text, int start, int)
+{
+ if (start == 0)
+ return rl_completion_matches(text, readline_cmd_generator);
+ if (strncmp(rl_line_buffer, "read_", 5) && strncmp(rl_line_buffer, "write_", 6))
+ return rl_completion_matches(text, readline_obj_generator);
+ return NULL;
+}
+#endif
+
+void shell(RTLIL::Design *design)
+{
+ static int recursion_counter = 0;
+
+ recursion_counter++;
+ log_cmd_error_throw = true;
+
+#ifdef YOSYS_ENABLE_READLINE
+ rl_readline_name = "yosys";
+ rl_attempted_completion_function = readline_completion;
+ rl_basic_word_break_characters = " \t\n";
+#endif
+
+ char *command = NULL;
+#ifdef YOSYS_ENABLE_READLINE
+ while ((command = readline(create_prompt(design, recursion_counter))) != NULL)
+ {
+#else
+ char command_buffer[4096];
+ while (1)
+ {
+ fputs(create_prompt(design, recursion_counter), stdout);
+ fflush(stdout);
+ if ((command = fgets(command_buffer, 4096, stdin)) == NULL)
+ break;
+#endif
+ if (command[strspn(command, " \t\r\n")] == 0)
+ continue;
+#ifdef YOSYS_ENABLE_READLINE
+ add_history(command);
+#endif
+
+ char *p = command + strspn(command, " \t\r\n");
+ if (!strncmp(p, "exit", 4)) {
+ p += 4;
+ p += strspn(p, " \t\r\n");
+ if (*p == 0)
+ break;
+ }
+
+ try {
+ log_assert(design->selection_stack.size() == 1);
+ Pass::call(design, command);
+ } catch (log_cmd_error_exception) {
+ while (design->selection_stack.size() > 1)
+ design->selection_stack.pop_back();
+ log_reset_stack();
+ }
+ }
+ if (command == NULL)
+ printf("exit\n");
+
+ recursion_counter--;
+ log_cmd_error_throw = false;
+}
+
+struct ShellPass : public Pass {
+ ShellPass() : Pass("shell", "enter interactive command mode") { }
+ virtual void help() {
+ log("\n");
+ log(" shell\n");
+ log("\n");
+ log("This command enters the interactive command mode. This can be useful\n");
+ log("in a script to interrupt the script at a certain point and allow for\n");
+ log("interactive inspection or manual synthesis of the design at this point.\n");
+ log("\n");
+ log("The command prompt of the interactive shell indicates the current\n");
+ log("selection (see 'help select'):\n");
+ log("\n");
+ log(" yosys>\n");
+ log(" the entire design is selected\n");
+ log("\n");
+ log(" yosys*>\n");
+ log(" only part of the design is selected\n");
+ log("\n");
+ log(" yosys [modname]>\n");
+ log(" the entire module 'modname' is selected using 'select -module modname'\n");
+ log("\n");
+ log(" yosys [modname]*>\n");
+ log(" only part of current module 'modname' is selected\n");
+ log("\n");
+ log("When in interactive shell, some errors (e.g. invalid command arguments)\n");
+ log("do not terminate yosys but return to the command prompt.\n");
+ log("\n");
+ log("This command is the default action if nothing else has been specified\n");
+ log("on the command line.\n");
+ log("\n");
+ log("Press Ctrl-D or type 'exit' to leave the interactive shell.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
+ extra_args(args, 1, design, false);
+ shell(design);
+ }
+} ShellPass;
+
+#ifdef YOSYS_ENABLE_READLINE
+struct HistoryPass : public Pass {
+ HistoryPass() : Pass("history", "show last interactive commands") { }
+ virtual void help() {
+ log("\n");
+ log(" history\n");
+ log("\n");
+ log("This command prints all commands in the shell history buffer. This are\n");
+ log("all commands executed in an interactive session, but not the commands\n");
+ log("from executed scripts.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
+ extra_args(args, 1, design, false);
+ for(HIST_ENTRY **list = history_list(); *list != NULL; list++)
+ log("%s\n", (*list)->line);
+ }
+} HistoryPass;
+#endif
+
+struct ScriptCmdPass : public Pass {
+ ScriptCmdPass() : Pass("script", "execute commands from script file") { }
+ virtual void help() {
+ log("\n");
+ log(" script <filename> [<from_label>:<to_label>]\n");
+ log("\n");
+ log("This command executes the yosys commands in the specified file.\n");
+ log("\n");
+ log("The 2nd argument can be used to only execute the section of the\n");
+ log("file between the specified labels. An empty from label is synonymous\n");
+ log("for the beginning of the file and an empty to label is synonymous\n");
+ log("for the end of the file.\n");
+ log("\n");
+ log("If only one label is specified (without ':') then only the block\n");
+ log("marked with that label (until the next label) is executed.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
+ if (args.size() < 2)
+ log_cmd_error("Missing script file.\n");
+ else if (args.size() == 2)
+ run_frontend(args[1], "script", design);
+ else if (args.size() == 3)
+ run_frontend(args[1], "script", NULL, &args[2], design);
+ else
+ extra_args(args, 2, design, false);
+ }
+} ScriptCmdPass;
+
+YOSYS_NAMESPACE_END
diff --git a/kernel/yosys.h b/kernel/yosys.h
new file mode 100644
index 00000000..ae73146b
--- /dev/null
+++ b/kernel/yosys.h
@@ -0,0 +1,311 @@
+/*
+ * 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.
+ *
+ */
+
+
+// *** NOTE TO THE READER ***
+//
+// Maybe you have just opened this file in the hope to learn more about the
+// Yosys API. Let me congratulate you on this great decision! ;)
+//
+// If you want to know how the design is represented by Yosys in the memory,
+// you should read "kernel/rtlil.h".
+//
+// If you want to know how to register a command with Yosys, you could read
+// "kernel/register.h", but it would be easier to just look at a simple
+// example instead. A simple one would be "passes/cmds/log.cc".
+//
+// This header is very boring. It just defines some general things that
+// belong nowhere else and includes the interesting headers.
+//
+// Find more information in the "CodingReadme" file.
+
+
+#ifndef YOSYS_H
+#define YOSYS_H
+
+#include <map>
+#include <set>
+#include <tuple>
+#include <vector>
+#include <string>
+#include <algorithm>
+#include <functional>
+#include <unordered_map>
+#include <unordered_set>
+#include <initializer_list>
+#include <stdexcept>
+#include <memory>
+#include <cmath>
+
+#include <sstream>
+#include <fstream>
+#include <istream>
+#include <ostream>
+#include <iostream>
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <limits.h>
+
+#ifndef _YOSYS_
+# error It looks like you are trying to build Yosys without the config defines set. \
+ When building Yosys with a custom make system, make sure you set all the \
+ defines the Yosys Makefile would set for your build configuration.
+#endif
+
+#ifdef YOSYS_ENABLE_TCL
+# include <tcl.h>
+#endif
+
+#ifdef _WIN32
+# undef NOMINMAX
+# define NOMINMAX 1
+# undef YY_NO_UNISTD_H
+# define YY_NO_UNISTD_H 1
+
+# include <windows.h>
+# include <io.h>
+# include <direct.h>
+
+# define strtok_r strtok_s
+# define strdup _strdup
+# define snprintf _snprintf
+# define getcwd _getcwd
+# define mkdir _mkdir
+# define popen _popen
+# define pclose _pclose
+
+# ifndef __MINGW32__
+# define PATH_MAX MAX_PATH
+# define isatty _isatty
+# define fileno _fileno
+# endif
+#endif
+
+#ifndef PATH_MAX
+# define PATH_MAX 4096
+#endif
+
+#define PRIVATE_NAMESPACE_BEGIN namespace {
+#define PRIVATE_NAMESPACE_END }
+#define YOSYS_NAMESPACE_BEGIN namespace Yosys {
+#define YOSYS_NAMESPACE_END }
+#define YOSYS_NAMESPACE_PREFIX Yosys::
+#define USING_YOSYS_NAMESPACE using namespace Yosys;
+
+#if __cplusplus >= 201103L
+# define YS_OVERRIDE override
+# define YS_FINAL final
+#else
+# define YS_OVERRIDE
+# define YS_FINAL
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+# define YS_ATTRIBUTE(...) __attribute__((__VA_ARGS__))
+# define YS_NORETURN
+#elif defined(_MSC_VER)
+# define YS_ATTRIBUTE(...)
+# define YS_NORETURN __declspec(noreturn)
+#else
+# define YS_ATTRIBUTE(...)
+# define YS_NORETURN
+#endif
+
+YOSYS_NAMESPACE_BEGIN
+
+// Note: All headers included in hashlib.h must be included
+// outside of YOSYS_NAMESPACE before this or bad things will happen.
+#ifdef HASHLIB_H
+# undef HASHLIB_H
+# include "kernel/hashlib.h"
+#else
+# include "kernel/hashlib.h"
+# undef HASHLIB_H
+#endif
+
+using std::vector;
+using std::string;
+using std::tuple;
+using std::pair;
+
+using std::make_tuple;
+using std::make_pair;
+using std::min;
+using std::max;
+
+// A primitive shared string implementation that does not
+// move its .c_str() when the object is copied or moved.
+struct shared_str {
+ std::shared_ptr<string> content;
+ shared_str() { }
+ shared_str(string s) { content = std::shared_ptr<string>(new string(s)); }
+ shared_str(const char *s) { content = std::shared_ptr<string>(new string(s)); }
+ const char *c_str() const { return content->c_str(); }
+ const string &str() const { return *content; }
+ bool operator==(const shared_str &other) const { return *content == *other.content; }
+ unsigned int hash() const { return hashlib::hash_ops<std::string>::hash(*content); }
+};
+
+using hashlib::mkhash;
+using hashlib::mkhash_init;
+using hashlib::mkhash_add;
+using hashlib::mkhash_xorshift;
+using hashlib::hash_ops;
+using hashlib::hash_cstr_ops;
+using hashlib::hash_ptr_ops;
+using hashlib::hash_obj_ops;
+using hashlib::dict;
+using hashlib::idict;
+using hashlib::pool;
+using hashlib::mfp;
+
+namespace RTLIL {
+ struct IdString;
+ struct Const;
+ struct SigBit;
+ struct SigSpec;
+ struct Wire;
+ struct Cell;
+ struct Module;
+ struct Design;
+ struct Monitor;
+}
+
+namespace AST {
+ struct AstNode;
+}
+
+using RTLIL::IdString;
+using RTLIL::Const;
+using RTLIL::SigBit;
+using RTLIL::SigSpec;
+using RTLIL::Wire;
+using RTLIL::Cell;
+using RTLIL::Module;
+using RTLIL::Design;
+
+namespace hashlib {
+ template<> struct hash_ops<RTLIL::Wire*> : hash_obj_ops {};
+ template<> struct hash_ops<RTLIL::Cell*> : hash_obj_ops {};
+ template<> struct hash_ops<RTLIL::Module*> : hash_obj_ops {};
+ template<> struct hash_ops<RTLIL::Design*> : hash_obj_ops {};
+ template<> struct hash_ops<RTLIL::Monitor*> : hash_obj_ops {};
+ template<> struct hash_ops<AST::AstNode*> : hash_obj_ops {};
+
+ template<> struct hash_ops<const RTLIL::Wire*> : hash_obj_ops {};
+ template<> struct hash_ops<const RTLIL::Cell*> : hash_obj_ops {};
+ template<> struct hash_ops<const RTLIL::Module*> : hash_obj_ops {};
+ template<> struct hash_ops<const RTLIL::Design*> : hash_obj_ops {};
+ template<> struct hash_ops<const RTLIL::Monitor*> : hash_obj_ops {};
+ template<> struct hash_ops<const AST::AstNode*> : hash_obj_ops {};
+}
+
+void memhasher_on();
+void memhasher_off();
+void memhasher_do();
+
+extern bool memhasher_active;
+inline void memhasher() { if (memhasher_active) memhasher_do(); }
+
+void yosys_banner();
+int ceil_log2(int x);
+std::string stringf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 1, 2));
+std::string vstringf(const char *fmt, va_list ap);
+int readsome(std::istream &f, char *s, int n);
+std::string next_token(std::string &text, const char *sep = " \t\r\n", bool long_strings = false);
+std::vector<std::string> split_tokens(const std::string &text, const char *sep = " \t\r\n");
+bool patmatch(const char *pattern, const char *string);
+int run_command(const std::string &command, std::function<void(const std::string&)> process_line = std::function<void(const std::string&)>());
+std::string make_temp_file(std::string template_str = "/tmp/yosys_XXXXXX");
+std::string make_temp_dir(std::string template_str = "/tmp/yosys_XXXXXX");
+bool check_file_exists(std::string filename, bool is_exec = false);
+bool is_absolute_path(std::string filename);
+void remove_directory(std::string dirname);
+
+template<typename T> int GetSize(const T &obj) { return obj.size(); }
+int GetSize(RTLIL::Wire *wire);
+
+extern int autoidx;
+extern int yosys_xtrace;
+
+YOSYS_NAMESPACE_END
+
+#include "kernel/log.h"
+#include "kernel/rtlil.h"
+#include "kernel/register.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+using RTLIL::State;
+using RTLIL::SigChunk;
+using RTLIL::SigSig;
+
+namespace hashlib {
+ template<> struct hash_ops<RTLIL::State> : hash_ops<int> {};
+}
+
+void yosys_setup();
+void yosys_shutdown();
+
+#ifdef YOSYS_ENABLE_TCL
+Tcl_Interp *yosys_get_tcl_interp();
+#endif
+
+extern RTLIL::Design *yosys_design;
+
+RTLIL::IdString new_id(std::string file, int line, std::string func);
+
+#define NEW_ID \
+ YOSYS_NAMESPACE_PREFIX new_id(__FILE__, __LINE__, __FUNCTION__)
+
+#define ID(_str) \
+ ([]() { static YOSYS_NAMESPACE_PREFIX RTLIL::IdString _id(_str); return _id; })()
+
+RTLIL::Design *yosys_get_design();
+std::string proc_self_dirname();
+std::string proc_share_dirname();
+const char *create_prompt(RTLIL::Design *design, int recursion_counter);
+std::vector<std::string> glob_filename(const std::string &filename_pattern);
+void rewrite_filename(std::string &filename);
+
+void run_pass(std::string command, RTLIL::Design *design = nullptr);
+void run_frontend(std::string filename, std::string command, std::string *backend_command, std::string *from_to_label = nullptr, RTLIL::Design *design = nullptr);
+void run_frontend(std::string filename, std::string command, RTLIL::Design *design = nullptr);
+void run_backend(std::string filename, std::string command, RTLIL::Design *design = nullptr);
+void shell(RTLIL::Design *design);
+
+// from kernel/version_*.o (cc source generated from Makefile)
+extern const char *yosys_version_str;
+
+// from passes/cmds/design.cc
+extern std::map<std::string, RTLIL::Design*> saved_designs;
+extern std::vector<RTLIL::Design*> pushed_designs;
+
+// from passes/cmds/pluginc.cc
+extern std::map<std::string, void*> loaded_plugins;
+extern std::map<std::string, std::string> loaded_plugin_aliases;
+void load_plugin(std::string filename, std::vector<std::string> aliases);
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/libs/bigint/.gitignore b/libs/bigint/.gitignore
new file mode 100644
index 00000000..4467edcf
--- /dev/null
+++ b/libs/bigint/.gitignore
@@ -0,0 +1,6 @@
+*.o
+sample
+testsuite
+testsuite.expected
+testsuite.out
+testsuite.err
diff --git a/libs/bigint/BigInteger.cc b/libs/bigint/BigInteger.cc
new file mode 100644
index 00000000..3b23aa1e
--- /dev/null
+++ b/libs/bigint/BigInteger.cc
@@ -0,0 +1,405 @@
+#include "BigInteger.hh"
+
+void BigInteger::operator =(const BigInteger &x) {
+ // Calls like a = a have no effect
+ if (this == &x)
+ return;
+ // Copy sign
+ sign = x.sign;
+ // Copy the rest
+ mag = x.mag;
+}
+
+BigInteger::BigInteger(const Blk *b, Index blen, Sign s) : mag(b, blen) {
+ switch (s) {
+ case zero:
+ if (!mag.isZero())
+ throw "BigInteger::BigInteger(const Blk *, Index, Sign): Cannot use a sign of zero with a nonzero magnitude";
+ sign = zero;
+ break;
+ case positive:
+ case negative:
+ // If the magnitude is zero, force the sign to zero.
+ sign = mag.isZero() ? zero : s;
+ break;
+ default:
+ /* g++ seems to be optimizing out this case on the assumption
+ * that the sign is a valid member of the enumeration. Oh well. */
+ throw "BigInteger::BigInteger(const Blk *, Index, Sign): Invalid sign";
+ }
+}
+
+BigInteger::BigInteger(const BigUnsigned &x, Sign s) : mag(x) {
+ switch (s) {
+ case zero:
+ if (!mag.isZero())
+ throw "BigInteger::BigInteger(const BigUnsigned &, Sign): Cannot use a sign of zero with a nonzero magnitude";
+ sign = zero;
+ break;
+ case positive:
+ case negative:
+ // If the magnitude is zero, force the sign to zero.
+ sign = mag.isZero() ? zero : s;
+ break;
+ default:
+ /* g++ seems to be optimizing out this case on the assumption
+ * that the sign is a valid member of the enumeration. Oh well. */
+ throw "BigInteger::BigInteger(const BigUnsigned &, Sign): Invalid sign";
+ }
+}
+
+/* CONSTRUCTION FROM PRIMITIVE INTEGERS
+ * Same idea as in BigUnsigned.cc, except that negative input results in a
+ * negative BigInteger instead of an exception. */
+
+// Done longhand to let us use initialization.
+BigInteger::BigInteger(unsigned long x) : mag(x) { sign = mag.isZero() ? zero : positive; }
+BigInteger::BigInteger(unsigned int x) : mag(x) { sign = mag.isZero() ? zero : positive; }
+BigInteger::BigInteger(unsigned short x) : mag(x) { sign = mag.isZero() ? zero : positive; }
+
+// For signed input, determine the desired magnitude and sign separately.
+
+namespace {
+ template <class X, class UX>
+ BigInteger::Blk magOf(X x) {
+ /* UX(...) cast needed to stop short(-2^15), which negates to
+ * itself, from sign-extending in the conversion to Blk. */
+ return BigInteger::Blk(x < 0 ? UX(-x) : x);
+ }
+ template <class X>
+ BigInteger::Sign signOf(X x) {
+ return (x == 0) ? BigInteger::zero
+ : (x > 0) ? BigInteger::positive
+ : BigInteger::negative;
+ }
+}
+
+BigInteger::BigInteger(long x) : sign(signOf(x)), mag(magOf<long , unsigned long >(x)) {}
+BigInteger::BigInteger(int x) : sign(signOf(x)), mag(magOf<int , unsigned int >(x)) {}
+BigInteger::BigInteger(short x) : sign(signOf(x)), mag(magOf<short, unsigned short>(x)) {}
+
+// CONVERSION TO PRIMITIVE INTEGERS
+
+/* Reuse BigUnsigned's conversion to an unsigned primitive integer.
+ * The friend is a separate function rather than
+ * BigInteger::convertToUnsignedPrimitive to avoid requiring BigUnsigned to
+ * declare BigInteger. */
+template <class X>
+inline X convertBigUnsignedToPrimitiveAccess(const BigUnsigned &a) {
+ return a.convertToPrimitive<X>();
+}
+
+template <class X>
+X BigInteger::convertToUnsignedPrimitive() const {
+ if (sign == negative)
+ throw "BigInteger::to<Primitive>: "
+ "Cannot convert a negative integer to an unsigned type";
+ else
+ return convertBigUnsignedToPrimitiveAccess<X>(mag);
+}
+
+/* Similar to BigUnsigned::convertToPrimitive, but split into two cases for
+ * nonnegative and negative numbers. */
+template <class X, class UX>
+X BigInteger::convertToSignedPrimitive() const {
+ if (sign == zero)
+ return 0;
+ else if (mag.getLength() == 1) {
+ // The single block might fit in an X. Try the conversion.
+ Blk b = mag.getBlock(0);
+ if (sign == positive) {
+ X x = X(b);
+ if (x >= 0 && Blk(x) == b)
+ return x;
+ } else {
+ X x = -X(b);
+ /* UX(...) needed to avoid rejecting conversion of
+ * -2^15 to a short. */
+ if (x < 0 && Blk(UX(-x)) == b)
+ return x;
+ }
+ // Otherwise fall through.
+ }
+ throw "BigInteger::to<Primitive>: "
+ "Value is too big to fit in the requested type";
+}
+
+unsigned long BigInteger::toUnsignedLong () const { return convertToUnsignedPrimitive<unsigned long > (); }
+unsigned int BigInteger::toUnsignedInt () const { return convertToUnsignedPrimitive<unsigned int > (); }
+unsigned short BigInteger::toUnsignedShort() const { return convertToUnsignedPrimitive<unsigned short> (); }
+long BigInteger::toLong () const { return convertToSignedPrimitive <long , unsigned long> (); }
+int BigInteger::toInt () const { return convertToSignedPrimitive <int , unsigned int> (); }
+short BigInteger::toShort () const { return convertToSignedPrimitive <short, unsigned short>(); }
+
+// COMPARISON
+BigInteger::CmpRes BigInteger::compareTo(const BigInteger &x) const {
+ // A greater sign implies a greater number
+ if (sign < x.sign)
+ return less;
+ else if (sign > x.sign)
+ return greater;
+ else switch (sign) {
+ // If the signs are the same...
+ case zero:
+ return equal; // Two zeros are equal
+ case positive:
+ // Compare the magnitudes
+ return mag.compareTo(x.mag);
+ case negative:
+ // Compare the magnitudes, but return the opposite result
+ return CmpRes(-mag.compareTo(x.mag));
+ default:
+ throw "BigInteger internal error";
+ }
+}
+
+/* COPY-LESS OPERATIONS
+ * These do some messing around to determine the sign of the result,
+ * then call one of BigUnsigned's copy-less operations. */
+
+// See remarks about aliased calls in BigUnsigned.cc .
+#define DTRT_ALIASED(cond, op) \
+ if (cond) { \
+ BigInteger tmpThis; \
+ tmpThis.op; \
+ *this = tmpThis; \
+ return; \
+ }
+
+void BigInteger::add(const BigInteger &a, const BigInteger &b) {
+ DTRT_ALIASED(this == &a || this == &b, add(a, b));
+ // If one argument is zero, copy the other.
+ if (a.sign == zero)
+ operator =(b);
+ else if (b.sign == zero)
+ operator =(a);
+ // If the arguments have the same sign, take the
+ // common sign and add their magnitudes.
+ else if (a.sign == b.sign) {
+ sign = a.sign;
+ mag.add(a.mag, b.mag);
+ } else {
+ // Otherwise, their magnitudes must be compared.
+ switch (a.mag.compareTo(b.mag)) {
+ case equal:
+ // If their magnitudes are the same, copy zero.
+ mag = 0;
+ sign = zero;
+ break;
+ // Otherwise, take the sign of the greater, and subtract
+ // the lesser magnitude from the greater magnitude.
+ case greater:
+ sign = a.sign;
+ mag.subtract(a.mag, b.mag);
+ break;
+ case less:
+ sign = b.sign;
+ mag.subtract(b.mag, a.mag);
+ break;
+ }
+ }
+}
+
+void BigInteger::subtract(const BigInteger &a, const BigInteger &b) {
+ // Notice that this routine is identical to BigInteger::add,
+ // if one replaces b.sign by its opposite.
+ DTRT_ALIASED(this == &a || this == &b, subtract(a, b));
+ // If a is zero, copy b and flip its sign. If b is zero, copy a.
+ if (a.sign == zero) {
+ mag = b.mag;
+ // Take the negative of _b_'s, sign, not ours.
+ // Bug pointed out by Sam Larkin on 2005.03.30.
+ sign = Sign(-b.sign);
+ } else if (b.sign == zero)
+ operator =(a);
+ // If their signs differ, take a.sign and add the magnitudes.
+ else if (a.sign != b.sign) {
+ sign = a.sign;
+ mag.add(a.mag, b.mag);
+ } else {
+ // Otherwise, their magnitudes must be compared.
+ switch (a.mag.compareTo(b.mag)) {
+ // If their magnitudes are the same, copy zero.
+ case equal:
+ mag = 0;
+ sign = zero;
+ break;
+ // If a's magnitude is greater, take a.sign and
+ // subtract a from b.
+ case greater:
+ sign = a.sign;
+ mag.subtract(a.mag, b.mag);
+ break;
+ // If b's magnitude is greater, take the opposite
+ // of b.sign and subtract b from a.
+ case less:
+ sign = Sign(-b.sign);
+ mag.subtract(b.mag, a.mag);
+ break;
+ }
+ }
+}
+
+void BigInteger::multiply(const BigInteger &a, const BigInteger &b) {
+ DTRT_ALIASED(this == &a || this == &b, multiply(a, b));
+ // If one object is zero, copy zero and return.
+ if (a.sign == zero || b.sign == zero) {
+ sign = zero;
+ mag = 0;
+ return;
+ }
+ // If the signs of the arguments are the same, the result
+ // is positive, otherwise it is negative.
+ sign = (a.sign == b.sign) ? positive : negative;
+ // Multiply the magnitudes.
+ mag.multiply(a.mag, b.mag);
+}
+
+/*
+ * DIVISION WITH REMAINDER
+ * Please read the comments before the definition of
+ * `BigUnsigned::divideWithRemainder' in `BigUnsigned.cc' for lots of
+ * information you should know before reading this function.
+ *
+ * Following Knuth, I decree that x / y is to be
+ * 0 if y==0 and floor(real-number x / y) if y!=0.
+ * Then x % y shall be x - y*(integer x / y).
+ *
+ * Note that x = y * (x / y) + (x % y) always holds.
+ * In addition, (x % y) is from 0 to y - 1 if y > 0,
+ * and from -(|y| - 1) to 0 if y < 0. (x % y) = x if y = 0.
+ *
+ * Examples: (q = a / b, r = a % b)
+ * a b q r
+ * === === === ===
+ * 4 3 1 1
+ * -4 3 -2 2
+ * 4 -3 -2 -2
+ * -4 -3 1 -1
+ */
+void BigInteger::divideWithRemainder(const BigInteger &b, BigInteger &q) {
+ // Defend against aliased calls;
+ // same idea as in BigUnsigned::divideWithRemainder .
+ if (this == &q)
+ throw "BigInteger::divideWithRemainder: Cannot write quotient and remainder into the same variable";
+ if (this == &b || &q == &b) {
+ BigInteger tmpB(b);
+ divideWithRemainder(tmpB, q);
+ return;
+ }
+
+ // Division by zero gives quotient 0 and remainder *this
+ if (b.sign == zero) {
+ q.mag = 0;
+ q.sign = zero;
+ return;
+ }
+ // 0 / b gives quotient 0 and remainder 0
+ if (sign == zero) {
+ q.mag = 0;
+ q.sign = zero;
+ return;
+ }
+
+ // Here *this != 0, b != 0.
+
+ // Do the operands have the same sign?
+ if (sign == b.sign) {
+ // Yes: easy case. Quotient is zero or positive.
+ q.sign = positive;
+ } else {
+ // No: harder case. Quotient is negative.
+ q.sign = negative;
+ // Decrease the magnitude of the dividend by one.
+ mag--;
+ /*
+ * We tinker with the dividend before and with the
+ * quotient and remainder after so that the result
+ * comes out right. To see why it works, consider the following
+ * list of examples, where A is the magnitude-decreased
+ * a, Q and R are the results of BigUnsigned division
+ * with remainder on A and |b|, and q and r are the
+ * final results we want:
+ *
+ * a A b Q R q r
+ * -3 -2 3 0 2 -1 0
+ * -4 -3 3 1 0 -2 2
+ * -5 -4 3 1 1 -2 1
+ * -6 -5 3 1 2 -2 0
+ *
+ * It appears that we need a total of 3 corrections:
+ * Decrease the magnitude of a to get A. Increase the
+ * magnitude of Q to get q (and make it negative).
+ * Find r = (b - 1) - R and give it the desired sign.
+ */
+ }
+
+ // Divide the magnitudes.
+ mag.divideWithRemainder(b.mag, q.mag);
+
+ if (sign != b.sign) {
+ // More for the harder case (as described):
+ // Increase the magnitude of the quotient by one.
+ q.mag++;
+ // Modify the remainder.
+ mag.subtract(b.mag, mag);
+ mag--;
+ }
+
+ // Sign of the remainder is always the sign of the divisor b.
+ sign = b.sign;
+
+ // Set signs to zero as necessary. (Thanks David Allen!)
+ if (mag.isZero())
+ sign = zero;
+ if (q.mag.isZero())
+ q.sign = zero;
+
+ // WHEW!!!
+}
+
+// Negation
+void BigInteger::negate(const BigInteger &a) {
+ DTRT_ALIASED(this == &a, negate(a));
+ // Copy a's magnitude
+ mag = a.mag;
+ // Copy the opposite of a.sign
+ sign = Sign(-a.sign);
+}
+
+// INCREMENT/DECREMENT OPERATORS
+
+// Prefix increment
+void BigInteger::operator ++() {
+ if (sign == negative) {
+ mag--;
+ if (mag == 0)
+ sign = zero;
+ } else {
+ mag++;
+ sign = positive; // if not already
+ }
+}
+
+// Postfix increment: same as prefix
+void BigInteger::operator ++(int) {
+ operator ++();
+}
+
+// Prefix decrement
+void BigInteger::operator --() {
+ if (sign == positive) {
+ mag--;
+ if (mag == 0)
+ sign = zero;
+ } else {
+ mag++;
+ sign = negative;
+ }
+}
+
+// Postfix decrement: same as prefix
+void BigInteger::operator --(int) {
+ operator --();
+}
+
diff --git a/libs/bigint/BigInteger.hh b/libs/bigint/BigInteger.hh
new file mode 100644
index 00000000..cf6e9105
--- /dev/null
+++ b/libs/bigint/BigInteger.hh
@@ -0,0 +1,215 @@
+#ifndef BIGINTEGER_H
+#define BIGINTEGER_H
+
+#include "BigUnsigned.hh"
+
+/* A BigInteger object represents a signed integer of size limited only by
+ * available memory. BigUnsigneds support most mathematical operators and can
+ * be converted to and from most primitive integer types.
+ *
+ * A BigInteger is just an aggregate of a BigUnsigned and a sign. (It is no
+ * longer derived from BigUnsigned because that led to harmful implicit
+ * conversions.) */
+class BigInteger {
+
+public:
+ typedef BigUnsigned::Blk Blk;
+ typedef BigUnsigned::Index Index;
+ typedef BigUnsigned::CmpRes CmpRes;
+ static const CmpRes
+ less = BigUnsigned::less ,
+ equal = BigUnsigned::equal ,
+ greater = BigUnsigned::greater;
+ // Enumeration for the sign of a BigInteger.
+ enum Sign { negative = -1, zero = 0, positive = 1 };
+
+protected:
+ Sign sign;
+ BigUnsigned mag;
+
+public:
+ // Constructs zero.
+ BigInteger() : sign(zero), mag() {}
+
+ // Copy constructor
+ BigInteger(const BigInteger &x) : sign(x.sign), mag(x.mag) {};
+
+ // Assignment operator
+ void operator=(const BigInteger &x);
+
+ // Constructor that copies from a given array of blocks with a sign.
+ BigInteger(const Blk *b, Index blen, Sign s);
+
+ // Nonnegative constructor that copies from a given array of blocks.
+ BigInteger(const Blk *b, Index blen) : mag(b, blen) {
+ sign = mag.isZero() ? zero : positive;
+ }
+
+ // Constructor from a BigUnsigned and a sign
+ BigInteger(const BigUnsigned &x, Sign s);
+
+ // Nonnegative constructor from a BigUnsigned
+ BigInteger(const BigUnsigned &x) : mag(x) {
+ sign = mag.isZero() ? zero : positive;
+ }
+
+ // Constructors from primitive integer types
+ BigInteger(unsigned long x);
+ BigInteger( long x);
+ BigInteger(unsigned int x);
+ BigInteger( int x);
+ BigInteger(unsigned short x);
+ BigInteger( short x);
+
+ /* Converters to primitive integer types
+ * The implicit conversion operators caused trouble, so these are now
+ * named. */
+ unsigned long toUnsignedLong () const;
+ long toLong () const;
+ unsigned int toUnsignedInt () const;
+ int toInt () const;
+ unsigned short toUnsignedShort() const;
+ short toShort () const;
+protected:
+ // Helper
+ template <class X> X convertToUnsignedPrimitive() const;
+ template <class X, class UX> X convertToSignedPrimitive() const;
+public:
+
+ // ACCESSORS
+ Sign getSign() const { return sign; }
+ /* The client can't do any harm by holding a read-only reference to the
+ * magnitude. */
+ const BigUnsigned &getMagnitude() const { return mag; }
+
+ // Some accessors that go through to the magnitude
+ Index getLength() const { return mag.getLength(); }
+ Index getCapacity() const { return mag.getCapacity(); }
+ Blk getBlock(Index i) const { return mag.getBlock(i); }
+ bool isZero() const { return sign == zero; } // A bit special
+
+ // COMPARISONS
+
+ // Compares this to x like Perl's <=>
+ CmpRes compareTo(const BigInteger &x) const;
+
+ // Ordinary comparison operators
+ bool operator ==(const BigInteger &x) const {
+ return sign == x.sign && mag == x.mag;
+ }
+ bool operator !=(const BigInteger &x) const { return !operator ==(x); };
+ bool operator < (const BigInteger &x) const { return compareTo(x) == less ; }
+ bool operator <=(const BigInteger &x) const { return compareTo(x) != greater; }
+ bool operator >=(const BigInteger &x) const { return compareTo(x) != less ; }
+ bool operator > (const BigInteger &x) const { return compareTo(x) == greater; }
+
+ // OPERATORS -- See the discussion in BigUnsigned.hh.
+ void add (const BigInteger &a, const BigInteger &b);
+ void subtract(const BigInteger &a, const BigInteger &b);
+ void multiply(const BigInteger &a, const BigInteger &b);
+ /* See the comment on BigUnsigned::divideWithRemainder. Semantics
+ * differ from those of primitive integers when negatives and/or zeros
+ * are involved. */
+ void divideWithRemainder(const BigInteger &b, BigInteger &q);
+ void negate(const BigInteger &a);
+
+ /* Bitwise operators are not provided for BigIntegers. Use
+ * getMagnitude to get the magnitude and operate on that instead. */
+
+ BigInteger operator +(const BigInteger &x) const;
+ BigInteger operator -(const BigInteger &x) const;
+ BigInteger operator *(const BigInteger &x) const;
+ BigInteger operator /(const BigInteger &x) const;
+ BigInteger operator %(const BigInteger &x) const;
+ BigInteger operator -() const;
+
+ void operator +=(const BigInteger &x);
+ void operator -=(const BigInteger &x);
+ void operator *=(const BigInteger &x);
+ void operator /=(const BigInteger &x);
+ void operator %=(const BigInteger &x);
+ void flipSign();
+
+ // INCREMENT/DECREMENT OPERATORS
+ void operator ++( );
+ void operator ++(int);
+ void operator --( );
+ void operator --(int);
+};
+
+// NORMAL OPERATORS
+/* These create an object to hold the result and invoke
+ * the appropriate put-here operation on it, passing
+ * this and x. The new object is then returned. */
+inline BigInteger BigInteger::operator +(const BigInteger &x) const {
+ BigInteger ans;
+ ans.add(*this, x);
+ return ans;
+}
+inline BigInteger BigInteger::operator -(const BigInteger &x) const {
+ BigInteger ans;
+ ans.subtract(*this, x);
+ return ans;
+}
+inline BigInteger BigInteger::operator *(const BigInteger &x) const {
+ BigInteger ans;
+ ans.multiply(*this, x);
+ return ans;
+}
+inline BigInteger BigInteger::operator /(const BigInteger &x) const {
+ if (x.isZero()) throw "BigInteger::operator /: division by zero";
+ BigInteger q, r;
+ r = *this;
+ r.divideWithRemainder(x, q);
+ return q;
+}
+inline BigInteger BigInteger::operator %(const BigInteger &x) const {
+ if (x.isZero()) throw "BigInteger::operator %: division by zero";
+ BigInteger q, r;
+ r = *this;
+ r.divideWithRemainder(x, q);
+ return r;
+}
+inline BigInteger BigInteger::operator -() const {
+ BigInteger ans;
+ ans.negate(*this);
+ return ans;
+}
+
+/*
+ * ASSIGNMENT OPERATORS
+ *
+ * Now the responsibility for making a temporary copy if necessary
+ * belongs to the put-here operations. See Assignment Operators in
+ * BigUnsigned.hh.
+ */
+inline void BigInteger::operator +=(const BigInteger &x) {
+ add(*this, x);
+}
+inline void BigInteger::operator -=(const BigInteger &x) {
+ subtract(*this, x);
+}
+inline void BigInteger::operator *=(const BigInteger &x) {
+ multiply(*this, x);
+}
+inline void BigInteger::operator /=(const BigInteger &x) {
+ if (x.isZero()) throw "BigInteger::operator /=: division by zero";
+ /* The following technique is slightly faster than copying *this first
+ * when x is large. */
+ BigInteger q;
+ divideWithRemainder(x, q);
+ // *this contains the remainder, but we overwrite it with the quotient.
+ *this = q;
+}
+inline void BigInteger::operator %=(const BigInteger &x) {
+ if (x.isZero()) throw "BigInteger::operator %=: division by zero";
+ BigInteger q;
+ // Mods *this by x. Don't care about quotient left in q.
+ divideWithRemainder(x, q);
+}
+// This one is trivial
+inline void BigInteger::flipSign() {
+ sign = Sign(-sign);
+}
+
+#endif
diff --git a/libs/bigint/BigIntegerAlgorithms.cc b/libs/bigint/BigIntegerAlgorithms.cc
new file mode 100644
index 00000000..7edebda7
--- /dev/null
+++ b/libs/bigint/BigIntegerAlgorithms.cc
@@ -0,0 +1,70 @@
+#include "BigIntegerAlgorithms.hh"
+
+BigUnsigned gcd(BigUnsigned a, BigUnsigned b) {
+ BigUnsigned trash;
+ // Neat in-place alternating technique.
+ for (;;) {
+ if (b.isZero())
+ return a;
+ a.divideWithRemainder(b, trash);
+ if (a.isZero())
+ return b;
+ b.divideWithRemainder(a, trash);
+ }
+}
+
+void extendedEuclidean(BigInteger m, BigInteger n,
+ BigInteger &g, BigInteger &r, BigInteger &s) {
+ if (&g == &r || &g == &s || &r == &s)
+ throw "BigInteger extendedEuclidean: Outputs are aliased";
+ BigInteger r1(1), s1(0), r2(0), s2(1), q;
+ /* Invariants:
+ * r1*m(orig) + s1*n(orig) == m(current)
+ * r2*m(orig) + s2*n(orig) == n(current) */
+ for (;;) {
+ if (n.isZero()) {
+ r = r1; s = s1; g = m;
+ return;
+ }
+ // Subtract q times the second invariant from the first invariant.
+ m.divideWithRemainder(n, q);
+ r1 -= q*r2; s1 -= q*s2;
+
+ if (m.isZero()) {
+ r = r2; s = s2; g = n;
+ return;
+ }
+ // Subtract q times the first invariant from the second invariant.
+ n.divideWithRemainder(m, q);
+ r2 -= q*r1; s2 -= q*s1;
+ }
+}
+
+BigUnsigned modinv(const BigInteger &x, const BigUnsigned &n) {
+ BigInteger g, r, s;
+ extendedEuclidean(x, n, g, r, s);
+ if (g == 1)
+ // r*x + s*n == 1, so r*x === 1 (mod n), so r is the answer.
+ return (r % n).getMagnitude(); // (r % n) will be nonnegative
+ else
+ throw "BigInteger modinv: x and n have a common factor";
+}
+
+BigUnsigned modexp(const BigInteger &base, const BigUnsigned &exponent,
+ const BigUnsigned &modulus) {
+ BigUnsigned ans = 1, base2 = (base % modulus).getMagnitude();
+ BigUnsigned::Index i = exponent.bitLength();
+ // For each bit of the exponent, most to least significant...
+ while (i > 0) {
+ i--;
+ // Square.
+ ans *= ans;
+ ans %= modulus;
+ // And multiply if the bit is a 1.
+ if (exponent.getBit(i)) {
+ ans *= base2;
+ ans %= modulus;
+ }
+ }
+ return ans;
+}
diff --git a/libs/bigint/BigIntegerAlgorithms.hh b/libs/bigint/BigIntegerAlgorithms.hh
new file mode 100644
index 00000000..b1dd9432
--- /dev/null
+++ b/libs/bigint/BigIntegerAlgorithms.hh
@@ -0,0 +1,25 @@
+#ifndef BIGINTEGERALGORITHMS_H
+#define BIGINTEGERALGORITHMS_H
+
+#include "BigInteger.hh"
+
+/* Some mathematical algorithms for big integers.
+ * This code is new and, as such, experimental. */
+
+// Returns the greatest common divisor of a and b.
+BigUnsigned gcd(BigUnsigned a, BigUnsigned b);
+
+/* Extended Euclidean algorithm.
+ * Given m and n, finds gcd g and numbers r, s such that r*m + s*n == g. */
+void extendedEuclidean(BigInteger m, BigInteger n,
+ BigInteger &g, BigInteger &r, BigInteger &s);
+
+/* Returns the multiplicative inverse of x modulo n, or throws an exception if
+ * they have a common factor. */
+BigUnsigned modinv(const BigInteger &x, const BigUnsigned &n);
+
+// Returns (base ^ exponent) % modulus.
+BigUnsigned modexp(const BigInteger &base, const BigUnsigned &exponent,
+ const BigUnsigned &modulus);
+
+#endif
diff --git a/libs/bigint/BigIntegerLibrary.hh b/libs/bigint/BigIntegerLibrary.hh
new file mode 100644
index 00000000..2a0ebee6
--- /dev/null
+++ b/libs/bigint/BigIntegerLibrary.hh
@@ -0,0 +1,8 @@
+// This header file includes all of the library header files.
+
+#include "NumberlikeArray.hh"
+#include "BigUnsigned.hh"
+#include "BigInteger.hh"
+#include "BigIntegerAlgorithms.hh"
+#include "BigUnsignedInABase.hh"
+#include "BigIntegerUtils.hh"
diff --git a/libs/bigint/BigIntegerUtils.cc b/libs/bigint/BigIntegerUtils.cc
new file mode 100644
index 00000000..44073af6
--- /dev/null
+++ b/libs/bigint/BigIntegerUtils.cc
@@ -0,0 +1,50 @@
+#include "BigIntegerUtils.hh"
+#include "BigUnsignedInABase.hh"
+
+std::string bigUnsignedToString(const BigUnsigned &x) {
+ return std::string(BigUnsignedInABase(x, 10));
+}
+
+std::string bigIntegerToString(const BigInteger &x) {
+ return (x.getSign() == BigInteger::negative)
+ ? (std::string("-") + bigUnsignedToString(x.getMagnitude()))
+ : (bigUnsignedToString(x.getMagnitude()));
+}
+
+BigUnsigned stringToBigUnsigned(const std::string &s) {
+ return BigUnsigned(BigUnsignedInABase(s, 10));
+}
+
+BigInteger stringToBigInteger(const std::string &s) {
+ // Recognize a sign followed by a BigUnsigned.
+ return (s[0] == '-') ? BigInteger(stringToBigUnsigned(s.substr(1, s.length() - 1)), BigInteger::negative)
+ : (s[0] == '+') ? BigInteger(stringToBigUnsigned(s.substr(1, s.length() - 1)))
+ : BigInteger(stringToBigUnsigned(s));
+}
+
+std::ostream &operator <<(std::ostream &os, const BigUnsigned &x) {
+ BigUnsignedInABase::Base base;
+ long osFlags = os.flags();
+ if (osFlags & os.dec)
+ base = 10;
+ else if (osFlags & os.hex) {
+ base = 16;
+ if (osFlags & os.showbase)
+ os << "0x";
+ } else if (osFlags & os.oct) {
+ base = 8;
+ if (osFlags & os.showbase)
+ os << '0';
+ } else
+ throw "std::ostream << BigUnsigned: Could not determine the desired base from output-stream flags";
+ std::string s = std::string(BigUnsignedInABase(x, base));
+ os << s;
+ return os;
+}
+
+std::ostream &operator <<(std::ostream &os, const BigInteger &x) {
+ if (x.getSign() == BigInteger::negative)
+ os << '-';
+ os << x.getMagnitude();
+ return os;
+}
diff --git a/libs/bigint/BigIntegerUtils.hh b/libs/bigint/BigIntegerUtils.hh
new file mode 100644
index 00000000..c815b5d7
--- /dev/null
+++ b/libs/bigint/BigIntegerUtils.hh
@@ -0,0 +1,72 @@
+#ifndef BIGINTEGERUTILS_H
+#define BIGINTEGERUTILS_H
+
+#include "BigInteger.hh"
+#include <string>
+#include <iostream>
+
+/* This file provides:
+ * - Convenient std::string <-> BigUnsigned/BigInteger conversion routines
+ * - std::ostream << operators for BigUnsigned/BigInteger */
+
+// std::string conversion routines. Base 10 only.
+std::string bigUnsignedToString(const BigUnsigned &x);
+std::string bigIntegerToString(const BigInteger &x);
+BigUnsigned stringToBigUnsigned(const std::string &s);
+BigInteger stringToBigInteger(const std::string &s);
+
+// Creates a BigInteger from data such as `char's; read below for details.
+template <class T>
+BigInteger dataToBigInteger(const T* data, BigInteger::Index length, BigInteger::Sign sign);
+
+// Outputs x to os, obeying the flags `dec', `hex', `bin', and `showbase'.
+std::ostream &operator <<(std::ostream &os, const BigUnsigned &x);
+
+// Outputs x to os, obeying the flags `dec', `hex', `bin', and `showbase'.
+// My somewhat arbitrary policy: a negative sign comes before a base indicator (like -0xFF).
+std::ostream &operator <<(std::ostream &os, const BigInteger &x);
+
+// BEGIN TEMPLATE DEFINITIONS.
+
+/*
+ * Converts binary data to a BigInteger.
+ * Pass an array `data', its length, and the desired sign.
+ *
+ * Elements of `data' may be of any type `T' that has the following
+ * two properties (this includes almost all integral types):
+ *
+ * (1) `sizeof(T)' correctly gives the amount of binary data in one
+ * value of `T' and is a factor of `sizeof(Blk)'.
+ *
+ * (2) When a value of `T' is casted to a `Blk', the low bytes of
+ * the result contain the desired binary data.
+ */
+template <class T>
+BigInteger dataToBigInteger(const T* data, BigInteger::Index length, BigInteger::Sign sign) {
+ // really ceiling(numBytes / sizeof(BigInteger::Blk))
+ unsigned int pieceSizeInBits = 8 * sizeof(T);
+ unsigned int piecesPerBlock = sizeof(BigInteger::Blk) / sizeof(T);
+ unsigned int numBlocks = (length + piecesPerBlock - 1) / piecesPerBlock;
+
+ // Allocate our block array
+ BigInteger::Blk *blocks = new BigInteger::Blk[numBlocks];
+
+ BigInteger::Index blockNum, pieceNum, pieceNumHere;
+
+ // Convert
+ for (blockNum = 0, pieceNum = 0; blockNum < numBlocks; blockNum++) {
+ BigInteger::Blk curBlock = 0;
+ for (pieceNumHere = 0; pieceNumHere < piecesPerBlock && pieceNum < length;
+ pieceNumHere++, pieceNum++)
+ curBlock |= (BigInteger::Blk(data[pieceNum]) << (pieceSizeInBits * pieceNumHere));
+ blocks[blockNum] = curBlock;
+ }
+
+ // Create the BigInteger.
+ BigInteger x(blocks, numBlocks, sign);
+
+ delete [] blocks;
+ return x;
+}
+
+#endif
diff --git a/libs/bigint/BigUnsigned.cc b/libs/bigint/BigUnsigned.cc
new file mode 100644
index 00000000..d7f9889c
--- /dev/null
+++ b/libs/bigint/BigUnsigned.cc
@@ -0,0 +1,697 @@
+#include "BigUnsigned.hh"
+
+// Memory management definitions have moved to the bottom of NumberlikeArray.hh.
+
+// The templates used by these constructors and converters are at the bottom of
+// BigUnsigned.hh.
+
+BigUnsigned::BigUnsigned(unsigned long x) { initFromPrimitive (x); }
+BigUnsigned::BigUnsigned(unsigned int x) { initFromPrimitive (x); }
+BigUnsigned::BigUnsigned(unsigned short x) { initFromPrimitive (x); }
+BigUnsigned::BigUnsigned( long x) { initFromSignedPrimitive(x); }
+BigUnsigned::BigUnsigned( int x) { initFromSignedPrimitive(x); }
+BigUnsigned::BigUnsigned( short x) { initFromSignedPrimitive(x); }
+
+unsigned long BigUnsigned::toUnsignedLong () const { return convertToPrimitive <unsigned long >(); }
+unsigned int BigUnsigned::toUnsignedInt () const { return convertToPrimitive <unsigned int >(); }
+unsigned short BigUnsigned::toUnsignedShort() const { return convertToPrimitive <unsigned short>(); }
+long BigUnsigned::toLong () const { return convertToSignedPrimitive< long >(); }
+int BigUnsigned::toInt () const { return convertToSignedPrimitive< int >(); }
+short BigUnsigned::toShort () const { return convertToSignedPrimitive< short>(); }
+
+// BIT/BLOCK ACCESSORS
+
+void BigUnsigned::setBlock(Index i, Blk newBlock) {
+ if (newBlock == 0) {
+ if (i < len) {
+ blk[i] = 0;
+ zapLeadingZeros();
+ }
+ // If i >= len, no effect.
+ } else {
+ if (i >= len) {
+ // The nonzero block extends the number.
+ allocateAndCopy(i+1);
+ // Zero any added blocks that we aren't setting.
+ for (Index j = len; j < i; j++)
+ blk[j] = 0;
+ len = i+1;
+ }
+ blk[i] = newBlock;
+ }
+}
+
+/* Evidently the compiler wants BigUnsigned:: on the return type because, at
+ * that point, it hasn't yet parsed the BigUnsigned:: on the name to get the
+ * proper scope. */
+BigUnsigned::Index BigUnsigned::bitLength() const {
+ if (isZero())
+ return 0;
+ else {
+ Blk leftmostBlock = getBlock(len - 1);
+ Index leftmostBlockLen = 0;
+ while (leftmostBlock != 0) {
+ leftmostBlock >>= 1;
+ leftmostBlockLen++;
+ }
+ return leftmostBlockLen + (len - 1) * N;
+ }
+}
+
+void BigUnsigned::setBit(Index bi, bool newBit) {
+ Index blockI = bi / N;
+ Blk block = getBlock(blockI), mask = Blk(1) << (bi % N);
+ block = newBit ? (block | mask) : (block & ~mask);
+ setBlock(blockI, block);
+}
+
+// COMPARISON
+BigUnsigned::CmpRes BigUnsigned::compareTo(const BigUnsigned &x) const {
+ // A bigger length implies a bigger number.
+ if (len < x.len)
+ return less;
+ else if (len > x.len)
+ return greater;
+ else {
+ // Compare blocks one by one from left to right.
+ Index i = len;
+ while (i > 0) {
+ i--;
+ if (blk[i] == x.blk[i])
+ continue;
+ else if (blk[i] > x.blk[i])
+ return greater;
+ else
+ return less;
+ }
+ // If no blocks differed, the numbers are equal.
+ return equal;
+ }
+}
+
+// COPY-LESS OPERATIONS
+
+/*
+ * On most calls to copy-less operations, it's safe to read the inputs little by
+ * little and write the outputs little by little. However, if one of the
+ * inputs is coming from the same variable into which the output is to be
+ * stored (an "aliased" call), we risk overwriting the input before we read it.
+ * In this case, we first compute the result into a temporary BigUnsigned
+ * variable and then copy it into the requested output variable *this.
+ * Each put-here operation uses the DTRT_ALIASED macro (Do The Right Thing on
+ * aliased calls) to generate code for this check.
+ *
+ * I adopted this approach on 2007.02.13 (see Assignment Operators in
+ * BigUnsigned.hh). Before then, put-here operations rejected aliased calls
+ * with an exception. I think doing the right thing is better.
+ *
+ * Some of the put-here operations can probably handle aliased calls safely
+ * without the extra copy because (for example) they process blocks strictly
+ * right-to-left. At some point I might determine which ones don't need the
+ * copy, but my reasoning would need to be verified very carefully. For now
+ * I'll leave in the copy.
+ */
+#define DTRT_ALIASED(cond, op) \
+ if (cond) { \
+ BigUnsigned tmpThis; \
+ tmpThis.op; \
+ *this = tmpThis; \
+ return; \
+ }
+
+
+
+void BigUnsigned::add(const BigUnsigned &a, const BigUnsigned &b) {
+ DTRT_ALIASED(this == &a || this == &b, add(a, b));
+ // If one argument is zero, copy the other.
+ if (a.len == 0) {
+ operator =(b);
+ return;
+ } else if (b.len == 0) {
+ operator =(a);
+ return;
+ }
+ // Some variables...
+ // Carries in and out of an addition stage
+ bool carryIn, carryOut;
+ Blk temp;
+ Index i;
+ // a2 points to the longer input, b2 points to the shorter
+ const BigUnsigned *a2, *b2;
+ if (a.len >= b.len) {
+ a2 = &a;
+ b2 = &b;
+ } else {
+ a2 = &b;
+ b2 = &a;
+ }
+ // Set prelimiary length and make room in this BigUnsigned
+ len = a2->len + 1;
+ allocate(len);
+ // For each block index that is present in both inputs...
+ for (i = 0, carryIn = false; i < b2->len; i++) {
+ // Add input blocks
+ temp = a2->blk[i] + b2->blk[i];
+ // If a rollover occurred, the result is less than either input.
+ // This test is used many times in the BigUnsigned code.
+ carryOut = (temp < a2->blk[i]);
+ // If a carry was input, handle it
+ if (carryIn) {
+ temp++;
+ carryOut |= (temp == 0);
+ }
+ blk[i] = temp; // Save the addition result
+ carryIn = carryOut; // Pass the carry along
+ }
+ // If there is a carry left over, increase blocks until
+ // one does not roll over.
+ for (; i < a2->len && carryIn; i++) {
+ temp = a2->blk[i] + 1;
+ carryIn = (temp == 0);
+ blk[i] = temp;
+ }
+ // If the carry was resolved but the larger number
+ // still has blocks, copy them over.
+ for (; i < a2->len; i++)
+ blk[i] = a2->blk[i];
+ // Set the extra block if there's still a carry, decrease length otherwise
+ if (carryIn)
+ blk[i] = 1;
+ else
+ len--;
+}
+
+void BigUnsigned::subtract(const BigUnsigned &a, const BigUnsigned &b) {
+ DTRT_ALIASED(this == &a || this == &b, subtract(a, b));
+ if (b.len == 0) {
+ // If b is zero, copy a.
+ operator =(a);
+ return;
+ } else if (a.len < b.len)
+ // If a is shorter than b, the result is negative.
+ throw "BigUnsigned::subtract: "
+ "Negative result in unsigned calculation";
+ // Some variables...
+ bool borrowIn, borrowOut;
+ Blk temp;
+ Index i;
+ // Set preliminary length and make room
+ len = a.len;
+ allocate(len);
+ // For each block index that is present in both inputs...
+ for (i = 0, borrowIn = false; i < b.len; i++) {
+ temp = a.blk[i] - b.blk[i];
+ // If a reverse rollover occurred,
+ // the result is greater than the block from a.
+ borrowOut = (temp > a.blk[i]);
+ // Handle an incoming borrow
+ if (borrowIn) {
+ borrowOut |= (temp == 0);
+ temp--;
+ }
+ blk[i] = temp; // Save the subtraction result
+ borrowIn = borrowOut; // Pass the borrow along
+ }
+ // If there is a borrow left over, decrease blocks until
+ // one does not reverse rollover.
+ for (; i < a.len && borrowIn; i++) {
+ borrowIn = (a.blk[i] == 0);
+ blk[i] = a.blk[i] - 1;
+ }
+ /* If there's still a borrow, the result is negative.
+ * Throw an exception, but zero out this object so as to leave it in a
+ * predictable state. */
+ if (borrowIn) {
+ len = 0;
+ throw "BigUnsigned::subtract: Negative result in unsigned calculation";
+ } else
+ // Copy over the rest of the blocks
+ for (; i < a.len; i++)
+ blk[i] = a.blk[i];
+ // Zap leading zeros
+ zapLeadingZeros();
+}
+
+/*
+ * About the multiplication and division algorithms:
+ *
+ * I searched unsucessfully for fast C++ built-in operations like the `b_0'
+ * and `c_0' Knuth describes in Section 4.3.1 of ``The Art of Computer
+ * Programming'' (replace `place' by `Blk'):
+ *
+ * ``b_0[:] multiplication of a one-place integer by another one-place
+ * integer, giving a two-place answer;
+ *
+ * ``c_0[:] division of a two-place integer by a one-place integer,
+ * provided that the quotient is a one-place integer, and yielding
+ * also a one-place remainder.''
+ *
+ * I also missed his note that ``[b]y adjusting the word size, if
+ * necessary, nearly all computers will have these three operations
+ * available'', so I gave up on trying to use algorithms similar to his.
+ * A future version of the library might include such algorithms; I
+ * would welcome contributions from others for this.
+ *
+ * I eventually decided to use bit-shifting algorithms. To multiply `a'
+ * and `b', we zero out the result. Then, for each `1' bit in `a', we
+ * shift `b' left the appropriate amount and add it to the result.
+ * Similarly, to divide `a' by `b', we shift `b' left varying amounts,
+ * repeatedly trying to subtract it from `a'. When we succeed, we note
+ * the fact by setting a bit in the quotient. While these algorithms
+ * have the same O(n^2) time complexity as Knuth's, the ``constant factor''
+ * is likely to be larger.
+ *
+ * Because I used these algorithms, which require single-block addition
+ * and subtraction rather than single-block multiplication and division,
+ * the innermost loops of all four routines are very similar. Study one
+ * of them and all will become clear.
+ */
+
+/*
+ * This is a little inline function used by both the multiplication
+ * routine and the division routine.
+ *
+ * `getShiftedBlock' returns the `x'th block of `num << y'.
+ * `y' may be anything from 0 to N - 1, and `x' may be anything from
+ * 0 to `num.len'.
+ *
+ * Two things contribute to this block:
+ *
+ * (1) The `N - y' low bits of `num.blk[x]', shifted `y' bits left.
+ *
+ * (2) The `y' high bits of `num.blk[x-1]', shifted `N - y' bits right.
+ *
+ * But we must be careful if `x == 0' or `x == num.len', in
+ * which case we should use 0 instead of (2) or (1), respectively.
+ *
+ * If `y == 0', then (2) contributes 0, as it should. However,
+ * in some computer environments, for a reason I cannot understand,
+ * `a >> b' means `a >> (b % N)'. This means `num.blk[x-1] >> (N - y)'
+ * will return `num.blk[x-1]' instead of the desired 0 when `y == 0';
+ * the test `y == 0' handles this case specially.
+ */
+inline BigUnsigned::Blk getShiftedBlock(const BigUnsigned &num,
+ BigUnsigned::Index x, unsigned int y) {
+ BigUnsigned::Blk part1 = (x == 0 || y == 0) ? 0 : (num.blk[x - 1] >> (BigUnsigned::N - y));
+ BigUnsigned::Blk part2 = (x == num.len) ? 0 : (num.blk[x] << y);
+ return part1 | part2;
+}
+
+void BigUnsigned::multiply(const BigUnsigned &a, const BigUnsigned &b) {
+ DTRT_ALIASED(this == &a || this == &b, multiply(a, b));
+ // If either a or b is zero, set to zero.
+ if (a.len == 0 || b.len == 0) {
+ len = 0;
+ return;
+ }
+ /*
+ * Overall method:
+ *
+ * Set this = 0.
+ * For each 1-bit of `a' (say the `i2'th bit of block `i'):
+ * Add `b << (i blocks and i2 bits)' to *this.
+ */
+ // Variables for the calculation
+ Index i, j, k;
+ unsigned int i2;
+ Blk temp;
+ bool carryIn, carryOut;
+ // Set preliminary length and make room
+ len = a.len + b.len;
+ allocate(len);
+ // Zero out this object
+ for (i = 0; i < len; i++)
+ blk[i] = 0;
+ // For each block of the first number...
+ for (i = 0; i < a.len; i++) {
+ // For each 1-bit of that block...
+ for (i2 = 0; i2 < N; i2++) {
+ if ((a.blk[i] & (Blk(1) << i2)) == 0)
+ continue;
+ /*
+ * Add b to this, shifted left i blocks and i2 bits.
+ * j is the index in b, and k = i + j is the index in this.
+ *
+ * `getShiftedBlock', a short inline function defined above,
+ * is now used for the bit handling. It replaces the more
+ * complex `bHigh' code, in which each run of the loop dealt
+ * immediately with the low bits and saved the high bits to
+ * be picked up next time. The last run of the loop used to
+ * leave leftover high bits, which were handled separately.
+ * Instead, this loop runs an additional time with j == b.len.
+ * These changes were made on 2005.01.11.
+ */
+ for (j = 0, k = i, carryIn = false; j <= b.len; j++, k++) {
+ /*
+ * The body of this loop is very similar to the body of the first loop
+ * in `add', except that this loop does a `+=' instead of a `+'.
+ */
+ temp = blk[k] + getShiftedBlock(b, j, i2);
+ carryOut = (temp < blk[k]);
+ if (carryIn) {
+ temp++;
+ carryOut |= (temp == 0);
+ }
+ blk[k] = temp;
+ carryIn = carryOut;
+ }
+ // No more extra iteration to deal with `bHigh'.
+ // Roll-over a carry as necessary.
+ for (; carryIn; k++) {
+ blk[k]++;
+ carryIn = (blk[k] == 0);
+ }
+ }
+ }
+ // Zap possible leading zero
+ if (blk[len - 1] == 0)
+ len--;
+}
+
+/*
+ * DIVISION WITH REMAINDER
+ * This monstrous function mods *this by the given divisor b while storing the
+ * quotient in the given object q; at the end, *this contains the remainder.
+ * The seemingly bizarre pattern of inputs and outputs was chosen so that the
+ * function copies as little as possible (since it is implemented by repeated
+ * subtraction of multiples of b from *this).
+ *
+ * "modWithQuotient" might be a better name for this function, but I would
+ * rather not change the name now.
+ */
+void BigUnsigned::divideWithRemainder(const BigUnsigned &b, BigUnsigned &q) {
+ /* Defending against aliased calls is more complex than usual because we
+ * are writing to both *this and q.
+ *
+ * It would be silly to try to write quotient and remainder to the
+ * same variable. Rule that out right away. */
+ if (this == &q)
+ throw "BigUnsigned::divideWithRemainder: Cannot write quotient and remainder into the same variable";
+ /* Now *this and q are separate, so the only concern is that b might be
+ * aliased to one of them. If so, use a temporary copy of b. */
+ if (this == &b || &q == &b) {
+ BigUnsigned tmpB(b);
+ divideWithRemainder(tmpB, q);
+ return;
+ }
+
+ /*
+ * Knuth's definition of mod (which this function uses) is somewhat
+ * different from the C++ definition of % in case of division by 0.
+ *
+ * We let a / 0 == 0 (it doesn't matter much) and a % 0 == a, no
+ * exceptions thrown. This allows us to preserve both Knuth's demand
+ * that a mod 0 == a and the useful property that
+ * (a / b) * b + (a % b) == a.
+ */
+ if (b.len == 0) {
+ q.len = 0;
+ return;
+ }
+
+ /*
+ * If *this.len < b.len, then *this < b, and we can be sure that b doesn't go into
+ * *this at all. The quotient is 0 and *this is already the remainder (so leave it alone).
+ */
+ if (len < b.len) {
+ q.len = 0;
+ return;
+ }
+
+ // At this point we know (*this).len >= b.len > 0. (Whew!)
+
+ /*
+ * Overall method:
+ *
+ * For each appropriate i and i2, decreasing:
+ * Subtract (b << (i blocks and i2 bits)) from *this, storing the
+ * result in subtractBuf.
+ * If the subtraction succeeds with a nonnegative result:
+ * Turn on bit i2 of block i of the quotient q.
+ * Copy subtractBuf back into *this.
+ * Otherwise bit i2 of block i remains off, and *this is unchanged.
+ *
+ * Eventually q will contain the entire quotient, and *this will
+ * be left with the remainder.
+ *
+ * subtractBuf[x] corresponds to blk[x], not blk[x+i], since 2005.01.11.
+ * But on a single iteration, we don't touch the i lowest blocks of blk
+ * (and don't use those of subtractBuf) because these blocks are
+ * unaffected by the subtraction: we are subtracting
+ * (b << (i blocks and i2 bits)), which ends in at least `i' zero
+ * blocks. */
+ // Variables for the calculation
+ Index i, j, k;
+ unsigned int i2;
+ Blk temp;
+ bool borrowIn, borrowOut;
+
+ /*
+ * Make sure we have an extra zero block just past the value.
+ *
+ * When we attempt a subtraction, we might shift `b' so
+ * its first block begins a few bits left of the dividend,
+ * and then we'll try to compare these extra bits with
+ * a nonexistent block to the left of the dividend. The
+ * extra zero block ensures sensible behavior; we need
+ * an extra block in `subtractBuf' for exactly the same reason.
+ */
+ Index origLen = len; // Save real length.
+ /* To avoid an out-of-bounds access in case of reallocation, allocate
+ * first and then increment the logical length. */
+ allocateAndCopy(len + 1);
+ len++;
+ blk[origLen] = 0; // Zero the added block.
+
+ // subtractBuf holds part of the result of a subtraction; see above.
+ Blk *subtractBuf = new Blk[len];
+
+ // Set preliminary length for quotient and make room
+ q.len = origLen - b.len + 1;
+ q.allocate(q.len);
+ // Zero out the quotient
+ for (i = 0; i < q.len; i++)
+ q.blk[i] = 0;
+
+ // For each possible left-shift of b in blocks...
+ i = q.len;
+ while (i > 0) {
+ i--;
+ // For each possible left-shift of b in bits...
+ // (Remember, N is the number of bits in a Blk.)
+ q.blk[i] = 0;
+ i2 = N;
+ while (i2 > 0) {
+ i2--;
+ /*
+ * Subtract b, shifted left i blocks and i2 bits, from *this,
+ * and store the answer in subtractBuf. In the for loop, `k == i + j'.
+ *
+ * Compare this to the middle section of `multiply'. They
+ * are in many ways analogous. See especially the discussion
+ * of `getShiftedBlock'.
+ */
+ for (j = 0, k = i, borrowIn = false; j <= b.len; j++, k++) {
+ temp = blk[k] - getShiftedBlock(b, j, i2);
+ borrowOut = (temp > blk[k]);
+ if (borrowIn) {
+ borrowOut |= (temp == 0);
+ temp--;
+ }
+ // Since 2005.01.11, indices of `subtractBuf' directly match those of `blk', so use `k'.
+ subtractBuf[k] = temp;
+ borrowIn = borrowOut;
+ }
+ // No more extra iteration to deal with `bHigh'.
+ // Roll-over a borrow as necessary.
+ for (; k < origLen && borrowIn; k++) {
+ borrowIn = (blk[k] == 0);
+ subtractBuf[k] = blk[k] - 1;
+ }
+ /*
+ * If the subtraction was performed successfully (!borrowIn),
+ * set bit i2 in block i of the quotient.
+ *
+ * Then, copy the portion of subtractBuf filled by the subtraction
+ * back to *this. This portion starts with block i and ends--
+ * where? Not necessarily at block `i + b.len'! Well, we
+ * increased k every time we saved a block into subtractBuf, so
+ * the region of subtractBuf we copy is just [i, k).
+ */
+ if (!borrowIn) {
+ q.blk[i] |= (Blk(1) << i2);
+ while (k > i) {
+ k--;
+ blk[k] = subtractBuf[k];
+ }
+ }
+ }
+ }
+ // Zap possible leading zero in quotient
+ if (q.blk[q.len - 1] == 0)
+ q.len--;
+ // Zap any/all leading zeros in remainder
+ zapLeadingZeros();
+ // Deallocate subtractBuf.
+ // (Thanks to Brad Spencer for noticing my accidental omission of this!)
+ delete [] subtractBuf;
+}
+
+/* BITWISE OPERATORS
+ * These are straightforward blockwise operations except that they differ in
+ * the output length and the necessity of zapLeadingZeros. */
+
+void BigUnsigned::bitAnd(const BigUnsigned &a, const BigUnsigned &b) {
+ DTRT_ALIASED(this == &a || this == &b, bitAnd(a, b));
+ // The bitwise & can't be longer than either operand.
+ len = (a.len >= b.len) ? b.len : a.len;
+ allocate(len);
+ Index i;
+ for (i = 0; i < len; i++)
+ blk[i] = a.blk[i] & b.blk[i];
+ zapLeadingZeros();
+}
+
+void BigUnsigned::bitOr(const BigUnsigned &a, const BigUnsigned &b) {
+ DTRT_ALIASED(this == &a || this == &b, bitOr(a, b));
+ Index i;
+ const BigUnsigned *a2, *b2;
+ if (a.len >= b.len) {
+ a2 = &a;
+ b2 = &b;
+ } else {
+ a2 = &b;
+ b2 = &a;
+ }
+ allocate(a2->len);
+ for (i = 0; i < b2->len; i++)
+ blk[i] = a2->blk[i] | b2->blk[i];
+ for (; i < a2->len; i++)
+ blk[i] = a2->blk[i];
+ len = a2->len;
+ // Doesn't need zapLeadingZeros.
+}
+
+void BigUnsigned::bitXor(const BigUnsigned &a, const BigUnsigned &b) {
+ DTRT_ALIASED(this == &a || this == &b, bitXor(a, b));
+ Index i;
+ const BigUnsigned *a2, *b2;
+ if (a.len >= b.len) {
+ a2 = &a;
+ b2 = &b;
+ } else {
+ a2 = &b;
+ b2 = &a;
+ }
+ allocate(a2->len);
+ for (i = 0; i < b2->len; i++)
+ blk[i] = a2->blk[i] ^ b2->blk[i];
+ for (; i < a2->len; i++)
+ blk[i] = a2->blk[i];
+ len = a2->len;
+ zapLeadingZeros();
+}
+
+void BigUnsigned::bitShiftLeft(const BigUnsigned &a, int b) {
+ DTRT_ALIASED(this == &a, bitShiftLeft(a, b));
+ if (b < 0) {
+ if (b << 1 == 0)
+ throw "BigUnsigned::bitShiftLeft: "
+ "Pathological shift amount not implemented";
+ else {
+ bitShiftRight(a, -b);
+ return;
+ }
+ }
+ Index shiftBlocks = b / N;
+ unsigned int shiftBits = b % N;
+ // + 1: room for high bits nudged left into another block
+ len = a.len + shiftBlocks + 1;
+ allocate(len);
+ Index i, j;
+ for (i = 0; i < shiftBlocks; i++)
+ blk[i] = 0;
+ for (j = 0, i = shiftBlocks; j <= a.len; j++, i++)
+ blk[i] = getShiftedBlock(a, j, shiftBits);
+ // Zap possible leading zero
+ if (blk[len - 1] == 0)
+ len--;
+}
+
+void BigUnsigned::bitShiftRight(const BigUnsigned &a, int b) {
+ DTRT_ALIASED(this == &a, bitShiftRight(a, b));
+ if (b < 0) {
+ if (b << 1 == 0)
+ throw "BigUnsigned::bitShiftRight: "
+ "Pathological shift amount not implemented";
+ else {
+ bitShiftLeft(a, -b);
+ return;
+ }
+ }
+ // This calculation is wacky, but expressing the shift as a left bit shift
+ // within each block lets us use getShiftedBlock.
+ Index rightShiftBlocks = (b + N - 1) / N;
+ unsigned int leftShiftBits = N * rightShiftBlocks - b;
+ // Now (N * rightShiftBlocks - leftShiftBits) == b
+ // and 0 <= leftShiftBits < N.
+ if (rightShiftBlocks >= a.len + 1) {
+ // All of a is guaranteed to be shifted off, even considering the left
+ // bit shift.
+ len = 0;
+ return;
+ }
+ // Now we're allocating a positive amount.
+ // + 1: room for high bits nudged left into another block
+ len = a.len + 1 - rightShiftBlocks;
+ allocate(len);
+ Index i, j;
+ for (j = rightShiftBlocks, i = 0; j <= a.len; j++, i++)
+ blk[i] = getShiftedBlock(a, j, leftShiftBits);
+ // Zap possible leading zero
+ if (blk[len - 1] == 0)
+ len--;
+}
+
+// INCREMENT/DECREMENT OPERATORS
+
+// Prefix increment
+void BigUnsigned::operator ++() {
+ Index i;
+ bool carry = true;
+ for (i = 0; i < len && carry; i++) {
+ blk[i]++;
+ carry = (blk[i] == 0);
+ }
+ if (carry) {
+ // Allocate and then increase length, as in divideWithRemainder
+ allocateAndCopy(len + 1);
+ len++;
+ blk[i] = 1;
+ }
+}
+
+// Postfix increment: same as prefix
+void BigUnsigned::operator ++(int) {
+ operator ++();
+}
+
+// Prefix decrement
+void BigUnsigned::operator --() {
+ if (len == 0)
+ throw "BigUnsigned::operator --(): Cannot decrement an unsigned zero";
+ Index i;
+ bool borrow = true;
+ for (i = 0; borrow; i++) {
+ borrow = (blk[i] == 0);
+ blk[i]--;
+ }
+ // Zap possible leading zero (there can only be one)
+ if (blk[len - 1] == 0)
+ len--;
+}
+
+// Postfix decrement: same as prefix
+void BigUnsigned::operator --(int) {
+ operator --();
+}
diff --git a/libs/bigint/BigUnsigned.hh b/libs/bigint/BigUnsigned.hh
new file mode 100644
index 00000000..9228753c
--- /dev/null
+++ b/libs/bigint/BigUnsigned.hh
@@ -0,0 +1,418 @@
+#ifndef BIGUNSIGNED_H
+#define BIGUNSIGNED_H
+
+#include "NumberlikeArray.hh"
+
+/* A BigUnsigned object represents a nonnegative integer of size limited only by
+ * available memory. BigUnsigneds support most mathematical operators and can
+ * be converted to and from most primitive integer types.
+ *
+ * The number is stored as a NumberlikeArray of unsigned longs as if it were
+ * written in base 256^sizeof(unsigned long). The least significant block is
+ * first, and the length is such that the most significant block is nonzero. */
+class BigUnsigned : protected NumberlikeArray<unsigned long> {
+
+public:
+ // Enumeration for the result of a comparison.
+ enum CmpRes { less = -1, equal = 0, greater = 1 };
+
+ // BigUnsigneds are built with a Blk type of unsigned long.
+ typedef unsigned long Blk;
+
+ typedef NumberlikeArray<Blk>::Index Index;
+ using NumberlikeArray<Blk>::N;
+
+protected:
+ // Creates a BigUnsigned with a capacity; for internal use.
+ BigUnsigned(int, Index c) : NumberlikeArray<Blk>(0, c) {}
+
+ // Decreases len to eliminate any leading zero blocks.
+ void zapLeadingZeros() {
+ while (len > 0 && blk[len - 1] == 0)
+ len--;
+ }
+
+public:
+ // Constructs zero.
+ BigUnsigned() : NumberlikeArray<Blk>() {}
+
+ // Copy constructor
+ BigUnsigned(const BigUnsigned &x) : NumberlikeArray<Blk>(x) {}
+
+ // Assignment operator
+ void operator=(const BigUnsigned &x) {
+ NumberlikeArray<Blk>::operator =(x);
+ }
+
+ // Constructor that copies from a given array of blocks.
+ BigUnsigned(const Blk *b, Index blen) : NumberlikeArray<Blk>(b, blen) {
+ // Eliminate any leading zeros we may have been passed.
+ zapLeadingZeros();
+ }
+
+ // Destructor. NumberlikeArray does the delete for us.
+ ~BigUnsigned() {}
+
+ // Constructors from primitive integer types
+ BigUnsigned(unsigned long x);
+ BigUnsigned( long x);
+ BigUnsigned(unsigned int x);
+ BigUnsigned( int x);
+ BigUnsigned(unsigned short x);
+ BigUnsigned( short x);
+protected:
+ // Helpers
+ template <class X> void initFromPrimitive (X x);
+ template <class X> void initFromSignedPrimitive(X x);
+public:
+
+ /* Converters to primitive integer types
+ * The implicit conversion operators caused trouble, so these are now
+ * named. */
+ unsigned long toUnsignedLong () const;
+ long toLong () const;
+ unsigned int toUnsignedInt () const;
+ int toInt () const;
+ unsigned short toUnsignedShort() const;
+ short toShort () const;
+protected:
+ // Helpers
+ template <class X> X convertToSignedPrimitive() const;
+ template <class X> X convertToPrimitive () const;
+public:
+
+ // BIT/BLOCK ACCESSORS
+
+ // Expose these from NumberlikeArray directly.
+ using NumberlikeArray<Blk>::getCapacity;
+ using NumberlikeArray<Blk>::getLength;
+
+ /* Returns the requested block, or 0 if it is beyond the length (as if
+ * the number had 0s infinitely to the left). */
+ Blk getBlock(Index i) const { return i >= len ? 0 : blk[i]; }
+ /* Sets the requested block. The number grows or shrinks as necessary. */
+ void setBlock(Index i, Blk newBlock);
+
+ // The number is zero if and only if the canonical length is zero.
+ bool isZero() const { return NumberlikeArray<Blk>::isEmpty(); }
+
+ /* Returns the length of the number in bits, i.e., zero if the number
+ * is zero and otherwise one more than the largest value of bi for
+ * which getBit(bi) returns true. */
+ Index bitLength() const;
+ /* Get the state of bit bi, which has value 2^bi. Bits beyond the
+ * number's length are considered to be 0. */
+ bool getBit(Index bi) const {
+ return (getBlock(bi / N) & (Blk(1) << (bi % N))) != 0;
+ }
+ /* Sets the state of bit bi to newBit. The number grows or shrinks as
+ * necessary. */
+ void setBit(Index bi, bool newBit);
+
+ // COMPARISONS
+
+ // Compares this to x like Perl's <=>
+ CmpRes compareTo(const BigUnsigned &x) const;
+
+ // Ordinary comparison operators
+ bool operator ==(const BigUnsigned &x) const {
+ return NumberlikeArray<Blk>::operator ==(x);
+ }
+ bool operator !=(const BigUnsigned &x) const {
+ return NumberlikeArray<Blk>::operator !=(x);
+ }
+ bool operator < (const BigUnsigned &x) const { return compareTo(x) == less ; }
+ bool operator <=(const BigUnsigned &x) const { return compareTo(x) != greater; }
+ bool operator >=(const BigUnsigned &x) const { return compareTo(x) != less ; }
+ bool operator > (const BigUnsigned &x) const { return compareTo(x) == greater; }
+
+ /*
+ * BigUnsigned and BigInteger both provide three kinds of operators.
+ * Here ``big-integer'' refers to BigInteger or BigUnsigned.
+ *
+ * (1) Overloaded ``return-by-value'' operators:
+ * +, -, *, /, %, unary -, &, |, ^, <<, >>.
+ * Big-integer code using these operators looks identical to code using
+ * the primitive integer types. These operators take one or two
+ * big-integer inputs and return a big-integer result, which can then
+ * be assigned to a BigInteger variable or used in an expression.
+ * Example:
+ * BigInteger a(1), b = 1;
+ * BigInteger c = a + b;
+ *
+ * (2) Overloaded assignment operators:
+ * +=, -=, *=, /=, %=, flipSign, &=, |=, ^=, <<=, >>=, ++, --.
+ * Again, these are used on big integers just like on ints. They take
+ * one writable big integer that both provides an operand and receives a
+ * result. Most also take a second read-only operand.
+ * Example:
+ * BigInteger a(1), b(1);
+ * a += b;
+ *
+ * (3) Copy-less operations: `add', `subtract', etc.
+ * These named methods take operands as arguments and store the result
+ * in the receiver (*this), avoiding unnecessary copies and allocations.
+ * `divideWithRemainder' is special: it both takes the dividend from and
+ * stores the remainder into the receiver, and it takes a separate
+ * object in which to store the quotient. NOTE: If you are wondering
+ * why these don't return a value, you probably mean to use the
+ * overloaded return-by-value operators instead.
+ *
+ * Examples:
+ * BigInteger a(43), b(7), c, d;
+ *
+ * c = a + b; // Now c == 50.
+ * c.add(a, b); // Same effect but without the two copies.
+ *
+ * c.divideWithRemainder(b, d);
+ * // 50 / 7; now d == 7 (quotient) and c == 1 (remainder).
+ *
+ * // ``Aliased'' calls now do the right thing using a temporary
+ * // copy, but see note on `divideWithRemainder'.
+ * a.add(a, b);
+ */
+
+ // COPY-LESS OPERATIONS
+
+ // These 8: Arguments are read-only operands, result is saved in *this.
+ void add(const BigUnsigned &a, const BigUnsigned &b);
+ void subtract(const BigUnsigned &a, const BigUnsigned &b);
+ void multiply(const BigUnsigned &a, const BigUnsigned &b);
+ void bitAnd(const BigUnsigned &a, const BigUnsigned &b);
+ void bitOr(const BigUnsigned &a, const BigUnsigned &b);
+ void bitXor(const BigUnsigned &a, const BigUnsigned &b);
+ /* Negative shift amounts translate to opposite-direction shifts,
+ * except for -2^(8*sizeof(int)-1) which is unimplemented. */
+ void bitShiftLeft(const BigUnsigned &a, int b);
+ void bitShiftRight(const BigUnsigned &a, int b);
+
+ /* `a.divideWithRemainder(b, q)' is like `q = a / b, a %= b'.
+ * / and % use semantics similar to Knuth's, which differ from the
+ * primitive integer semantics under division by zero. See the
+ * implementation in BigUnsigned.cc for details.
+ * `a.divideWithRemainder(b, a)' throws an exception: it doesn't make
+ * sense to write quotient and remainder into the same variable. */
+ void divideWithRemainder(const BigUnsigned &b, BigUnsigned &q);
+
+ /* `divide' and `modulo' are no longer offered. Use
+ * `divideWithRemainder' instead. */
+
+ // OVERLOADED RETURN-BY-VALUE OPERATORS
+ BigUnsigned operator +(const BigUnsigned &x) const;
+ BigUnsigned operator -(const BigUnsigned &x) const;
+ BigUnsigned operator *(const BigUnsigned &x) const;
+ BigUnsigned operator /(const BigUnsigned &x) const;
+ BigUnsigned operator %(const BigUnsigned &x) const;
+ /* OK, maybe unary minus could succeed in one case, but it really
+ * shouldn't be used, so it isn't provided. */
+ BigUnsigned operator &(const BigUnsigned &x) const;
+ BigUnsigned operator |(const BigUnsigned &x) const;
+ BigUnsigned operator ^(const BigUnsigned &x) const;
+ BigUnsigned operator <<(int b) const;
+ BigUnsigned operator >>(int b) const;
+
+ // OVERLOADED ASSIGNMENT OPERATORS
+ void operator +=(const BigUnsigned &x);
+ void operator -=(const BigUnsigned &x);
+ void operator *=(const BigUnsigned &x);
+ void operator /=(const BigUnsigned &x);
+ void operator %=(const BigUnsigned &x);
+ void operator &=(const BigUnsigned &x);
+ void operator |=(const BigUnsigned &x);
+ void operator ^=(const BigUnsigned &x);
+ void operator <<=(int b);
+ void operator >>=(int b);
+
+ /* INCREMENT/DECREMENT OPERATORS
+ * To discourage messy coding, these do not return *this, so prefix
+ * and postfix behave the same. */
+ void operator ++( );
+ void operator ++(int);
+ void operator --( );
+ void operator --(int);
+
+ // Helper function that needs access to BigUnsigned internals
+ friend Blk getShiftedBlock(const BigUnsigned &num, Index x,
+ unsigned int y);
+
+ // See BigInteger.cc.
+ template <class X>
+ friend X convertBigUnsignedToPrimitiveAccess(const BigUnsigned &a);
+};
+
+/* Implementing the return-by-value and assignment operators in terms of the
+ * copy-less operations. The copy-less operations are responsible for making
+ * any necessary temporary copies to work around aliasing. */
+
+inline BigUnsigned BigUnsigned::operator +(const BigUnsigned &x) const {
+ BigUnsigned ans;
+ ans.add(*this, x);
+ return ans;
+}
+inline BigUnsigned BigUnsigned::operator -(const BigUnsigned &x) const {
+ BigUnsigned ans;
+ ans.subtract(*this, x);
+ return ans;
+}
+inline BigUnsigned BigUnsigned::operator *(const BigUnsigned &x) const {
+ BigUnsigned ans;
+ ans.multiply(*this, x);
+ return ans;
+}
+inline BigUnsigned BigUnsigned::operator /(const BigUnsigned &x) const {
+ if (x.isZero()) throw "BigUnsigned::operator /: division by zero";
+ BigUnsigned q, r;
+ r = *this;
+ r.divideWithRemainder(x, q);
+ return q;
+}
+inline BigUnsigned BigUnsigned::operator %(const BigUnsigned &x) const {
+ if (x.isZero()) throw "BigUnsigned::operator %: division by zero";
+ BigUnsigned q, r;
+ r = *this;
+ r.divideWithRemainder(x, q);
+ return r;
+}
+inline BigUnsigned BigUnsigned::operator &(const BigUnsigned &x) const {
+ BigUnsigned ans;
+ ans.bitAnd(*this, x);
+ return ans;
+}
+inline BigUnsigned BigUnsigned::operator |(const BigUnsigned &x) const {
+ BigUnsigned ans;
+ ans.bitOr(*this, x);
+ return ans;
+}
+inline BigUnsigned BigUnsigned::operator ^(const BigUnsigned &x) const {
+ BigUnsigned ans;
+ ans.bitXor(*this, x);
+ return ans;
+}
+inline BigUnsigned BigUnsigned::operator <<(int b) const {
+ BigUnsigned ans;
+ ans.bitShiftLeft(*this, b);
+ return ans;
+}
+inline BigUnsigned BigUnsigned::operator >>(int b) const {
+ BigUnsigned ans;
+ ans.bitShiftRight(*this, b);
+ return ans;
+}
+
+inline void BigUnsigned::operator +=(const BigUnsigned &x) {
+ add(*this, x);
+}
+inline void BigUnsigned::operator -=(const BigUnsigned &x) {
+ subtract(*this, x);
+}
+inline void BigUnsigned::operator *=(const BigUnsigned &x) {
+ multiply(*this, x);
+}
+inline void BigUnsigned::operator /=(const BigUnsigned &x) {
+ if (x.isZero()) throw "BigUnsigned::operator /=: division by zero";
+ /* The following technique is slightly faster than copying *this first
+ * when x is large. */
+ BigUnsigned q;
+ divideWithRemainder(x, q);
+ // *this contains the remainder, but we overwrite it with the quotient.
+ *this = q;
+}
+inline void BigUnsigned::operator %=(const BigUnsigned &x) {
+ if (x.isZero()) throw "BigUnsigned::operator %=: division by zero";
+ BigUnsigned q;
+ // Mods *this by x. Don't care about quotient left in q.
+ divideWithRemainder(x, q);
+}
+inline void BigUnsigned::operator &=(const BigUnsigned &x) {
+ bitAnd(*this, x);
+}
+inline void BigUnsigned::operator |=(const BigUnsigned &x) {
+ bitOr(*this, x);
+}
+inline void BigUnsigned::operator ^=(const BigUnsigned &x) {
+ bitXor(*this, x);
+}
+inline void BigUnsigned::operator <<=(int b) {
+ bitShiftLeft(*this, b);
+}
+inline void BigUnsigned::operator >>=(int b) {
+ bitShiftRight(*this, b);
+}
+
+/* Templates for conversions of BigUnsigned to and from primitive integers.
+ * BigInteger.cc needs to instantiate convertToPrimitive, and the uses in
+ * BigUnsigned.cc didn't do the trick; I think g++ inlined convertToPrimitive
+ * instead of generating linkable instantiations. So for consistency, I put
+ * all the templates here. */
+
+// CONSTRUCTION FROM PRIMITIVE INTEGERS
+
+/* Initialize this BigUnsigned from the given primitive integer. The same
+ * pattern works for all primitive integer types, so I put it into a template to
+ * reduce code duplication. (Don't worry: this is protected and we instantiate
+ * it only with primitive integer types.) Type X could be signed, but x is
+ * known to be nonnegative. */
+template <class X>
+void BigUnsigned::initFromPrimitive(X x) {
+ if (x == 0)
+ ; // NumberlikeArray already initialized us to zero.
+ else {
+ // Create a single block. blk is NULL; no need to delete it.
+ cap = 1;
+ blk = new Blk[1];
+ len = 1;
+ blk[0] = Blk(x);
+ }
+}
+
+/* Ditto, but first check that x is nonnegative. I could have put the check in
+ * initFromPrimitive and let the compiler optimize it out for unsigned-type
+ * instantiations, but I wanted to avoid the warning stupidly issued by g++ for
+ * a condition that is constant in *any* instantiation, even if not in all. */
+template <class X>
+void BigUnsigned::initFromSignedPrimitive(X x) {
+ if (x < 0)
+ throw "BigUnsigned constructor: "
+ "Cannot construct a BigUnsigned from a negative number";
+ else
+ initFromPrimitive(x);
+}
+
+// CONVERSION TO PRIMITIVE INTEGERS
+
+/* Template with the same idea as initFromPrimitive. This might be slightly
+ * slower than the previous version with the masks, but it's much shorter and
+ * clearer, which is the library's stated goal. */
+template <class X>
+X BigUnsigned::convertToPrimitive() const {
+ if (len == 0)
+ // The number is zero; return zero.
+ return 0;
+ else if (len == 1) {
+ // The single block might fit in an X. Try the conversion.
+ X x = X(blk[0]);
+ // Make sure the result accurately represents the block.
+ if (Blk(x) == blk[0])
+ // Successful conversion.
+ return x;
+ // Otherwise fall through.
+ }
+ throw "BigUnsigned::to<Primitive>: "
+ "Value is too big to fit in the requested type";
+}
+
+/* Wrap the above in an x >= 0 test to make sure we got a nonnegative result,
+ * not a negative one that happened to convert back into the correct nonnegative
+ * one. (E.g., catch incorrect conversion of 2^31 to the long -2^31.) Again,
+ * separated to avoid a g++ warning. */
+template <class X>
+X BigUnsigned::convertToSignedPrimitive() const {
+ X x = convertToPrimitive<X>();
+ if (x >= 0)
+ return x;
+ else
+ throw "BigUnsigned::to(Primitive): "
+ "Value is too big to fit in the requested type";
+}
+
+#endif
diff --git a/libs/bigint/BigUnsignedInABase.cc b/libs/bigint/BigUnsignedInABase.cc
new file mode 100644
index 00000000..999faaf2
--- /dev/null
+++ b/libs/bigint/BigUnsignedInABase.cc
@@ -0,0 +1,125 @@
+#include "BigUnsignedInABase.hh"
+
+BigUnsignedInABase::BigUnsignedInABase(const Digit *d, Index l, Base base)
+ : NumberlikeArray<Digit>(d, l), base(base) {
+ // Check the base
+ if (base < 2)
+ throw "BigUnsignedInABase::BigUnsignedInABase(const Digit *, Index, Base): The base must be at least 2";
+
+ // Validate the digits.
+ for (Index i = 0; i < l; i++)
+ if (blk[i] >= base)
+ throw "BigUnsignedInABase::BigUnsignedInABase(const Digit *, Index, Base): A digit is too large for the specified base";
+
+ // Eliminate any leading zeros we may have been passed.
+ zapLeadingZeros();
+}
+
+namespace {
+ unsigned int bitLen(unsigned int x) {
+ unsigned int len = 0;
+ while (x > 0) {
+ x >>= 1;
+ len++;
+ }
+ return len;
+ }
+ unsigned int ceilingDiv(unsigned int a, unsigned int b) {
+ return (a + b - 1) / b;
+ }
+}
+
+BigUnsignedInABase::BigUnsignedInABase(const BigUnsigned &x, Base base) {
+ // Check the base
+ if (base < 2)
+ throw "BigUnsignedInABase(BigUnsigned, Base): The base must be at least 2";
+ this->base = base;
+
+ // Get an upper bound on how much space we need
+ int maxBitLenOfX = x.getLength() * BigUnsigned::N;
+ int minBitsPerDigit = bitLen(base) - 1;
+ int maxDigitLenOfX = ceilingDiv(maxBitLenOfX, minBitsPerDigit);
+ len = maxDigitLenOfX; // Another change to comply with `staying in bounds'.
+ allocate(len); // Get the space
+
+ BigUnsigned x2(x), buBase(base);
+ Index digitNum = 0;
+
+ while (!x2.isZero()) {
+ // Get last digit. This is like `lastDigit = x2 % buBase, x2 /= buBase'.
+ BigUnsigned lastDigit(x2);
+ lastDigit.divideWithRemainder(buBase, x2);
+ // Save the digit.
+ blk[digitNum] = lastDigit.toUnsignedShort();
+ // Move on. We can't run out of room: we figured it out above.
+ digitNum++;
+ }
+
+ // Save the actual length.
+ len = digitNum;
+}
+
+BigUnsignedInABase::operator BigUnsigned() const {
+ BigUnsigned ans(0), buBase(base), temp;
+ Index digitNum = len;
+ while (digitNum > 0) {
+ digitNum--;
+ temp.multiply(ans, buBase);
+ ans.add(temp, BigUnsigned(blk[digitNum]));
+ }
+ return ans;
+}
+
+BigUnsignedInABase::BigUnsignedInABase(const std::string &s, Base base) {
+ // Check the base.
+ if (base > 36)
+ throw "BigUnsignedInABase(std::string, Base): The default string conversion routines use the symbol set 0-9, A-Z and therefore support only up to base 36. You tried a conversion with a base over 36; write your own string conversion routine.";
+ // Save the base.
+ // This pattern is seldom seen in C++, but the analogous ``this.'' is common in Java.
+ this->base = base;
+
+ // `s.length()' is a `size_t', while `len' is a `NumberlikeArray::Index',
+ // also known as an `unsigned int'. Some compilers warn without this cast.
+ len = Index(s.length());
+ allocate(len);
+
+ Index digitNum, symbolNumInString;
+ for (digitNum = 0; digitNum < len; digitNum++) {
+ symbolNumInString = len - 1 - digitNum;
+ char theSymbol = s[symbolNumInString];
+ if (theSymbol >= '0' && theSymbol <= '9')
+ blk[digitNum] = theSymbol - '0';
+ else if (theSymbol >= 'A' && theSymbol <= 'Z')
+ blk[digitNum] = theSymbol - 'A' + 10;
+ else if (theSymbol >= 'a' && theSymbol <= 'z')
+ blk[digitNum] = theSymbol - 'a' + 10;
+ else
+ throw "BigUnsignedInABase(std::string, Base): Bad symbol in input. Only 0-9, A-Z, a-z are accepted.";
+
+ if (blk[digitNum] >= base)
+ throw "BigUnsignedInABase::BigUnsignedInABase(const Digit *, Index, Base): A digit is too large for the specified base";
+ }
+ zapLeadingZeros();
+}
+
+BigUnsignedInABase::operator std::string() const {
+ if (base > 36)
+ throw "BigUnsignedInABase ==> std::string: The default string conversion routines use the symbol set 0-9, A-Z and therefore support only up to base 36. You tried a conversion with a base over 36; write your own string conversion routine.";
+ if (len == 0)
+ return std::string("0");
+ // Some compilers don't have push_back, so use a char * buffer instead.
+ char *s = new char[len + 1];
+ s[len] = '\0';
+ Index digitNum, symbolNumInString;
+ for (symbolNumInString = 0; symbolNumInString < len; symbolNumInString++) {
+ digitNum = len - 1 - symbolNumInString;
+ Digit theDigit = blk[digitNum];
+ if (theDigit < 10)
+ s[symbolNumInString] = char('0' + theDigit);
+ else
+ s[symbolNumInString] = char('A' + theDigit - 10);
+ }
+ std::string s2(s);
+ delete [] s;
+ return s2;
+}
diff --git a/libs/bigint/BigUnsignedInABase.hh b/libs/bigint/BigUnsignedInABase.hh
new file mode 100644
index 00000000..0ea89c6e
--- /dev/null
+++ b/libs/bigint/BigUnsignedInABase.hh
@@ -0,0 +1,122 @@
+#ifndef BIGUNSIGNEDINABASE_H
+#define BIGUNSIGNEDINABASE_H
+
+#include "NumberlikeArray.hh"
+#include "BigUnsigned.hh"
+#include <string>
+
+/*
+ * A BigUnsignedInABase object represents a nonnegative integer of size limited
+ * only by available memory, represented in a user-specified base that can fit
+ * in an `unsigned short' (most can, and this saves memory).
+ *
+ * BigUnsignedInABase is intended as an intermediary class with little
+ * functionality of its own. BigUnsignedInABase objects can be constructed
+ * from, and converted to, BigUnsigneds (requiring multiplication, mods, etc.)
+ * and `std::string's (by switching digit values for appropriate characters).
+ *
+ * BigUnsignedInABase is similar to BigUnsigned. Note the following:
+ *
+ * (1) They represent the number in exactly the same way, except that
+ * BigUnsignedInABase uses ``digits'' (or Digit) where BigUnsigned uses
+ * ``blocks'' (or Blk).
+ *
+ * (2) Both use the management features of NumberlikeArray. (In fact, my desire
+ * to add a BigUnsignedInABase class without duplicating a lot of code led me to
+ * introduce NumberlikeArray.)
+ *
+ * (3) The only arithmetic operation supported by BigUnsignedInABase is an
+ * equality test. Use BigUnsigned for arithmetic.
+ */
+
+class BigUnsignedInABase : protected NumberlikeArray<unsigned short> {
+
+public:
+ // The digits of a BigUnsignedInABase are unsigned shorts.
+ typedef unsigned short Digit;
+ // That's also the type of a base.
+ typedef Digit Base;
+
+protected:
+ // The base in which this BigUnsignedInABase is expressed
+ Base base;
+
+ // Creates a BigUnsignedInABase with a capacity; for internal use.
+ BigUnsignedInABase(int, Index c) : NumberlikeArray<Digit>(0, c) {}
+
+ // Decreases len to eliminate any leading zero digits.
+ void zapLeadingZeros() {
+ while (len > 0 && blk[len - 1] == 0)
+ len--;
+ }
+
+public:
+ // Constructs zero in base 2.
+ BigUnsignedInABase() : NumberlikeArray<Digit>(), base(2) {}
+
+ // Copy constructor
+ BigUnsignedInABase(const BigUnsignedInABase &x) : NumberlikeArray<Digit>(x), base(x.base) {}
+
+ // Assignment operator
+ void operator =(const BigUnsignedInABase &x) {
+ NumberlikeArray<Digit>::operator =(x);
+ base = x.base;
+ }
+
+ // Constructor that copies from a given array of digits.
+ BigUnsignedInABase(const Digit *d, Index l, Base base);
+
+ // Destructor. NumberlikeArray does the delete for us.
+ ~BigUnsignedInABase() {}
+
+ // LINKS TO BIGUNSIGNED
+ BigUnsignedInABase(const BigUnsigned &x, Base base);
+ operator BigUnsigned() const;
+
+ /* LINKS TO STRINGS
+ *
+ * These use the symbols ``0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'' to
+ * represent digits of 0 through 35. When parsing strings, lowercase is
+ * also accepted.
+ *
+ * All string representations are big-endian (big-place-value digits
+ * first). (Computer scientists have adopted zero-based counting; why
+ * can't they tolerate little-endian numbers?)
+ *
+ * No string representation has a ``base indicator'' like ``0x''.
+ *
+ * An exception is made for zero: it is converted to ``0'' and not the
+ * empty string.
+ *
+ * If you want different conventions, write your own routines to go
+ * between BigUnsignedInABase and strings. It's not hard.
+ */
+ operator std::string() const;
+ BigUnsignedInABase(const std::string &s, Base base);
+
+public:
+
+ // ACCESSORS
+ Base getBase() const { return base; }
+
+ // Expose these from NumberlikeArray directly.
+ using NumberlikeArray<Digit>::getCapacity;
+ using NumberlikeArray<Digit>::getLength;
+
+ /* Returns the requested digit, or 0 if it is beyond the length (as if
+ * the number had 0s infinitely to the left). */
+ Digit getDigit(Index i) const { return i >= len ? 0 : blk[i]; }
+
+ // The number is zero if and only if the canonical length is zero.
+ bool isZero() const { return NumberlikeArray<Digit>::isEmpty(); }
+
+ /* Equality test. For the purposes of this test, two BigUnsignedInABase
+ * values must have the same base to be equal. */
+ bool operator ==(const BigUnsignedInABase &x) const {
+ return base == x.base && NumberlikeArray<Digit>::operator ==(x);
+ }
+ bool operator !=(const BigUnsignedInABase &x) const { return !operator ==(x); }
+
+};
+
+#endif
diff --git a/libs/bigint/ChangeLog b/libs/bigint/ChangeLog
new file mode 100644
index 00000000..ac6927c4
--- /dev/null
+++ b/libs/bigint/ChangeLog
@@ -0,0 +1,146 @@
+ Change Log
+
+These entries tell you what was added, fixed, or improved in each version as
+compared to the previous one. In case you haven't noticed, a version number
+roughly corresponds to the release date of that version in `YYYY.MM.DD[.N]'
+format, where `.N' goes `.2', `.3', etc. if there are multiple versions on the
+same day. The topmost version listed is the one you have.
+
+2010.04.30
+----------
+- Strengthen the advice about build/IDE configuration in the README.
+
+2009.05.03
+----------
+- BigUnsigned::{get,set}Bit: Change two remaining `1 <<' to `Blk(1) <<' to work
+ on systems where sizeof(unsigned int) != sizeof(Blk). Bug reported by Brad
+ Spencer.
+- dataToBigInteger: Change a `delete' to `delete []' to avoid leaking memory.
+ Bug reported by Nicolás Carrasco.
+
+2009.03.26
+----------
+- BigUnsignedInABase(std::string) Reject digits too big for the base.
+ Bug reported by Niakam Kazemi.
+
+2008.07.20
+----------
+Dennis Yew pointed out serious problems with ambiguities and unwanted
+conversions when mixing BigInteger/BigUnsigned and primitive integers. To fix
+these, I removed the implicit conversions from BigInteger/BigUnsigned to
+primitive integers and from BigInteger to BigUnsigned. Removing the
+BigInteger-to-BigUnsigned conversion required changing BigInteger to have a
+BigUnsigned field instead of inheriting from it; this was a complex task but
+ultimately gave a saner design. At the same time, I went through the entire
+codebase, making the formatting and comments prettier and reworking anything I
+thought was unclear. I also added a testsuite (currently for 32-bit systems
+only); it doesn't yet cover the entire library but should help to ensure that
+things work the way they should.
+
+A number of changes from version 2007.07.07 break compatibility with existing
+code that uses the library, but updating that code should be pretty easy:
+- BigInteger can no longer be implicitly converted to BigUnsigned. Use
+ getMagnitude() instead.
+- BigUnsigned and BigInteger can no longer be implicitly converted to primitive
+ integers. Use the toInt() family of functions instead.
+- The easy* functions have been renamed to more mature names:
+ bigUnsignedToString, bigIntegerToString, stringToBigUnsigned,
+ stringToBigInteger, dataToBigInteger.
+- BigInteger no longer supports bitwise operations. Get the magnitude with
+ getMagnitude() and operate on that instead.
+- The old {BigUnsigned,BigInteger}::{divide,modulo} copy-less options have been
+ removed. Use divideWithRemainder instead.
+- Added a base argument to BigUnsignedInABase's digit-array constructor. I
+ ope no one used that constructor in its broken state anyway.
+
+Other notable changes:
+- Added BigUnsigned functions setBlock, bitLength, getBit, setBit.
+- The bit-shifting operations now support negative shift amounts, which shift in
+ the other direction.
+- Added some big-integer algorithms in BigIntegerAlgorithms.hh: gcd,
+ extendedEuclidean, modinv, modexp.
+
+2007.07.07
+----------
+Update the "Running the sample program produces this output:" comment in
+sample.cc for the bitwise operators.
+
+2007.06.14
+----------
+- Implement << and >> for BigUnsigned in response to email from Marco Schulze.
+- Fix name: DOTR_ALIASED -> DTRT_ALIASED.
+- Demonstrate all bitwise operators (&, |, ^, <<, >>) in sample.cc.
+
+2007.02.16
+----------
+Boris Dessy pointed out that the library threw an exception on "a *= a", so I changed all the put-here operations to handle aliased calls correctly using a temporary copy instead of throwing exceptions.
+
+2006.08.14
+----------
+In BigUnsigned::bitXor, change allocate(b2->len) to allocate(a2->len): we should allocate enough space for the longer number, not the shorter one! Thanks to Sriram Sankararaman for pointing this out.
+
+2006.05.03
+----------
+I ran the sample program using valgrind and discovered a `delete s' that should be `delete [] s' and a `len++' before an `allocateAndCopy(len)' that should have been after an `allocateAndCopy(len + 1)'. I fixed both. Yay for valgrind!
+
+2006.05.01
+----------
+I fixed incorrect results reported by Mohand Mezmaz and related memory corruption on platforms where Blk is bigger than int. I replaced (1 << x) with (Blk(1) << x) in two places in BigUnsigned.cc.
+
+2006.04.24
+----------
+Two bug fixes: BigUnsigned "++x" no longer segfaults when x grows in length, and BigUnsigned == and != are now redeclared so as to be usable. I redid the Makefile: I removed the *.tag mechanism and hard-coded the library's header dependencies, I added comments, and I made the Makefile more useful for building one's own programs instead of just the sample.
+
+2006.02.26
+----------
+A few tweaks in preparation for a group to distribute the library. The project Web site has moved; I updated the references. I fixed a typo and added a missing function in NumberlikeArray.hh. I'm using Eclipse now, so you get Eclipse project files.
+
+2005.03.30
+----------
+Sam Larkin found a bug in `BigInteger::subtract'; I fixed it.
+
+2005.01.18
+----------
+I fixed some problems with `easyDataToBI'. Due to some multiply declared variables, this function would not compile. However, it is a template function, so the compiler parses it and doesn't compile the parsed representation until something uses the function; this is how I missed the problems. I also removed debugging output from this function.
+
+2005.01.17
+----------
+A fix to some out-of-bounds accesses reported by Milan Tomic (see the comment under `BigUnsigned::divideWithRemainder'). `BigUnsigned::multiply' and `BigUnsigned::divideWithRemainder' implementations neatened up a bit with the help of a function `getShiftedBlock'. I (finally!) introduced a constant `BigUnsigned::N', the number of bits in a `BigUnsigned::Blk', which varies depending on machine word size. In both code and comments, it replaces the much clunkier `8*sizeof(Blk)'. Numerous other small changes. There's a new conversion routine `easyDataToBI' that will convert almost any format of binary data to a `BigInteger'.
+
+I have inserted a significant number of new comments. Most explain unobvious aspects of the code.
+
+2005.01.06
+----------
+Some changes to the way zero-length arrays are handled by `NumberlikeArray', which fixed a memory leak reported by Milan Tomic.
+
+2004.12.24.2
+------------
+I tied down a couple of loose ends involving division/modulo. I added an explanation of put-here vs. overloaded operators in the sample program; this has confused too many people. Miscellaneous other improvements.
+
+I believe that, at this point, the Big Integer Library makes no assumptions about the word size of the machine it is using. `BigUnsigned::Blk' is always an `unsigned long', whatever that may be, and its size is computed with `sizeof' when necessary. However, just in case, I would be interested to have someone test the library on a non-32-bit machine to see if it works.
+
+2004.12.24
+----------
+This is a _major_ upgrade to the library. Among the things that have changed:
+
+I wrote the original version of the library, particularly the four ``classical algorithms'' in `BigUnsigned.cc', using array indexing. Then I rewrote it to use pointers because I thought that would be faster. But recently, I revisited the code in `BigUnsigned.cc' and found that I could not begin to understand what it was doing.
+
+I have decided that the drawbacks of pointers, increased coding difficulty and reduced code readability, far outweigh their speed benefits. Plus, any modern optimizing compiler should produce fast code either way. Therefore, I rewrote the library to use array indexing again. (Thank goodness for regular-expression find-and-replace. It saved me a lot of time.)
+
+The put-here operations `divide' and `modulo' of each of `BigUnsigned' and `BigInteger' have been supplanted by a single operation `divideWithRemainder'. Read the profuse comments for more information on its exact behavior.
+
+There is a new class `BigUnsignedInABase' that is like `BigUnsigned' but uses a user-specified, small base instead of `256 ^ sizeof(unsigned long)'. Much of the code common to the two has been factored out into `NumberlikeArray'.
+
+`BigUnsignedInABase' facilitates conversion between `BigUnsigned's and digit-by-digit string representations using `std::string'. Convenience routines to do this conversion are in `BigIntegerUtils.hh'. `iostream' compatibility has been improved.
+
+I would like to thank Chris Morbitzer for the e-mail message that catalyzed this major upgrade. He wanted a way to convert a string to a BigInteger. One thing just led to another, roughly in reverse order from how they are listed here.
+
+2004.1216
+---------
+Brad Spencer pointed out a memory leak in `BigUnsigned::divide'. It is fixed in the December 16, 2004 version.
+
+2004.1205
+---------
+After months of inactivity, I fixed a bug in the `BigInteger' division routine; thanks to David Allen for reporting the bug. I also added simple routines for decimal output to `std::ostream's, and there is a demo that prints out powers of 3.
+
+~~~~
diff --git a/libs/bigint/Makefile b/libs/bigint/Makefile
new file mode 100644
index 00000000..3018e98e
--- /dev/null
+++ b/libs/bigint/Makefile
@@ -0,0 +1,73 @@
+# Mention default target.
+all:
+
+# Implicit rule to compile C++ files. Modify to your taste.
+%.o: %.cc
+ g++ -c -O2 -Wall -Wextra -pedantic $<
+
+# Components of the library.
+library-objects = \
+ BigUnsigned.o \
+ BigInteger.o \
+ BigIntegerAlgorithms.o \
+ BigUnsignedInABase.o \
+ BigIntegerUtils.o \
+
+library-headers = \
+ NumberlikeArray.hh \
+ BigUnsigned.hh \
+ BigInteger.hh \
+ BigIntegerAlgorithms.hh \
+ BigUnsignedInABase.hh \
+ BigIntegerLibrary.hh \
+
+# To ``make the library'', make all its objects using the implicit rule.
+library: $(library-objects)
+
+# Conservatively assume that all the objects depend on all the headers.
+$(library-objects): $(library-headers)
+
+# TESTSUITE (NOTE: Currently expects a 32-bit system)
+# Compiling the testsuite.
+testsuite.o: $(library-headers)
+testsuite: testsuite.o $(library-objects)
+ g++ $^ -o $@
+# Extract the expected output from the testsuite source.
+testsuite.expected: testsuite.cc
+ nl -ba -p -s: $< | sed -nre 's,^ +([0-9]+):.*//([^ ]),Line \1: \2,p' >$@
+# Run the testsuite.
+.PHONY: test
+test: testsuite testsuite.expected
+ ./run-testsuite
+testsuite-cleanfiles = \
+ testsuite.o testsuite testsuite.expected \
+ testsuite.out testsuite.err
+
+# The rules below build a program that uses the library. They are preset to
+# build ``sample'' from ``sample.cc''. You can change the name(s) of the
+# source file(s) and program file to build your own program, or you can write
+# your own Makefile.
+
+# Components of the program.
+program = sample
+program-objects = sample.o
+
+# Conservatively assume all the program source files depend on all the library
+# headers. You can change this if it is not the case.
+$(program-objects) : $(library-headers)
+
+# How to link the program. The implicit rule covers individual objects.
+$(program) : $(program-objects) $(library-objects)
+ g++ $^ -o $@
+
+# Delete all generated files we know about.
+clean :
+ rm -f $(library-objects) $(testsuite-cleanfiles) $(program-objects) $(program)
+
+# I removed the *.tag dependency tracking system because it had few advantages
+# over manually entering all the dependencies. If there were a portable,
+# reliable dependency tracking system, I'd use it, but I know of no such;
+# cons and depcomp are almost good enough.
+
+# Come back and define default target.
+all : library $(program)
diff --git a/libs/bigint/NumberlikeArray.hh b/libs/bigint/NumberlikeArray.hh
new file mode 100644
index 00000000..53c8e5be
--- /dev/null
+++ b/libs/bigint/NumberlikeArray.hh
@@ -0,0 +1,177 @@
+#ifndef NUMBERLIKEARRAY_H
+#define NUMBERLIKEARRAY_H
+
+// Make sure we have NULL.
+#ifndef NULL
+#define NULL 0
+#endif
+
+/* A NumberlikeArray<Blk> object holds a heap-allocated array of Blk with a
+ * length and a capacity and provides basic memory management features.
+ * BigUnsigned and BigUnsignedInABase both subclass it.
+ *
+ * NumberlikeArray provides no information hiding. Subclasses should use
+ * nonpublic inheritance and manually expose members as desired using
+ * declarations like this:
+ *
+ * public:
+ * NumberlikeArray< the-type-argument >::getLength;
+ */
+template <class Blk>
+class NumberlikeArray {
+public:
+
+ // Type for the index of a block in the array
+ typedef unsigned int Index;
+ // The number of bits in a block, defined below.
+ static const unsigned int N;
+
+ // The current allocated capacity of this NumberlikeArray (in blocks)
+ Index cap;
+ // The actual length of the value stored in this NumberlikeArray (in blocks)
+ Index len;
+ // Heap-allocated array of the blocks (can be NULL if len == 0)
+ Blk *blk;
+
+ // Constructs a ``zero'' NumberlikeArray with the given capacity.
+ NumberlikeArray(Index c) : cap(c), len(0) {
+ blk = (cap > 0) ? (new Blk[cap]) : NULL;
+ }
+
+ /* Constructs a zero NumberlikeArray without allocating a backing array.
+ * A subclass that doesn't know the needed capacity at initialization
+ * time can use this constructor and then overwrite blk without first
+ * deleting it. */
+ NumberlikeArray() : cap(0), len(0) {
+ blk = NULL;
+ }
+
+ // Destructor. Note that `delete NULL' is a no-op.
+ ~NumberlikeArray() {
+ delete [] blk;
+ }
+
+ /* Ensures that the array has at least the requested capacity; may
+ * destroy the contents. */
+ void allocate(Index c);
+
+ /* Ensures that the array has at least the requested capacity; does not
+ * destroy the contents. */
+ void allocateAndCopy(Index c);
+
+ // Copy constructor
+ NumberlikeArray(const NumberlikeArray<Blk> &x);
+
+ // Assignment operator
+ void operator=(const NumberlikeArray<Blk> &x);
+
+ // Constructor that copies from a given array of blocks
+ NumberlikeArray(const Blk *b, Index blen);
+
+ // ACCESSORS
+ Index getCapacity() const { return cap; }
+ Index getLength() const { return len; }
+ Blk getBlock(Index i) const { return blk[i]; }
+ bool isEmpty() const { return len == 0; }
+
+ /* Equality comparison: checks if both objects have the same length and
+ * equal (==) array elements to that length. Subclasses may wish to
+ * override. */
+ bool operator ==(const NumberlikeArray<Blk> &x) const;
+
+ bool operator !=(const NumberlikeArray<Blk> &x) const {
+ return !operator ==(x);
+ }
+};
+
+/* BEGIN TEMPLATE DEFINITIONS. They are present here so that source files that
+ * include this header file can generate the necessary real definitions. */
+
+template <class Blk>
+const unsigned int NumberlikeArray<Blk>::N = 8 * sizeof(Blk);
+
+template <class Blk>
+void NumberlikeArray<Blk>::allocate(Index c) {
+ // If the requested capacity is more than the current capacity...
+ if (c > cap) {
+ // Delete the old number array
+ delete [] blk;
+ // Allocate the new array
+ cap = c;
+ blk = new Blk[cap];
+ }
+}
+
+template <class Blk>
+void NumberlikeArray<Blk>::allocateAndCopy(Index c) {
+ // If the requested capacity is more than the current capacity...
+ if (c > cap) {
+ Blk *oldBlk = blk;
+ // Allocate the new number array
+ cap = c;
+ blk = new Blk[cap];
+ // Copy number blocks
+ Index i;
+ for (i = 0; i < len; i++)
+ blk[i] = oldBlk[i];
+ // Delete the old array
+ delete [] oldBlk;
+ }
+}
+
+template <class Blk>
+NumberlikeArray<Blk>::NumberlikeArray(const NumberlikeArray<Blk> &x)
+ : len(x.len) {
+ // Create array
+ cap = len;
+ blk = new Blk[cap];
+ // Copy blocks
+ Index i;
+ for (i = 0; i < len; i++)
+ blk[i] = x.blk[i];
+}
+
+template <class Blk>
+void NumberlikeArray<Blk>::operator=(const NumberlikeArray<Blk> &x) {
+ /* Calls like a = a have no effect; catch them before the aliasing
+ * causes a problem */
+ if (this == &x)
+ return;
+ // Copy length
+ len = x.len;
+ // Expand array if necessary
+ allocate(len);
+ // Copy number blocks
+ Index i;
+ for (i = 0; i < len; i++)
+ blk[i] = x.blk[i];
+}
+
+template <class Blk>
+NumberlikeArray<Blk>::NumberlikeArray(const Blk *b, Index blen)
+ : cap(blen), len(blen) {
+ // Create array
+ blk = new Blk[cap];
+ // Copy blocks
+ Index i;
+ for (i = 0; i < len; i++)
+ blk[i] = b[i];
+}
+
+template <class Blk>
+bool NumberlikeArray<Blk>::operator ==(const NumberlikeArray<Blk> &x) const {
+ if (len != x.len)
+ // Definitely unequal.
+ return false;
+ else {
+ // Compare corresponding blocks one by one.
+ Index i;
+ for (i = 0; i < len; i++)
+ if (blk[i] != x.blk[i])
+ return false;
+ // No blocks differed, so the objects are equal.
+ return true;
+ }
+}
+
+#endif
diff --git a/libs/bigint/README b/libs/bigint/README
new file mode 100644
index 00000000..e1842381
--- /dev/null
+++ b/libs/bigint/README
@@ -0,0 +1,81 @@
+
+Note by Clifford Wolf:
+This version of bigint was downloaded at 2012-08-29 from
+https://mattmccutchen.net/bigint/bigint-2010.04.30.tar.bz2
+
+Some minor changes were made to the source code (e.g. "using"
+was added to access declarations to prohibit compiler warnings).
+
+
+==============================================================================
+
+ C++ Big Integer Library
+ (see ChangeLog for version)
+
+ http://mattmccutchen.net/bigint/
+
+ Written and maintained by Matt McCutchen <matt@mattmccutchen.net>
+
+You can use this library in a C++ program to do arithmetic on integers of size
+limited only by your computer's memory. The library provides BigUnsigned and
+BigInteger classes that represent nonnegative integers and signed integers,
+respectively. Most of the C++ arithmetic operators are overloaded for these
+classes, so big-integer calculations are as easy as:
+
+ #include "BigIntegerLibrary.hh"
+
+ BigInteger a = 65536;
+ cout << (a * a * a * a * a * a * a * a);
+
+ (prints 340282366920938463463374607431768211456)
+
+The code in `sample.cc' demonstrates the most important features of the library.
+To get started quickly, read the code and explanations in that file and run it.
+If you want more detail or a feature not shown in `sample.cc', consult the
+consult the actual header and source files, which are thoroughly commented.
+
+This library emphasizes ease of use and clarity of implementation over speed;
+some users will prefer GMP (http://swox.com/gmp/), which is faster. The code is
+intended to be reasonably portable across computers and modern C++ compilers; in
+particular, it uses whatever word size the computer provides (32-bit, 64-bit, or
+otherwise).
+
+Compiling programs that use the library
+---------------------------------------
+The library consists of a folder full of C++ header files (`.hh') and source
+files (`.cc'). Your own programs should `#include' the necessary header files
+and link with the source files. A makefile that builds the sample program
+(`sample.cc') is included; you can adapt it to replace the sample with your own
+program.
+
+Alternatively, you can use your own build system or IDE. In that case, you must
+put the library header files where the compiler will find them and arrange to
+have your program linked with the library source files; otherwise, you will get
+errors about missing header files or "undefined references". To learn how to do
+this, consult the documentation for the build system or IDE; don't bother asking
+me. Adding all the library files to your project will work in many IDEs but may
+not be the most desirable approach.
+
+Resources
+---------
+The library's Web site (above) provides links to released versions, the current
+development version, and a mailing list for release announcements, questions,
+bug reports, and other discussion of the library. I would be delighted to hear
+from you if you like this library and/or find a good use for it.
+
+Bugs and enhancements
+---------------------
+The library has been tested by me and others but is by no means bug-free. If
+you find a bug, please report it, whether it comes in the form of compiling
+trouble, a mathematically inaccurate result, or a memory-management blooper
+(since I use Java, these are altogether too common in my C++). I generally fix
+all reported bugs. You are also welcome to request enhancements, but I am
+unlikely to do substantial amounts of work on enhancements at this point.
+
+Legal
+-----
+I, Matt McCutchen, the sole author of the original Big Integer Library, waive my
+copyright to it, placing it in the public domain. The library comes with
+absolutely no warranty.
+
+~~~~
diff --git a/libs/bigint/run-testsuite b/libs/bigint/run-testsuite
new file mode 100755
index 00000000..ff737291
--- /dev/null
+++ b/libs/bigint/run-testsuite
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+bad=
+
+# If you encounter the following problem with Valgrind like I did:
+# https://bugzilla.redhat.com/show_bug.cgi?id=455644
+# you can pass the environment variable NO_VALGRIND=1 to run the testsuite
+# without it.
+if [ "$NO_VALGRIND" ]; then
+ cmd=(./testsuite)
+else
+ cmd=(valgrind --error-exitcode=1 --leak-check=full ./testsuite)
+fi
+
+set -o pipefail
+# Stdout goes directly to testsuite.out; stderr goes down the pipe.
+if ! "${cmd[@]}" 2>&1 >testsuite.out | tee testsuite.err; then
+ echo >&2 'Memory errors!'
+ bad=1
+fi
+
+if grep 'LEAK SUMMARY' testsuite.err >/dev/null; then
+ echo >&2 'Memory leaks!'
+ bad=1
+fi
+
+if ! diff -u testsuite.expected testsuite.out; then
+ echo >&2 'Output is incorrect!'
+ bad=1
+fi
+
+if [ $bad ]; then
+ echo >&2 'Test suite failed!'
+ exit 1
+else
+ echo 'Test suite passed.'
+fi
diff --git a/libs/bigint/sample.cc b/libs/bigint/sample.cc
new file mode 100644
index 00000000..62b41df3
--- /dev/null
+++ b/libs/bigint/sample.cc
@@ -0,0 +1,125 @@
+// Sample program demonstrating the use of the Big Integer Library.
+
+// Standard libraries
+#include <string>
+#include <iostream>
+
+// `BigIntegerLibrary.hh' includes all of the library headers.
+#include "BigIntegerLibrary.hh"
+
+int main() {
+ /* The library throws `const char *' error messages when things go
+ * wrong. It's a good idea to catch them using a `try' block like this
+ * one. Your C++ compiler might need a command-line option to compile
+ * code that uses exceptions. */
+ try {
+ BigInteger a; // a is 0
+ int b = 535;
+
+ /* Any primitive integer can be converted implicitly to a
+ * BigInteger. */
+ a = b;
+
+ /* The reverse conversion requires a method call (implicit
+ * conversions were previously supported but caused trouble).
+ * If a were too big for an int, the library would throw an
+ * exception. */
+ b = a.toInt();
+
+ BigInteger c(a); // Copy a BigInteger.
+
+ // The int literal is converted to a BigInteger.
+ BigInteger d(-314159265);
+
+ /* This won't compile (at least on 32-bit machines) because the
+ * number is too big to be a primitive integer literal, and
+ * there's no such thing as a BigInteger literal. */
+ //BigInteger e(3141592653589793238462643383279);
+
+ // Instead you can convert the number from a string.
+ std::string s("3141592653589793238462643383279");
+ BigInteger f = stringToBigInteger(s);
+
+ // You can convert the other way too.
+ std::string s2 = bigIntegerToString(f);
+
+ // f is implicitly stringified and sent to std::cout.
+ std::cout << f << std::endl;
+
+ /* Let's do some math! The library overloads most of the
+ * mathematical operators (including assignment operators) to
+ * work on BigIntegers. There are also ``copy-less''
+ * operations; see `BigUnsigned.hh' for details. */
+
+ // Arithmetic operators
+ BigInteger g(314159), h(265);
+ std::cout << (g + h) << '\n'
+ << (g - h) << '\n'
+ << (g * h) << '\n'
+ << (g / h) << '\n'
+ << (g % h) << std::endl;
+
+ // Bitwise operators
+ BigUnsigned i(0xFF0000FF), j(0x0000FFFF);
+ // The library's << operator recognizes base flags.
+ std::cout.flags(std::ios::hex | std::ios::showbase);
+ std::cout << (i & j) << '\n'
+ << (i | j) << '\n'
+ << (i ^ j) << '\n'
+ // Shift distances are ordinary unsigned ints.
+ << (j << 21) << '\n'
+ << (j >> 10) << '\n';
+ std::cout.flags(std::ios::dec);
+
+ // Let's do some heavy lifting and calculate powers of 314.
+ int maxPower = 10;
+ BigUnsigned x(1), big314(314);
+ for (int power = 0; power <= maxPower; power++) {
+ std::cout << "314^" << power << " = " << x << std::endl;
+ x *= big314; // A BigInteger assignment operator
+ }
+
+ // Some big-integer algorithms (albeit on small integers).
+ std::cout << gcd(BigUnsigned(60), 72) << '\n'
+ << modinv(BigUnsigned(7), 11) << '\n'
+ << modexp(BigUnsigned(314), 159, 2653) << std::endl;
+
+ // Add your own code here to experiment with the library.
+ } catch(char const* err) {
+ std::cout << "The library threw an exception:\n"
+ << err << std::endl;
+ }
+
+ return 0;
+}
+
+/*
+The original sample program produces this output:
+
+3141592653589793238462643383279
+314424
+313894
+83252135
+1185
+134
+0xFF
+0xFF00FFFF
+0xFF00FF00
+0x1FFFE00000
+0x3F
+314^0 = 1
+314^1 = 314
+314^2 = 98596
+314^3 = 30959144
+314^4 = 9721171216
+314^5 = 3052447761824
+314^6 = 958468597212736
+314^7 = 300959139524799104
+314^8 = 94501169810786918656
+314^9 = 29673367320587092457984
+314^10 = 9317437338664347031806976
+12
+8
+1931
+
+*/
diff --git a/libs/bigint/testsuite.cc b/libs/bigint/testsuite.cc
new file mode 100644
index 00000000..7cb9768e
--- /dev/null
+++ b/libs/bigint/testsuite.cc
@@ -0,0 +1,326 @@
+/* Test suite for the library. First, it ``tests'' that all the constructs it
+ * uses compile successfully. Then, its output to stdout is compared to the
+ * expected output automatically extracted from slash-slash comments below.
+ *
+ * NOTE: For now, the test suite expects a 32-bit system. On others, some tests
+ * may fail, and it may be ineffective at catching bugs. TODO: Remedy this. */
+
+#include "BigIntegerLibrary.hh"
+
+#include <string>
+#include <iostream>
+using namespace std;
+
+// Evaluate expr and print the result or "error" as appropriate.
+#define TEST(expr) do {\
+ cout << "Line " << __LINE__ << ": ";\
+ try {\
+ cout << (expr);\
+ } catch (const char *err) {\
+ cout << "error";\
+ }\
+ cout << endl;\
+} while (0)
+
+const BigUnsigned &check(const BigUnsigned &x) {
+ unsigned int l = x.getLength();
+ if (l != 0 && x.getBlock(l-1) == 0)
+ cout << "check: Unzapped number!" << endl;
+ if (l > x.getCapacity())
+ cout << "check: Capacity inconsistent with length!" << endl;
+ return x;
+}
+
+const BigInteger &check(const BigInteger &x) {
+ if (x.getSign() == 0 && !x.getMagnitude().isZero())
+ cout << "check: Sign should not be zero!" << endl;
+ if (x.getSign() != 0 && x.getMagnitude().isZero())
+ cout << "check: Sign should be zero!" << endl;
+ check(x.getMagnitude());
+ return x;
+}
+
+short pathologicalShort = ~((unsigned short)(~0) >> 1);
+int pathologicalInt = ~((unsigned int)(~0) >> 1);
+long pathologicalLong = ~((unsigned long)(~0) >> 1);
+
+int main() {
+
+try {
+
+BigUnsigned z(0), one(1), ten(10);
+TEST(z); //0
+TEST(1); //1
+TEST(10); //10
+
+// TODO: Comprehensively test the general and special cases of each function.
+
+// === Default constructors ===
+
+TEST(check(BigUnsigned())); //0
+TEST(check(BigInteger())); //0
+
+// === Block-array constructors ===
+
+BigUnsigned::Blk myBlocks[3];
+myBlocks[0] = 3;
+myBlocks[1] = 4;
+myBlocks[2] = 0;
+BigUnsigned bu(myBlocks, 3);
+TEST(check(bu)); //17179869187
+TEST(check(BigInteger(myBlocks, 3))); //17179869187
+TEST(check(BigInteger(bu ))); //17179869187
+
+// For nonzero magnitude, reject zero and invalid signs.
+TEST(check(BigInteger(myBlocks, 3, BigInteger::positive))); //17179869187
+TEST(check(BigInteger(myBlocks, 3, BigInteger::negative))); //-17179869187
+TEST(check(BigInteger(myBlocks, 3, BigInteger::zero ))); //error
+TEST(check(BigInteger(bu, BigInteger::positive))); //17179869187
+TEST(check(BigInteger(bu, BigInteger::negative))); //-17179869187
+TEST(check(BigInteger(bu, BigInteger::zero ))); //error
+
+// For zero magnitude, force the sign to zero without error.
+BigUnsigned::Blk myZeroBlocks[1];
+myZeroBlocks[0] = 0;
+TEST(check(BigInteger(myZeroBlocks, 1, BigInteger::positive))); //0
+TEST(check(BigInteger(myZeroBlocks, 1, BigInteger::negative))); //0
+TEST(check(BigInteger(myZeroBlocks, 1, BigInteger::zero ))); //0
+
+// === BigUnsigned conversion limits ===
+
+TEST(BigUnsigned(0).toUnsignedLong()); //0
+TEST(BigUnsigned(4294967295U).toUnsignedLong()); //4294967295
+TEST(stringToBigUnsigned("4294967296").toUnsignedLong()); //error
+
+TEST(BigUnsigned(0).toLong()); //0
+TEST(BigUnsigned(2147483647).toLong()); //2147483647
+TEST(BigUnsigned(2147483648U).toLong()); //error
+
+// int is the same as long on a 32-bit system
+TEST(BigUnsigned(0).toUnsignedInt()); //0
+TEST(BigUnsigned(4294967295U).toUnsignedInt()); //4294967295
+TEST(stringToBigUnsigned("4294967296").toUnsignedInt()); //error
+
+TEST(BigUnsigned(0).toInt()); //0
+TEST(BigUnsigned(2147483647).toInt()); //2147483647
+TEST(BigUnsigned(2147483648U).toInt()); //error
+
+TEST(BigUnsigned(0).toUnsignedShort()); //0
+TEST(BigUnsigned(65535).toUnsignedShort()); //65535
+TEST(BigUnsigned(65536).toUnsignedShort()); //error
+
+TEST(BigUnsigned(0).toShort()); //0
+TEST(BigUnsigned(32767).toShort()); //32767
+TEST(BigUnsigned(32768).toShort()); //error
+
+// === BigInteger conversion limits ===
+
+TEST(BigInteger(-1).toUnsignedLong()); //error
+TEST(BigInteger(0).toUnsignedLong()); //0
+TEST(BigInteger(4294967295U).toUnsignedLong()); //4294967295
+TEST(stringToBigInteger("4294967296").toUnsignedLong()); //error
+
+TEST(stringToBigInteger("-2147483649").toLong()); //error
+TEST(stringToBigInteger("-2147483648").toLong()); //-2147483648
+TEST(BigInteger(-2147483647).toLong()); //-2147483647
+TEST(BigInteger(0).toLong()); //0
+TEST(BigInteger(2147483647).toLong()); //2147483647
+TEST(BigInteger(2147483648U).toLong()); //error
+
+// int is the same as long on a 32-bit system
+TEST(BigInteger(-1).toUnsignedInt()); //error
+TEST(BigInteger(0).toUnsignedInt()); //0
+TEST(BigInteger(4294967295U).toUnsignedInt()); //4294967295
+TEST(stringToBigInteger("4294967296").toUnsignedInt()); //error
+
+TEST(stringToBigInteger("-2147483649").toInt()); //error
+TEST(stringToBigInteger("-2147483648").toInt()); //-2147483648
+TEST(BigInteger(-2147483647).toInt()); //-2147483647
+TEST(BigInteger(0).toInt()); //0
+TEST(BigInteger(2147483647).toInt()); //2147483647
+TEST(BigInteger(2147483648U).toInt()); //error
+
+TEST(BigInteger(-1).toUnsignedShort()); //error
+TEST(BigInteger(0).toUnsignedShort()); //0
+TEST(BigInteger(65535).toUnsignedShort()); //65535
+TEST(BigInteger(65536).toUnsignedShort()); //error
+
+TEST(BigInteger(-32769).toShort()); //error
+TEST(BigInteger(-32768).toShort()); //-32768
+TEST(BigInteger(-32767).toShort()); //-32767
+TEST(BigInteger(0).toShort()); //0
+TEST(BigInteger(32767).toShort()); //32767
+TEST(BigInteger(32768).toShort()); //error
+
+// === Negative BigUnsigneds ===
+
+// ...during construction
+TEST(BigUnsigned(short(-1))); //error
+TEST(BigUnsigned(pathologicalShort)); //error
+TEST(BigUnsigned(-1)); //error
+TEST(BigUnsigned(pathologicalInt)); //error
+TEST(BigUnsigned(long(-1))); //error
+TEST(BigUnsigned(pathologicalLong)); //error
+
+// ...during subtraction
+TEST(BigUnsigned(5) - BigUnsigned(6)); //error
+TEST(stringToBigUnsigned("314159265358979323") - stringToBigUnsigned("314159265358979324")); //error
+TEST(check(BigUnsigned(5) - BigUnsigned(5))); //0
+TEST(check(stringToBigUnsigned("314159265358979323") - stringToBigUnsigned("314159265358979323"))); //0
+TEST(check(stringToBigUnsigned("4294967296") - BigUnsigned(1))); //4294967295
+
+// === BigUnsigned addition ===
+
+TEST(check(BigUnsigned(0) + 0)); //0
+TEST(check(BigUnsigned(0) + 1)); //1
+// Ordinary carry
+TEST(check(stringToBigUnsigned("8589934591" /* 2^33 - 1*/)
+ + stringToBigUnsigned("4294967298" /* 2^32 + 2 */))); //12884901889
+// Creation of a new block
+TEST(check(BigUnsigned(0xFFFFFFFFU) + 1)); //4294967296
+
+// === BigUnsigned subtraction ===
+
+TEST(check(BigUnsigned(1) - 0)); //1
+TEST(check(BigUnsigned(1) - 1)); //0
+TEST(check(BigUnsigned(2) - 1)); //1
+// Ordinary borrow
+TEST(check(stringToBigUnsigned("12884901889")
+ - stringToBigUnsigned("4294967298"))); //8589934591
+// Borrow that removes a block
+TEST(check(stringToBigUnsigned("4294967296") - 1)); //4294967295
+
+// === BigUnsigned multiplication and division ===
+
+BigUnsigned a = check(BigUnsigned(314159265) * 358979323);
+TEST(a); //112776680263877595
+TEST(a / 123); //916883579381118
+TEST(a % 123); //81
+
+TEST(BigUnsigned(5) / 0); //error
+
+// === Block accessors ===
+
+BigUnsigned b;
+TEST(b); //0
+TEST(b.getBlock(0)); //0
+b.setBlock(1, 314);
+// Did b grow properly? And did we zero intermediate blocks?
+TEST(check(b)); //1348619730944
+TEST(b.getLength()); //2
+TEST(b.getBlock(0)); //0
+TEST(b.getBlock(1)); //314
+// Did b shrink properly?
+b.setBlock(1, 0);
+TEST(check(b)); //0
+
+BigUnsigned bb(314);
+bb.setBlock(1, 159);
+// Make sure we used allocateAndCopy, not allocate
+TEST(bb.getBlock(0)); //314
+TEST(bb.getBlock(1)); //159
+// Blocks beyond the number should be zero regardless of whether they are
+// within the capacity.
+bb.add(1, 2);
+TEST(bb.getBlock(0)); //3
+TEST(bb.getBlock(1)); //0
+TEST(bb.getBlock(2)); //0
+TEST(bb.getBlock(314159)); //0
+
+// === Bit accessors ===
+
+TEST(BigUnsigned(0).bitLength()); //0
+TEST(BigUnsigned(1).bitLength()); //1
+TEST(BigUnsigned(4095).bitLength()); //12
+TEST(BigUnsigned(4096).bitLength()); //13
+// 5 billion is between 2^32 (about 4 billion) and 2^33 (about 8 billion).
+TEST(stringToBigUnsigned("5000000000").bitLength()); //33
+
+// 25 is binary 11001.
+BigUnsigned bbb(25);
+TEST(bbb.getBit(4)); //1
+TEST(bbb.getBit(3)); //1
+TEST(bbb.getBit(2)); //0
+TEST(bbb.getBit(1)); //0
+TEST(bbb.getBit(0)); //1
+TEST(bbb.bitLength()); //5
+// Effectively add 2^32.
+bbb.setBit(32, true);
+TEST(bbb); //4294967321
+bbb.setBit(31, true);
+bbb.setBit(32, false);
+TEST(check(bbb)); //2147483673
+
+// === Combining BigUnsigned, BigInteger, and primitive integers ===
+
+BigUnsigned p1 = BigUnsigned(3) * 5;
+TEST(p1); //15
+/* In this case, we would like g++ to implicitly promote the BigUnsigned to a
+ * BigInteger, but it seems to prefer converting the -5 to a BigUnsigned, which
+ * causes an error. If I take out constructors for BigUnsigned from signed
+ * primitive integers, the BigUnsigned(3) becomes ambiguous, and if I take out
+ * all the constructors but BigUnsigned(unsigned long), g++ uses that
+ * constructor and gets a wrong (positive) answer. Thus, I think we'll just
+ * have to live with this cast. */
+BigInteger p2 = BigInteger(BigUnsigned(3)) * -5;
+TEST(p2); //-15
+
+// === Test some previous bugs ===
+
+{
+ /* Test that BigInteger division sets the sign to zero.
+ * Bug reported by David Allen. */
+ BigInteger num(3), denom(5), quotient;
+ num.divideWithRemainder(denom, quotient);
+ check(quotient);
+ num = 5;
+ num.divideWithRemainder(denom, quotient);
+ check(num);
+}
+
+{
+ /* Test that BigInteger subtraction sets the sign properly.
+ * Bug reported by Samuel Larkin. */
+ BigInteger zero(0), three(3), ans;
+ ans = zero - three;
+ TEST(check(ans).getSign()); //-1
+}
+
+{
+ /* Test that BigInteger multiplication shifts bits properly on systems
+ * where long is bigger than int. (Obviously, this would only catch the
+ * bug when run on such a system.)
+ * Bug reported by Mohand Mezmaz. */
+ BigInteger f=4; f*=3;
+ TEST(check(f)); //12
+}
+
+{
+ /* Test that bitwise XOR allocates the larger length.
+ * Bug reported by Sriram Sankararaman. */
+ BigUnsigned a(0), b(3), ans;
+ ans = a ^ b;
+ TEST(ans); //3
+}
+
+{
+ /* Test that an aliased multiplication works.
+ * Bug reported by Boris Dessy. */
+ BigInteger num(5);
+ num *= num;
+ TEST(check(num)); //25
+}
+
+{
+ /* Test that BigUnsignedInABase(std::string) constructor rejects digits
+ * too big for the specified base.
+ * Bug reported by Niakam Kazemi. */
+ TEST(BigUnsignedInABase("f", 10)); //error
+}
+
+} catch (const char *err) {
+ cout << "UNCAUGHT ERROR: " << err << endl;
+}
+
+return 0;
+}
diff --git a/libs/ezsat/.gitignore b/libs/ezsat/.gitignore
new file mode 100644
index 00000000..e079bd09
--- /dev/null
+++ b/libs/ezsat/.gitignore
@@ -0,0 +1,5 @@
+demo_bit
+demo_cmp
+demo_vec
+puzzle3d
+testbench
diff --git a/libs/ezsat/Makefile b/libs/ezsat/Makefile
new file mode 100644
index 00000000..b1f86416
--- /dev/null
+++ b/libs/ezsat/Makefile
@@ -0,0 +1,30 @@
+
+CC = clang
+CXX = clang
+CXXFLAGS = -MD -Wall -Wextra -ggdb
+CXXFLAGS += -std=c++11 -O0
+LDLIBS = ../minisat/Options.cc ../minisat/SimpSolver.cc ../minisat/Solver.cc ../minisat/System.cc -lm -lstdc++
+
+
+all: demo_vec demo_bit demo_cmp testbench puzzle3d
+
+demo_vec: demo_vec.o ezsat.o ezminisat.o
+demo_bit: demo_bit.o ezsat.o ezminisat.o
+demo_cmp: demo_cmp.o ezsat.o ezminisat.o
+testbench: testbench.o ezsat.o ezminisat.o
+puzzle3d: puzzle3d.o ezsat.o ezminisat.o
+
+test: all
+ ./testbench
+ ./demo_bit
+ ./demo_vec
+ # ./demo_cmp
+ # ./puzzle3d
+
+clean:
+ rm -f demo_bit demo_vec demo_cmp testbench puzzle3d *.o *.d
+
+.PHONY: all test clean
+
+-include *.d
+
diff --git a/libs/ezsat/README b/libs/ezsat/README
new file mode 100644
index 00000000..c6745e6c
--- /dev/null
+++ b/libs/ezsat/README
@@ -0,0 +1,29 @@
+
+ **************************************************************************
+ * *
+ * The ezSAT C++11 library *
+ * *
+ * A simple frontend to SAT solvers with bindings to MiniSAT. *
+ * by Clifford Wolf *
+ * *
+ **************************************************************************
+
+============
+Introduction
+============
+
+This library acts as a frontend to SAT solvers and a helper for generating
+CNF for sat solvers. It comes with bindings for MiniSAT (http://minisat.se/).
+
+Have a look at demo_bit.cc and demo_vec.cc for examples of how to set up
+a SAT problem using ezSAT. Have a look at puzzle3d.cc for a more complex
+(real-world) example of using ezSAT.
+
+
+C++11 Warning
+-------------
+
+This project is written in C++11. Use appropriate compiler switches to compile
+it. Tested with clang version 3.0 and option -std=c++11. Also tested with gcc
+version 4.6.3 and option -std=c++0x.
+
diff --git a/libs/ezsat/demo_bit.cc b/libs/ezsat/demo_bit.cc
new file mode 100644
index 00000000..c7b11246
--- /dev/null
+++ b/libs/ezsat/demo_bit.cc
@@ -0,0 +1,71 @@
+/*
+ * ezSAT -- A simple and easy to use CNF generator for SAT solvers
+ *
+ * Copyright (C) 2013 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 "ezminisat.h"
+#include <stdio.h>
+
+void print_results(bool satisfiable, const std::vector<bool> &modelValues)
+{
+ if (!satisfiable) {
+ printf("not satisfiable.\n\n");
+ } else {
+ printf("satisfiable:");
+ for (auto val : modelValues)
+ printf(" %d", val ? 1 : 0);
+ printf("\n\n");
+ }
+}
+
+int main()
+{
+ ezMiniSAT sat;
+
+ // 3 input AOI-Gate
+ // 'pos_active' encodes the condition under which the pullup path of the gate is active
+ // 'neg_active' encodes the condition under which the pulldown path of the gate is active
+ // 'impossible' encodes the condition that both or none of the above paths is active
+ int pos_active = sat.AND(sat.NOT("A"), sat.OR(sat.NOT("B"), sat.NOT("C")));
+ int neg_active = sat.OR("A", sat.AND("B", "C"));
+ int impossible = sat.IFF(pos_active, neg_active);
+
+ std::vector<int> modelVars;
+ std::vector<bool> modelValues;
+ bool satisfiable;
+
+ modelVars.push_back(sat.VAR("A"));
+ modelVars.push_back(sat.VAR("B"));
+ modelVars.push_back(sat.VAR("C"));
+
+ printf("\n");
+
+ printf("pos_active: %s\n", sat.to_string(pos_active).c_str());
+ satisfiable = sat.solve(modelVars, modelValues, pos_active);
+ print_results(satisfiable, modelValues);
+
+ printf("neg_active: %s\n", sat.to_string(neg_active).c_str());
+ satisfiable = sat.solve(modelVars, modelValues, neg_active);
+ print_results(satisfiable, modelValues);
+
+ printf("impossible: %s\n", sat.to_string(impossible).c_str());
+ satisfiable = sat.solve(modelVars, modelValues, impossible);
+ print_results(satisfiable, modelValues);
+
+ return 0;
+}
+
diff --git a/libs/ezsat/demo_cmp.cc b/libs/ezsat/demo_cmp.cc
new file mode 100644
index 00000000..8d7ceb2b
--- /dev/null
+++ b/libs/ezsat/demo_cmp.cc
@@ -0,0 +1,146 @@
+/*
+ * ezSAT -- A simple and easy to use CNF generator for SAT solvers
+ *
+ * Copyright (C) 2013 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 "ezminisat.h"
+#include <assert.h>
+
+#define INIT_X 123456789
+#define INIT_Y 362436069
+#define INIT_Z 521288629
+#define INIT_W 88675123
+
+uint32_t xorshift128() {
+ static uint32_t x = INIT_X;
+ static uint32_t y = INIT_Y;
+ static uint32_t z = INIT_Z;
+ static uint32_t w = INIT_W;
+ uint32_t t = x ^ (x << 11);
+ x = y; y = z; z = w;
+ w ^= (w >> 19) ^ t ^ (t >> 8);
+ return w;
+}
+
+void test_cmp(uint32_t a, uint32_t b)
+{
+ ezMiniSAT sat;
+
+ printf("A = %10u (%10d)\n", a, int32_t(a));
+ printf("B = %10u (%10d)\n", b, int32_t(b));
+ printf("\n");
+
+ std::vector<int> va = sat.vec_var("a", 32);
+ std::vector<int> vb = sat.vec_var("b", 32);
+
+ sat.vec_set_unsigned(va, a);
+ sat.vec_set_unsigned(vb, b);
+
+#define MONITOR_VARS \
+ X(carry) X(overflow) X(sign) X(zero) \
+ X(lt_signed) X(le_signed) X(ge_signed) X(gt_signed) \
+ X(lt_unsigned) X(le_unsigned) X(ge_unsigned) X(gt_unsigned)
+
+#define X(_n) int _n; bool _n ## _master;
+ MONITOR_VARS
+#undef X
+
+ carry_master = ((uint64_t(a) - uint64_t(b)) >> 32) & 1;
+ overflow_master = (int32_t(a) - int32_t(b)) != (int64_t(int32_t(a)) - int64_t(int32_t(b)));
+ sign_master = ((a - b) >> 31) & 1;
+ zero_master = a == b;
+
+ sat.vec_cmp(va, vb, carry, overflow, sign, zero);
+
+ lt_signed_master = int32_t(a) < int32_t(b);
+ le_signed_master = int32_t(a) <= int32_t(b);
+ ge_signed_master = int32_t(a) >= int32_t(b);
+ gt_signed_master = int32_t(a) > int32_t(b);
+
+ lt_unsigned_master = a < b;
+ le_unsigned_master = a <= b;
+ ge_unsigned_master = a >= b;
+ gt_unsigned_master = a > b;
+
+ lt_signed = sat.vec_lt_signed(va, vb);
+ le_signed = sat.vec_le_signed(va, vb);
+ ge_signed = sat.vec_ge_signed(va, vb);
+ gt_signed = sat.vec_gt_signed(va, vb);
+
+ lt_unsigned = sat.vec_lt_unsigned(va, vb);
+ le_unsigned = sat.vec_le_unsigned(va, vb);
+ ge_unsigned = sat.vec_ge_unsigned(va, vb);
+ gt_unsigned = sat.vec_gt_unsigned(va, vb);
+
+ std::vector<int> modelExpressions;
+ std::vector<bool> modelValues, modelMaster;
+ std::vector<std::string> modelNames;
+
+#define X(_n) modelExpressions.push_back(_n); modelNames.push_back(#_n); modelMaster.push_back(_n ## _master);
+ MONITOR_VARS
+#undef X
+
+ std::vector<int> add_ab = sat.vec_add(va, vb);
+ std::vector<int> sub_ab = sat.vec_sub(va, vb);
+ std::vector<int> sub_ba = sat.vec_sub(vb, va);
+
+ sat.vec_append(modelExpressions, add_ab);
+ sat.vec_append(modelExpressions, sub_ab);
+ sat.vec_append(modelExpressions, sub_ba);
+
+ if (!sat.solve(modelExpressions, modelValues)) {
+ fprintf(stderr, "SAT solver failed to find a model!\n");
+ abort();
+ }
+
+ bool found_error = false;
+
+ for (size_t i = 0; i < modelMaster.size(); i++) {
+ if (modelMaster.at(i) != int(modelValues.at(i)))
+ found_error = true;
+ printf("%-20s %d%s\n", modelNames.at(i).c_str(), int(modelValues.at(i)),
+ modelMaster.at(i) != modelValues.at(i) ? " !!!" : "");
+ }
+ printf("\n");
+
+ uint32_t add_ab_value = sat.vec_model_get_unsigned(modelExpressions, modelValues, add_ab);
+ uint32_t sub_ab_value = sat.vec_model_get_unsigned(modelExpressions, modelValues, sub_ab);
+ uint32_t sub_ba_value = sat.vec_model_get_unsigned(modelExpressions, modelValues, sub_ba);
+
+ printf("%-20s %10u %10u%s\n", "result(a+b)", add_ab_value, a+b, add_ab_value != a+b ? " !!!" : "");
+ printf("%-20s %10u %10u%s\n", "result(a-b)", sub_ab_value, a-b, sub_ab_value != a-b ? " !!!" : "");
+ printf("%-20s %10u %10u%s\n", "result(b-a)", sub_ba_value, b-a, sub_ba_value != b-a ? " !!!" : "");
+ printf("\n");
+
+ if (found_error || add_ab_value != a+b || sub_ab_value != a-b || sub_ba_value != b-a)
+ abort();
+}
+
+int main()
+{
+ printf("\n");
+ for (int i = 0; i < 1024; i++) {
+ printf("************** %d **************\n\n", i);
+ uint32_t a = xorshift128();
+ uint32_t b = xorshift128();
+ if (xorshift128() % 16 == 0)
+ a = b;
+ test_cmp(a, b);
+ }
+ return 0;
+}
+
diff --git a/libs/ezsat/demo_vec.cc b/libs/ezsat/demo_vec.cc
new file mode 100644
index 00000000..eb8d7599
--- /dev/null
+++ b/libs/ezsat/demo_vec.cc
@@ -0,0 +1,112 @@
+/*
+ * ezSAT -- A simple and easy to use CNF generator for SAT solvers
+ *
+ * Copyright (C) 2013 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 "ezminisat.h"
+#include <assert.h>
+
+#define INIT_X 123456789
+#define INIT_Y 362436069
+#define INIT_Z 521288629
+#define INIT_W 88675123
+
+uint32_t xorshift128() {
+ static uint32_t x = INIT_X;
+ static uint32_t y = INIT_Y;
+ static uint32_t z = INIT_Z;
+ static uint32_t w = INIT_W;
+ uint32_t t = x ^ (x << 11);
+ x = y; y = z; z = w;
+ w ^= (w >> 19) ^ t ^ (t >> 8);
+ return w;
+}
+
+void xorshift128_sat(ezSAT &sat, std::vector<int> &x, std::vector<int> &y, std::vector<int> &z, std::vector<int> &w)
+{
+ std::vector<int> t = sat.vec_xor(x, sat.vec_shl(x, 11));
+ x = y; y = z; z = w;
+ w = sat.vec_xor(sat.vec_xor(w, sat.vec_shr(w, 19)), sat.vec_xor(t, sat.vec_shr(t, 8)));
+}
+
+void find_xorshift128_init_state(uint32_t &x, uint32_t &y, uint32_t &z, uint32_t &w, uint32_t w1, uint32_t w2, uint32_t w3, uint32_t w4)
+{
+ ezMiniSAT sat;
+
+ std::vector<int> vx = sat.vec_var("x", 32);
+ std::vector<int> vy = sat.vec_var("y", 32);
+ std::vector<int> vz = sat.vec_var("z", 32);
+ std::vector<int> vw = sat.vec_var("w", 32);
+
+ xorshift128_sat(sat, vx, vy, vz, vw);
+ sat.vec_set_unsigned(vw, w1);
+
+ xorshift128_sat(sat, vx, vy, vz, vw);
+ sat.vec_set_unsigned(vw, w2);
+
+ xorshift128_sat(sat, vx, vy, vz, vw);
+ sat.vec_set_unsigned(vw, w3);
+
+ xorshift128_sat(sat, vx, vy, vz, vw);
+ sat.vec_set_unsigned(vw, w4);
+
+ std::vector<int> modelExpressions;
+ std::vector<bool> modelValues;
+
+ sat.vec_append(modelExpressions, sat.vec_var("x", 32));
+ sat.vec_append(modelExpressions, sat.vec_var("y", 32));
+ sat.vec_append(modelExpressions, sat.vec_var("z", 32));
+ sat.vec_append(modelExpressions, sat.vec_var("w", 32));
+
+ // sat.printDIMACS(stdout);
+
+ if (!sat.solve(modelExpressions, modelValues)) {
+ fprintf(stderr, "SAT solver failed to find a model!\n");
+ abort();
+ }
+
+ x = sat.vec_model_get_unsigned(modelExpressions, modelValues, sat.vec_var("x", 32));
+ y = sat.vec_model_get_unsigned(modelExpressions, modelValues, sat.vec_var("y", 32));
+ z = sat.vec_model_get_unsigned(modelExpressions, modelValues, sat.vec_var("z", 32));
+ w = sat.vec_model_get_unsigned(modelExpressions, modelValues, sat.vec_var("w", 32));
+}
+
+int main()
+{
+ uint32_t w1 = xorshift128();
+ uint32_t w2 = xorshift128();
+ uint32_t w3 = xorshift128();
+ uint32_t w4 = xorshift128();
+ uint32_t x, y, z, w;
+
+ printf("\n");
+
+ find_xorshift128_init_state(x, y, z, w, w1, w2, w3, w4);
+
+ printf("x = %9u (%s)\n", (unsigned int)x, x == INIT_X ? "ok" : "ERROR");
+ printf("y = %9u (%s)\n", (unsigned int)y, y == INIT_Y ? "ok" : "ERROR");
+ printf("z = %9u (%s)\n", (unsigned int)z, z == INIT_Z ? "ok" : "ERROR");
+ printf("w = %9u (%s)\n", (unsigned int)w, w == INIT_W ? "ok" : "ERROR");
+
+ if (x != INIT_X || y != INIT_Y || z != INIT_Z || w != INIT_W)
+ abort();
+
+ printf("\n");
+
+ return 0;
+}
+
diff --git a/libs/ezsat/ezminisat.cc b/libs/ezsat/ezminisat.cc
new file mode 100644
index 00000000..e0ee6292
--- /dev/null
+++ b/libs/ezsat/ezminisat.cc
@@ -0,0 +1,243 @@
+/*
+ * ezSAT -- A simple and easy to use CNF generator for SAT solvers
+ *
+ * Copyright (C) 2013 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.
+ *
+ */
+
+// needed for MiniSAT headers (see Minisat Makefile)
+#define __STDC_LIMIT_MACROS
+#define __STDC_FORMAT_MACROS
+
+#include "ezminisat.h"
+
+#include <limits.h>
+#include <stdint.h>
+#include <csignal>
+#include <cinttypes>
+
+#ifndef _WIN32
+# include <unistd.h>
+#endif
+
+#include "../minisat/Solver.h"
+#include "../minisat/SimpSolver.h"
+
+ezMiniSAT::ezMiniSAT() : minisatSolver(NULL)
+{
+ minisatSolver = NULL;
+ foundContradiction = false;
+
+ freeze(CONST_TRUE);
+ freeze(CONST_FALSE);
+}
+
+ezMiniSAT::~ezMiniSAT()
+{
+ if (minisatSolver != NULL)
+ delete minisatSolver;
+}
+
+void ezMiniSAT::clear()
+{
+ if (minisatSolver != NULL) {
+ delete minisatSolver;
+ minisatSolver = NULL;
+ }
+ foundContradiction = false;
+ minisatVars.clear();
+#if EZMINISAT_SIMPSOLVER && EZMINISAT_INCREMENTAL
+ cnfFrozenVars.clear();
+#endif
+ ezSAT::clear();
+}
+
+#if EZMINISAT_SIMPSOLVER && EZMINISAT_INCREMENTAL
+void ezMiniSAT::freeze(int id)
+{
+ if (!mode_non_incremental())
+ cnfFrozenVars.insert(bind(id));
+}
+
+bool ezMiniSAT::eliminated(int idx)
+{
+ idx = idx < 0 ? -idx : idx;
+ if (minisatSolver != NULL && idx > 0 && idx <= int(minisatVars.size()))
+ return minisatSolver->isEliminated(minisatVars.at(idx-1));
+ return false;
+}
+#endif
+
+#ifndef _WIN32
+ezMiniSAT *ezMiniSAT::alarmHandlerThis = NULL;
+clock_t ezMiniSAT::alarmHandlerTimeout = 0;
+
+void ezMiniSAT::alarmHandler(int)
+{
+ if (clock() > alarmHandlerTimeout) {
+ alarmHandlerThis->minisatSolver->interrupt();
+ alarmHandlerTimeout = 0;
+ } else
+ alarm(1);
+}
+#endif
+
+bool ezMiniSAT::solver(const std::vector<int> &modelExpressions, std::vector<bool> &modelValues, const std::vector<int> &assumptions)
+{
+ preSolverCallback();
+
+ solverTimoutStatus = false;
+
+ if (0) {
+contradiction:
+ delete minisatSolver;
+ minisatSolver = NULL;
+ minisatVars.clear();
+ foundContradiction = true;
+ return false;
+ }
+
+ if (foundContradiction) {
+ consumeCnf();
+ return false;
+ }
+
+ std::vector<int> extraClauses, modelIdx;
+
+ for (auto id : assumptions)
+ extraClauses.push_back(bind(id));
+ for (auto id : modelExpressions)
+ modelIdx.push_back(bind(id));
+
+ if (minisatSolver == NULL) {
+ minisatSolver = new Solver;
+ minisatSolver->verbosity = EZMINISAT_VERBOSITY;
+ }
+
+#if EZMINISAT_INCREMENTAL
+ std::vector<std::vector<int>> cnf;
+ consumeCnf(cnf);
+#else
+ const std::vector<std::vector<int>> &cnf = this->cnf();
+#endif
+
+ while (int(minisatVars.size()) < numCnfVariables())
+ minisatVars.push_back(minisatSolver->newVar());
+
+#if EZMINISAT_SIMPSOLVER && EZMINISAT_INCREMENTAL
+ for (auto idx : cnfFrozenVars)
+ minisatSolver->setFrozen(minisatVars.at(idx > 0 ? idx-1 : -idx-1), true);
+ cnfFrozenVars.clear();
+#endif
+
+ for (auto &clause : cnf) {
+ Minisat::vec<Minisat::Lit> ps;
+ for (auto idx : clause) {
+ if (idx > 0)
+ ps.push(Minisat::mkLit(minisatVars.at(idx-1)));
+ else
+ ps.push(Minisat::mkLit(minisatVars.at(-idx-1), true));
+#if EZMINISAT_SIMPSOLVER
+ if (minisatSolver->isEliminated(minisatVars.at(idx > 0 ? idx-1 : -idx-1))) {
+ fprintf(stderr, "Assert in %s:%d failed! Missing call to ezsat->freeze(): %s (lit=%d)\n",
+ __FILE__, __LINE__, cnfLiteralInfo(idx).c_str(), idx);
+ abort();
+ }
+#endif
+ }
+ if (!minisatSolver->addClause(ps))
+ goto contradiction;
+ }
+
+ if (cnf.size() > 0 && !minisatSolver->simplify())
+ goto contradiction;
+
+ Minisat::vec<Minisat::Lit> assumps;
+
+ for (auto idx : extraClauses) {
+ if (idx > 0)
+ assumps.push(Minisat::mkLit(minisatVars.at(idx-1)));
+ else
+ assumps.push(Minisat::mkLit(minisatVars.at(-idx-1), true));
+#if EZMINISAT_SIMPSOLVER
+ if (minisatSolver->isEliminated(minisatVars.at(idx > 0 ? idx-1 : -idx-1))) {
+ fprintf(stderr, "Assert in %s:%d failed! Missing call to ezsat->freeze(): %s\n", __FILE__, __LINE__, cnfLiteralInfo(idx).c_str());
+ abort();
+ }
+#endif
+ }
+
+#ifndef _WIN32
+ struct sigaction sig_action;
+ struct sigaction old_sig_action;
+ int old_alarm_timeout = 0;
+
+ if (solverTimeout > 0) {
+ sig_action.sa_handler = alarmHandler;
+ sigemptyset(&sig_action.sa_mask);
+ sig_action.sa_flags = SA_RESTART;
+ alarmHandlerThis = this;
+ alarmHandlerTimeout = clock() + solverTimeout*CLOCKS_PER_SEC;
+ old_alarm_timeout = alarm(0);
+ sigaction(SIGALRM, &sig_action, &old_sig_action);
+ alarm(1);
+ }
+#endif
+
+ bool foundSolution = minisatSolver->solve(assumps);
+
+#ifndef _WIN32
+ if (solverTimeout > 0) {
+ if (alarmHandlerTimeout == 0)
+ solverTimoutStatus = true;
+ alarm(0);
+ sigaction(SIGALRM, &old_sig_action, NULL);
+ alarm(old_alarm_timeout);
+ }
+#endif
+
+ if (!foundSolution) {
+#if !EZMINISAT_INCREMENTAL
+ delete minisatSolver;
+ minisatSolver = NULL;
+ minisatVars.clear();
+#endif
+ return false;
+ }
+
+ modelValues.clear();
+ modelValues.resize(modelIdx.size());
+
+ for (size_t i = 0; i < modelIdx.size(); i++)
+ {
+ int idx = modelIdx[i];
+ bool refvalue = true;
+
+ if (idx < 0)
+ idx = -idx, refvalue = false;
+
+ using namespace Minisat;
+ lbool value = minisatSolver->modelValue(minisatVars.at(idx-1));
+ modelValues[i] = (value == Minisat::lbool(refvalue));
+ }
+
+#if !EZMINISAT_INCREMENTAL
+ delete minisatSolver;
+ minisatSolver = NULL;
+ minisatVars.clear();
+#endif
+ return true;
+}
+
diff --git a/libs/ezsat/ezminisat.h b/libs/ezsat/ezminisat.h
new file mode 100644
index 00000000..983e6fd0
--- /dev/null
+++ b/libs/ezsat/ezminisat.h
@@ -0,0 +1,71 @@
+/*
+ * ezSAT -- A simple and easy to use CNF generator for SAT solvers
+ *
+ * Copyright (C) 2013 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.
+ *
+ */
+
+#ifndef EZMINISAT_H
+#define EZMINISAT_H
+
+#define EZMINISAT_SIMPSOLVER 1
+#define EZMINISAT_VERBOSITY 0
+#define EZMINISAT_INCREMENTAL 1
+
+#include "ezsat.h"
+#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
+// don't force ezSAT users to use minisat headers..
+namespace Minisat {
+ class Solver;
+ class SimpSolver;
+}
+
+class ezMiniSAT : public ezSAT
+{
+private:
+#if EZMINISAT_SIMPSOLVER
+ typedef Minisat::SimpSolver Solver;
+#else
+ typedef Minisat::Solver Solver;
+#endif
+ Solver *minisatSolver;
+ std::vector<int> minisatVars;
+ bool foundContradiction;
+
+#if EZMINISAT_SIMPSOLVER && EZMINISAT_INCREMENTAL
+ std::set<int> cnfFrozenVars;
+#endif
+
+#ifndef _WIN32
+ static ezMiniSAT *alarmHandlerThis;
+ static clock_t alarmHandlerTimeout;
+ static void alarmHandler(int);
+#endif
+
+public:
+ ezMiniSAT();
+ virtual ~ezMiniSAT();
+ virtual void clear();
+#if EZMINISAT_SIMPSOLVER && EZMINISAT_INCREMENTAL
+ virtual void freeze(int id);
+ virtual bool eliminated(int idx);
+#endif
+ virtual bool solver(const std::vector<int> &modelExpressions, std::vector<bool> &modelValues, const std::vector<int> &assumptions);
+};
+
+#endif
diff --git a/libs/ezsat/ezsat.cc b/libs/ezsat/ezsat.cc
new file mode 100644
index 00000000..177bcd8a
--- /dev/null
+++ b/libs/ezsat/ezsat.cc
@@ -0,0 +1,1449 @@
+/*
+ * ezSAT -- A simple and easy to use CNF generator for SAT solvers
+ *
+ * Copyright (C) 2013 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 "ezsat.h"
+
+#include <cmath>
+#include <algorithm>
+#include <cassert>
+#include <string>
+
+#include <stdlib.h>
+
+const int ezSAT::CONST_TRUE = 1;
+const int ezSAT::CONST_FALSE = 2;
+
+static std::string my_int_to_string(int i)
+{
+#ifdef __MINGW32__
+ char buffer[64];
+ snprintf(buffer, 64, "%d", i);
+ return buffer;
+#else
+ return std::to_string(i);
+#endif
+}
+
+ezSAT::ezSAT()
+{
+ statehash = 5381;
+
+ flag_keep_cnf = false;
+ flag_non_incremental = false;
+
+ non_incremental_solve_used_up = false;
+
+ cnfConsumed = false;
+ cnfVariableCount = 0;
+ cnfClausesCount = 0;
+
+ solverTimeout = 0;
+ solverTimoutStatus = false;
+
+ literal("CONST_TRUE");
+ literal("CONST_FALSE");
+
+ assert(literal("CONST_TRUE") == CONST_TRUE);
+ assert(literal("CONST_FALSE") == CONST_FALSE);
+}
+
+ezSAT::~ezSAT()
+{
+}
+
+void ezSAT::addhash(unsigned int h)
+{
+ statehash = ((statehash << 5) + statehash) ^ h;
+}
+
+int ezSAT::value(bool val)
+{
+ return val ? CONST_TRUE : CONST_FALSE;
+}
+
+int ezSAT::literal()
+{
+ literals.push_back(std::string());
+ return literals.size();
+}
+
+int ezSAT::literal(const std::string &name)
+{
+ if (literalsCache.count(name) == 0) {
+ literals.push_back(name);
+ literalsCache[name] = literals.size();
+ }
+ return literalsCache.at(name);
+}
+
+int ezSAT::frozen_literal()
+{
+ int id = literal();
+ freeze(id);
+ return id;
+}
+
+int ezSAT::frozen_literal(const std::string &name)
+{
+ int id = literal(name);
+ freeze(id);
+ return id;
+}
+
+int ezSAT::expression(OpId op, int a, int b, int c, int d, int e, int f)
+{
+ std::vector<int> args(6);
+ args[0] = a, args[1] = b, args[2] = c;
+ args[3] = d, args[4] = e, args[5] = f;
+ return expression(op, args);
+}
+
+int ezSAT::expression(OpId op, const std::vector<int> &args)
+{
+ std::vector<int> myArgs;
+ myArgs.reserve(args.size());
+ bool xorRemovedOddTrues = false;
+
+ addhash(__LINE__);
+ addhash(op);
+
+ for (auto arg : args)
+ {
+ addhash(__LINE__);
+ addhash(arg);
+
+ if (arg == 0)
+ continue;
+ if (op == OpAnd && arg == CONST_TRUE)
+ continue;
+ if ((op == OpOr || op == OpXor) && arg == CONST_FALSE)
+ continue;
+ if (op == OpXor && arg == CONST_TRUE) {
+ xorRemovedOddTrues = !xorRemovedOddTrues;
+ continue;
+ }
+ myArgs.push_back(arg);
+ }
+
+ if (myArgs.size() > 0 && (op == OpAnd || op == OpOr || op == OpXor || op == OpIFF)) {
+ std::sort(myArgs.begin(), myArgs.end());
+ int j = 0;
+ for (int i = 1; i < int(myArgs.size()); i++)
+ if (j < 0 || myArgs[j] != myArgs[i])
+ myArgs[++j] = myArgs[i];
+ else if (op == OpXor)
+ j--;
+ myArgs.resize(j+1);
+ }
+
+ switch (op)
+ {
+ case OpNot:
+ assert(myArgs.size() == 1);
+ if (myArgs[0] == CONST_TRUE)
+ return CONST_FALSE;
+ if (myArgs[0] == CONST_FALSE)
+ return CONST_TRUE;
+ break;
+
+ case OpAnd:
+ if (myArgs.size() == 0)
+ return CONST_TRUE;
+ if (myArgs.size() == 1)
+ return myArgs[0];
+ break;
+
+ case OpOr:
+ if (myArgs.size() == 0)
+ return CONST_FALSE;
+ if (myArgs.size() == 1)
+ return myArgs[0];
+ break;
+
+ case OpXor:
+ if (myArgs.size() == 0)
+ return xorRemovedOddTrues ? CONST_TRUE : CONST_FALSE;
+ if (myArgs.size() == 1)
+ return xorRemovedOddTrues ? NOT(myArgs[0]) : myArgs[0];
+ break;
+
+ case OpIFF:
+ assert(myArgs.size() >= 1);
+ if (myArgs.size() == 1)
+ return CONST_TRUE;
+ // FIXME: Add proper const folding
+ break;
+
+ case OpITE:
+ assert(myArgs.size() == 3);
+ if (myArgs[0] == CONST_TRUE)
+ return myArgs[1];
+ if (myArgs[0] == CONST_FALSE)
+ return myArgs[2];
+ break;
+
+ default:
+ abort();
+ }
+
+ std::pair<OpId, std::vector<int>> myExpr(op, myArgs);
+ int id = 0;
+
+ if (expressionsCache.count(myExpr) > 0) {
+ id = expressionsCache.at(myExpr);
+ } else {
+ id = -(int(expressions.size()) + 1);
+ expressionsCache[myExpr] = id;
+ expressions.push_back(myExpr);
+ }
+
+ if (xorRemovedOddTrues)
+ id = NOT(id);
+
+ addhash(__LINE__);
+ addhash(id);
+
+ return id;
+}
+
+void ezSAT::lookup_literal(int id, std::string &name) const
+{
+ assert(0 < id && id <= int(literals.size()));
+ name = literals[id - 1];
+}
+
+const std::string &ezSAT::lookup_literal(int id) const
+{
+ assert(0 < id && id <= int(literals.size()));
+ return literals[id - 1];
+}
+
+void ezSAT::lookup_expression(int id, OpId &op, std::vector<int> &args) const
+{
+ assert(0 < -id && -id <= int(expressions.size()));
+ op = expressions[-id - 1].first;
+ args = expressions[-id - 1].second;
+}
+
+const std::vector<int> &ezSAT::lookup_expression(int id, OpId &op) const
+{
+ assert(0 < -id && -id <= int(expressions.size()));
+ op = expressions[-id - 1].first;
+ return expressions[-id - 1].second;
+}
+
+int ezSAT::parse_string(const std::string &)
+{
+ abort();
+}
+
+std::string ezSAT::to_string(int id) const
+{
+ std::string text;
+
+ if (id > 0)
+ {
+ lookup_literal(id, text);
+ }
+ else
+ {
+ OpId op;
+ std::vector<int> args;
+ lookup_expression(id, op, args);
+
+ switch (op)
+ {
+ case OpNot:
+ text = "not(";
+ break;
+
+ case OpAnd:
+ text = "and(";
+ break;
+
+ case OpOr:
+ text = "or(";
+ break;
+
+ case OpXor:
+ text = "xor(";
+ break;
+
+ case OpIFF:
+ text = "iff(";
+ break;
+
+ case OpITE:
+ text = "ite(";
+ break;
+
+ default:
+ abort();
+ }
+
+ for (int i = 0; i < int(args.size()); i++) {
+ if (i > 0)
+ text += ", ";
+ text += to_string(args[i]);
+ }
+
+ text += ")";
+ }
+
+ return text;
+}
+
+int ezSAT::eval(int id, const std::vector<int> &values) const
+{
+ if (id > 0) {
+ if (id <= int(values.size()) && (values[id-1] == CONST_TRUE || values[id-1] == CONST_FALSE || values[id-1] == 0))
+ return values[id-1];
+ return 0;
+ }
+
+ OpId op;
+ const std::vector<int> &args = lookup_expression(id, op);
+ int a, b;
+
+ switch (op)
+ {
+ case OpNot:
+ assert(args.size() == 1);
+ a = eval(args[0], values);
+ if (a == CONST_TRUE)
+ return CONST_FALSE;
+ if (a == CONST_FALSE)
+ return CONST_TRUE;
+ return 0;
+ case OpAnd:
+ a = CONST_TRUE;
+ for (auto arg : args) {
+ b = eval(arg, values);
+ if (b != CONST_TRUE && b != CONST_FALSE)
+ a = 0;
+ if (b == CONST_FALSE)
+ return CONST_FALSE;
+ }
+ return a;
+ case OpOr:
+ a = CONST_FALSE;
+ for (auto arg : args) {
+ b = eval(arg, values);
+ if (b != CONST_TRUE && b != CONST_FALSE)
+ a = 0;
+ if (b == CONST_TRUE)
+ return CONST_TRUE;
+ }
+ return a;
+ case OpXor:
+ a = CONST_FALSE;
+ for (auto arg : args) {
+ b = eval(arg, values);
+ if (b != CONST_TRUE && b != CONST_FALSE)
+ return 0;
+ if (b == CONST_TRUE)
+ a = a == CONST_TRUE ? CONST_FALSE : CONST_TRUE;
+ }
+ return a;
+ case OpIFF:
+ assert(args.size() > 0);
+ a = eval(args[0], values);
+ for (auto arg : args) {
+ b = eval(arg, values);
+ if (b != CONST_TRUE && b != CONST_FALSE)
+ return 0;
+ if (b != a)
+ return CONST_FALSE;
+ }
+ return CONST_TRUE;
+ case OpITE:
+ assert(args.size() == 3);
+ a = eval(args[0], values);
+ if (a == CONST_TRUE)
+ return eval(args[1], values);
+ if (a == CONST_FALSE)
+ return eval(args[2], values);
+ return 0;
+ default:
+ abort();
+ }
+}
+
+void ezSAT::clear()
+{
+ cnfConsumed = false;
+ cnfVariableCount = 0;
+ cnfClausesCount = 0;
+ cnfLiteralVariables.clear();
+ cnfExpressionVariables.clear();
+ cnfClauses.clear();
+}
+
+void ezSAT::freeze(int)
+{
+}
+
+bool ezSAT::eliminated(int)
+{
+ return false;
+}
+
+void ezSAT::assume(int id)
+{
+ addhash(__LINE__);
+ addhash(id);
+
+ if (id < 0)
+ {
+ assert(0 < -id && -id <= int(expressions.size()));
+ cnfExpressionVariables.resize(expressions.size());
+
+ if (cnfExpressionVariables[-id-1] == 0)
+ {
+ OpId op;
+ std::vector<int> args;
+ lookup_expression(id, op, args);
+
+ if (op == OpNot) {
+ int idx = bind(args[0]);
+ cnfClauses.push_back(std::vector<int>(1, -idx));
+ cnfClausesCount++;
+ return;
+ }
+ if (op == OpOr) {
+ std::vector<int> clause;
+ for (int arg : args)
+ clause.push_back(bind(arg));
+ cnfClauses.push_back(clause);
+ cnfClausesCount++;
+ return;
+ }
+ if (op == OpAnd) {
+ for (int arg : args) {
+ cnfClauses.push_back(std::vector<int>(1, bind(arg)));
+ cnfClausesCount++;
+ }
+ return;
+ }
+ }
+ }
+
+ int idx = bind(id);
+ cnfClauses.push_back(std::vector<int>(1, idx));
+ cnfClausesCount++;
+}
+
+void ezSAT::add_clause(const std::vector<int> &args)
+{
+ addhash(__LINE__);
+ for (auto arg : args)
+ addhash(arg);
+
+ cnfClauses.push_back(args);
+ cnfClausesCount++;
+}
+
+void ezSAT::add_clause(const std::vector<int> &args, bool argsPolarity, int a, int b, int c)
+{
+ std::vector<int> clause;
+ for (auto arg : args)
+ clause.push_back(argsPolarity ? +arg : -arg);
+ if (a != 0)
+ clause.push_back(a);
+ if (b != 0)
+ clause.push_back(b);
+ if (c != 0)
+ clause.push_back(c);
+ add_clause(clause);
+}
+
+void ezSAT::add_clause(int a, int b, int c)
+{
+ std::vector<int> clause;
+ if (a != 0)
+ clause.push_back(a);
+ if (b != 0)
+ clause.push_back(b);
+ if (c != 0)
+ clause.push_back(c);
+ add_clause(clause);
+}
+
+int ezSAT::bind_cnf_not(const std::vector<int> &args)
+{
+ assert(args.size() == 1);
+ return -args[0];
+}
+
+int ezSAT::bind_cnf_and(const std::vector<int> &args)
+{
+ assert(args.size() >= 2);
+
+ int idx = ++cnfVariableCount;
+ add_clause(args, false, idx);
+
+ for (auto arg : args)
+ add_clause(-idx, arg);
+
+ return idx;
+}
+
+int ezSAT::bind_cnf_or(const std::vector<int> &args)
+{
+ assert(args.size() >= 2);
+
+ int idx = ++cnfVariableCount;
+ add_clause(args, true, -idx);
+
+ for (auto arg : args)
+ add_clause(idx, -arg);
+
+ return idx;
+}
+
+int ezSAT::bound(int id) const
+{
+ if (id > 0 && id <= int(cnfLiteralVariables.size()))
+ return cnfLiteralVariables[id-1];
+ if (-id > 0 && -id <= int(cnfExpressionVariables.size()))
+ return cnfExpressionVariables[-id-1];
+ return 0;
+}
+
+std::string ezSAT::cnfLiteralInfo(int idx) const
+{
+ for (int i = 0; i < int(cnfLiteralVariables.size()); i++) {
+ if (cnfLiteralVariables[i] == idx)
+ return to_string(i+1);
+ if (cnfLiteralVariables[i] == -idx)
+ return "NOT " + to_string(i+1);
+ }
+ for (int i = 0; i < int(cnfExpressionVariables.size()); i++) {
+ if (cnfExpressionVariables[i] == idx)
+ return to_string(-i-1);
+ if (cnfExpressionVariables[i] == -idx)
+ return "NOT " + to_string(-i-1);
+ }
+ return "<unnamed>";
+}
+
+int ezSAT::bind(int id, bool auto_freeze)
+{
+ addhash(__LINE__);
+ addhash(id);
+ addhash(auto_freeze);
+
+ if (id >= 0) {
+ assert(0 < id && id <= int(literals.size()));
+ cnfLiteralVariables.resize(literals.size());
+ if (eliminated(cnfLiteralVariables[id-1])) {
+ fprintf(stderr, "ezSAT: Missing freeze on literal `%s'.\n", to_string(id).c_str());
+ abort();
+ }
+ if (cnfLiteralVariables[id-1] == 0) {
+ cnfLiteralVariables[id-1] = ++cnfVariableCount;
+ if (id == CONST_TRUE)
+ add_clause(+cnfLiteralVariables[id-1]);
+ if (id == CONST_FALSE)
+ add_clause(-cnfLiteralVariables[id-1]);
+ }
+ return cnfLiteralVariables[id-1];
+ }
+
+ assert(0 < -id && -id <= int(expressions.size()));
+ cnfExpressionVariables.resize(expressions.size());
+
+ if (eliminated(cnfExpressionVariables[-id-1]))
+ {
+ cnfExpressionVariables[-id-1] = 0;
+
+ // this will recursively call bind(id). within the recursion
+ // the cnf is pre-set to 0. an idx is allocated there, then it
+ // is frozen, then it returns here with the new idx already set.
+ if (auto_freeze)
+ freeze(id);
+ }
+
+ if (cnfExpressionVariables[-id-1] == 0)
+ {
+ OpId op;
+ std::vector<int> args;
+ lookup_expression(id, op, args);
+ int idx = 0;
+
+ if (op == OpXor) {
+ while (args.size() > 1) {
+ std::vector<int> newArgs;
+ for (int i = 0; i < int(args.size()); i += 2)
+ if (i+1 == int(args.size())) {
+ newArgs.push_back(args[i]);
+ } else {
+ int sub1 = AND(args[i], NOT(args[i+1]));
+ int sub2 = AND(NOT(args[i]), args[i+1]);
+ newArgs.push_back(OR(sub1, sub2));
+ }
+ args.swap(newArgs);
+ }
+ idx = bind(args.at(0), false);
+ goto assign_idx;
+ }
+
+ if (op == OpIFF) {
+ std::vector<int> invArgs;
+ for (auto arg : args)
+ invArgs.push_back(NOT(arg));
+ int sub1 = expression(OpAnd, args);
+ int sub2 = expression(OpAnd, invArgs);
+ idx = bind(OR(sub1, sub2), false);
+ goto assign_idx;
+ }
+
+ if (op == OpITE) {
+ int sub1 = AND(args[0], args[1]);
+ int sub2 = AND(NOT(args[0]), args[2]);
+ idx = bind(OR(sub1, sub2), false);
+ goto assign_idx;
+ }
+
+ for (int i = 0; i < int(args.size()); i++)
+ args[i] = bind(args[i], false);
+
+ switch (op)
+ {
+ case OpNot: idx = bind_cnf_not(args); break;
+ case OpAnd: idx = bind_cnf_and(args); break;
+ case OpOr: idx = bind_cnf_or(args); break;
+ default: abort();
+ }
+
+ assign_idx:
+ assert(idx != 0);
+ cnfExpressionVariables[-id-1] = idx;
+ }
+
+ return cnfExpressionVariables[-id-1];
+}
+
+void ezSAT::consumeCnf()
+{
+ if (mode_keep_cnf())
+ cnfClausesBackup.insert(cnfClausesBackup.end(), cnfClauses.begin(), cnfClauses.end());
+ else
+ cnfConsumed = true;
+ cnfClauses.clear();
+}
+
+void ezSAT::consumeCnf(std::vector<std::vector<int>> &cnf)
+{
+ if (mode_keep_cnf())
+ cnfClausesBackup.insert(cnfClausesBackup.end(), cnfClauses.begin(), cnfClauses.end());
+ else
+ cnfConsumed = true;
+ cnf.swap(cnfClauses);
+ cnfClauses.clear();
+}
+
+void ezSAT::getFullCnf(std::vector<std::vector<int>> &full_cnf) const
+{
+ assert(full_cnf.empty());
+ full_cnf.insert(full_cnf.end(), cnfClausesBackup.begin(), cnfClausesBackup.end());
+ full_cnf.insert(full_cnf.end(), cnfClauses.begin(), cnfClauses.end());
+}
+
+void ezSAT::preSolverCallback()
+{
+ assert(!non_incremental_solve_used_up);
+ if (mode_non_incremental())
+ non_incremental_solve_used_up = true;
+}
+
+bool ezSAT::solver(const std::vector<int>&, std::vector<bool>&, const std::vector<int>&)
+{
+ preSolverCallback();
+ fprintf(stderr, "************************************************************************\n");
+ fprintf(stderr, "ERROR: You are trying to use the solve() method of the ezSAT base class!\n");
+ fprintf(stderr, "Use a dervied class like ezMiniSAT instead.\n");
+ fprintf(stderr, "************************************************************************\n");
+ abort();
+}
+
+std::vector<int> ezSAT::vec_const(const std::vector<bool> &bits)
+{
+ std::vector<int> vec;
+ for (auto bit : bits)
+ vec.push_back(bit ? CONST_TRUE : CONST_FALSE);
+ return vec;
+}
+
+std::vector<int> ezSAT::vec_const_signed(int64_t value, int numBits)
+{
+ std::vector<int> vec;
+ for (int i = 0; i < numBits; i++)
+ vec.push_back(((value >> i) & 1) != 0 ? CONST_TRUE : CONST_FALSE);
+ return vec;
+}
+
+std::vector<int> ezSAT::vec_const_unsigned(uint64_t value, int numBits)
+{
+ std::vector<int> vec;
+ for (int i = 0; i < numBits; i++)
+ vec.push_back(((value >> i) & 1) != 0 ? CONST_TRUE : CONST_FALSE);
+ return vec;
+}
+
+std::vector<int> ezSAT::vec_var(int numBits)
+{
+ std::vector<int> vec;
+ for (int i = 0; i < numBits; i++)
+ vec.push_back(literal());
+ return vec;
+}
+
+std::vector<int> ezSAT::vec_var(std::string name, int numBits)
+{
+ std::vector<int> vec;
+ for (int i = 0; i < numBits; i++) {
+ vec.push_back(VAR(name + my_int_to_string(i)));
+ }
+ return vec;
+}
+
+std::vector<int> ezSAT::vec_cast(const std::vector<int> &vec1, int toBits, bool signExtend)
+{
+ std::vector<int> vec;
+ for (int i = 0; i < toBits; i++)
+ if (i >= int(vec1.size()))
+ vec.push_back(signExtend ? vec1.back() : CONST_FALSE);
+ else
+ vec.push_back(vec1[i]);
+ return vec;
+}
+
+std::vector<int> ezSAT::vec_not(const std::vector<int> &vec1)
+{
+ std::vector<int> vec;
+ for (auto bit : vec1)
+ vec.push_back(NOT(bit));
+ return vec;
+}
+
+std::vector<int> ezSAT::vec_and(const std::vector<int> &vec1, const std::vector<int> &vec2)
+{
+ assert(vec1.size() == vec2.size());
+ std::vector<int> vec(vec1.size());
+ for (int i = 0; i < int(vec1.size()); i++)
+ vec[i] = AND(vec1[i], vec2[i]);
+ return vec;
+}
+
+std::vector<int> ezSAT::vec_or(const std::vector<int> &vec1, const std::vector<int> &vec2)
+{
+ assert(vec1.size() == vec2.size());
+ std::vector<int> vec(vec1.size());
+ for (int i = 0; i < int(vec1.size()); i++)
+ vec[i] = OR(vec1[i], vec2[i]);
+ return vec;
+}
+
+std::vector<int> ezSAT::vec_xor(const std::vector<int> &vec1, const std::vector<int> &vec2)
+{
+ assert(vec1.size() == vec2.size());
+ std::vector<int> vec(vec1.size());
+ for (int i = 0; i < int(vec1.size()); i++)
+ vec[i] = XOR(vec1[i], vec2[i]);
+ return vec;
+}
+
+std::vector<int> ezSAT::vec_iff(const std::vector<int> &vec1, const std::vector<int> &vec2)
+{
+ assert(vec1.size() == vec2.size());
+ std::vector<int> vec(vec1.size());
+ for (int i = 0; i < int(vec1.size()); i++)
+ vec[i] = IFF(vec1[i], vec2[i]);
+ return vec;
+}
+
+std::vector<int> ezSAT::vec_ite(const std::vector<int> &vec1, const std::vector<int> &vec2, const std::vector<int> &vec3)
+{
+ assert(vec1.size() == vec2.size() && vec2.size() == vec3.size());
+ std::vector<int> vec(vec1.size());
+ for (int i = 0; i < int(vec1.size()); i++)
+ vec[i] = ITE(vec1[i], vec2[i], vec3[i]);
+ return vec;
+}
+
+
+std::vector<int> ezSAT::vec_ite(int sel, const std::vector<int> &vec1, const std::vector<int> &vec2)
+{
+ assert(vec1.size() == vec2.size());
+ std::vector<int> vec(vec1.size());
+ for (int i = 0; i < int(vec1.size()); i++)
+ vec[i] = ITE(sel, vec1[i], vec2[i]);
+ return vec;
+}
+
+// 'y' is the MSB (carry) and x the LSB (sum) output
+static void fulladder(ezSAT *that, int a, int b, int c, int &y, int &x)
+{
+ int tmp = that->XOR(a, b);
+ int new_x = that->XOR(tmp, c);
+ int new_y = that->OR(that->AND(a, b), that->AND(c, tmp));
+#if 0
+ printf("FULLADD> a=%s, b=%s, c=%s, carry=%s, sum=%s\n", that->to_string(a).c_str(), that->to_string(b).c_str(),
+ that->to_string(c).c_str(), that->to_string(new_y).c_str(), that->to_string(new_x).c_str());
+#endif
+ x = new_x, y = new_y;
+}
+
+// 'y' is the MSB (carry) and x the LSB (sum) output
+static void halfadder(ezSAT *that, int a, int b, int &y, int &x)
+{
+ int new_x = that->XOR(a, b);
+ int new_y = that->AND(a, b);
+#if 0
+ printf("HALFADD> a=%s, b=%s, carry=%s, sum=%s\n", that->to_string(a).c_str(), that->to_string(b).c_str(),
+ that->to_string(new_y).c_str(), that->to_string(new_x).c_str());
+#endif
+ x = new_x, y = new_y;
+}
+
+std::vector<int> ezSAT::vec_count(const std::vector<int> &vec, int numBits, bool clip)
+{
+ std::vector<int> sum = vec_const_unsigned(0, numBits);
+ std::vector<int> carry_vector;
+
+ for (auto bit : vec) {
+ int carry = bit;
+ for (int i = 0; i < numBits; i++)
+ halfadder(this, carry, sum[i], carry, sum[i]);
+ carry_vector.push_back(carry);
+ }
+
+ if (clip) {
+ int overflow = vec_reduce_or(carry_vector);
+ sum = vec_ite(overflow, vec_const_unsigned(~0, numBits), sum);
+ }
+
+#if 0
+ printf("COUNT> vec=[");
+ for (int i = int(vec.size())-1; i >= 0; i--)
+ printf("%s%s", to_string(vec[i]).c_str(), i ? ", " : "");
+ printf("], result=[");
+ for (int i = int(sum.size())-1; i >= 0; i--)
+ printf("%s%s", to_string(sum[i]).c_str(), i ? ", " : "");
+ printf("]\n");
+#endif
+
+ return sum;
+}
+
+std::vector<int> ezSAT::vec_add(const std::vector<int> &vec1, const std::vector<int> &vec2)
+{
+ assert(vec1.size() == vec2.size());
+ std::vector<int> vec(vec1.size());
+ int carry = CONST_FALSE;
+ for (int i = 0; i < int(vec1.size()); i++)
+ fulladder(this, vec1[i], vec2[i], carry, carry, vec[i]);
+
+#if 0
+ printf("ADD> vec1=[");
+ for (int i = int(vec1.size())-1; i >= 0; i--)
+ printf("%s%s", to_string(vec1[i]).c_str(), i ? ", " : "");
+ printf("], vec2=[");
+ for (int i = int(vec2.size())-1; i >= 0; i--)
+ printf("%s%s", to_string(vec2[i]).c_str(), i ? ", " : "");
+ printf("], result=[");
+ for (int i = int(vec.size())-1; i >= 0; i--)
+ printf("%s%s", to_string(vec[i]).c_str(), i ? ", " : "");
+ printf("]\n");
+#endif
+
+ return vec;
+}
+
+std::vector<int> ezSAT::vec_sub(const std::vector<int> &vec1, const std::vector<int> &vec2)
+{
+ assert(vec1.size() == vec2.size());
+ std::vector<int> vec(vec1.size());
+ int carry = CONST_TRUE;
+ for (int i = 0; i < int(vec1.size()); i++)
+ fulladder(this, vec1[i], NOT(vec2[i]), carry, carry, vec[i]);
+
+#if 0
+ printf("SUB> vec1=[");
+ for (int i = int(vec1.size())-1; i >= 0; i--)
+ printf("%s%s", to_string(vec1[i]).c_str(), i ? ", " : "");
+ printf("], vec2=[");
+ for (int i = int(vec2.size())-1; i >= 0; i--)
+ printf("%s%s", to_string(vec2[i]).c_str(), i ? ", " : "");
+ printf("], result=[");
+ for (int i = int(vec.size())-1; i >= 0; i--)
+ printf("%s%s", to_string(vec[i]).c_str(), i ? ", " : "");
+ printf("]\n");
+#endif
+
+ return vec;
+}
+
+std::vector<int> ezSAT::vec_neg(const std::vector<int> &vec)
+{
+ std::vector<int> zero(vec.size(), CONST_FALSE);
+ return vec_sub(zero, vec);
+}
+
+void ezSAT::vec_cmp(const std::vector<int> &vec1, const std::vector<int> &vec2, int &carry, int &overflow, int &sign, int &zero)
+{
+ assert(vec1.size() == vec2.size());
+ carry = CONST_TRUE;
+ zero = CONST_FALSE;
+ for (int i = 0; i < int(vec1.size()); i++) {
+ overflow = carry;
+ fulladder(this, vec1[i], NOT(vec2[i]), carry, carry, sign);
+ zero = OR(zero, sign);
+ }
+ overflow = XOR(overflow, carry);
+ carry = NOT(carry);
+ zero = NOT(zero);
+
+#if 0
+ printf("CMP> vec1=[");
+ for (int i = int(vec1.size())-1; i >= 0; i--)
+ printf("%s%s", to_string(vec1[i]).c_str(), i ? ", " : "");
+ printf("], vec2=[");
+ for (int i = int(vec2.size())-1; i >= 0; i--)
+ printf("%s%s", to_string(vec2[i]).c_str(), i ? ", " : "");
+ printf("], carry=%s, overflow=%s, sign=%s, zero=%s\n", to_string(carry).c_str(), to_string(overflow).c_str(), to_string(sign).c_str(), to_string(zero).c_str());
+#endif
+}
+
+int ezSAT::vec_lt_signed(const std::vector<int> &vec1, const std::vector<int> &vec2)
+{
+ int carry, overflow, sign, zero;
+ vec_cmp(vec1, vec2, carry, overflow, sign, zero);
+ return OR(AND(NOT(overflow), sign), AND(overflow, NOT(sign)));
+}
+
+int ezSAT::vec_le_signed(const std::vector<int> &vec1, const std::vector<int> &vec2)
+{
+ int carry, overflow, sign, zero;
+ vec_cmp(vec1, vec2, carry, overflow, sign, zero);
+ return OR(AND(NOT(overflow), sign), AND(overflow, NOT(sign)), zero);
+}
+
+int ezSAT::vec_ge_signed(const std::vector<int> &vec1, const std::vector<int> &vec2)
+{
+ int carry, overflow, sign, zero;
+ vec_cmp(vec1, vec2, carry, overflow, sign, zero);
+ return OR(AND(NOT(overflow), NOT(sign)), AND(overflow, sign));
+}
+
+int ezSAT::vec_gt_signed(const std::vector<int> &vec1, const std::vector<int> &vec2)
+{
+ int carry, overflow, sign, zero;
+ vec_cmp(vec1, vec2, carry, overflow, sign, zero);
+ return AND(OR(AND(NOT(overflow), NOT(sign)), AND(overflow, sign)), NOT(zero));
+}
+
+int ezSAT::vec_lt_unsigned(const std::vector<int> &vec1, const std::vector<int> &vec2)
+{
+ int carry, overflow, sign, zero;
+ vec_cmp(vec1, vec2, carry, overflow, sign, zero);
+ return carry;
+}
+
+int ezSAT::vec_le_unsigned(const std::vector<int> &vec1, const std::vector<int> &vec2)
+{
+ int carry, overflow, sign, zero;
+ vec_cmp(vec1, vec2, carry, overflow, sign, zero);
+ return OR(carry, zero);
+}
+
+int ezSAT::vec_ge_unsigned(const std::vector<int> &vec1, const std::vector<int> &vec2)
+{
+ int carry, overflow, sign, zero;
+ vec_cmp(vec1, vec2, carry, overflow, sign, zero);
+ return NOT(carry);
+}
+
+int ezSAT::vec_gt_unsigned(const std::vector<int> &vec1, const std::vector<int> &vec2)
+{
+ int carry, overflow, sign, zero;
+ vec_cmp(vec1, vec2, carry, overflow, sign, zero);
+ return AND(NOT(carry), NOT(zero));
+}
+
+int ezSAT::vec_eq(const std::vector<int> &vec1, const std::vector<int> &vec2)
+{
+ return vec_reduce_and(vec_iff(vec1, vec2));
+}
+
+int ezSAT::vec_ne(const std::vector<int> &vec1, const std::vector<int> &vec2)
+{
+ return NOT(vec_reduce_and(vec_iff(vec1, vec2)));
+}
+
+std::vector<int> ezSAT::vec_shl(const std::vector<int> &vec1, int shift, bool signExtend)
+{
+ std::vector<int> vec;
+ for (int i = 0; i < int(vec1.size()); i++) {
+ int j = i-shift;
+ if (int(vec1.size()) <= j)
+ vec.push_back(signExtend ? vec1.back() : CONST_FALSE);
+ else if (0 <= j)
+ vec.push_back(vec1[j]);
+ else
+ vec.push_back(CONST_FALSE);
+ }
+ return vec;
+}
+
+std::vector<int> ezSAT::vec_srl(const std::vector<int> &vec1, int shift)
+{
+ std::vector<int> vec;
+ for (int i = 0; i < int(vec1.size()); i++) {
+ int j = i-shift;
+ while (j < 0)
+ j += vec1.size();
+ while (j >= int(vec1.size()))
+ j -= vec1.size();
+ vec.push_back(vec1[j]);
+ }
+ return vec;
+}
+
+std::vector<int> ezSAT::vec_shift(const std::vector<int> &vec1, int shift, int extend_left, int extend_right)
+{
+ std::vector<int> vec;
+ for (int i = 0; i < int(vec1.size()); i++) {
+ int j = i+shift;
+ if (j < 0)
+ vec.push_back(extend_right);
+ else if (j >= int(vec1.size()))
+ vec.push_back(extend_left);
+ else
+ vec.push_back(vec1[j]);
+ }
+ return vec;
+}
+
+static int my_clog2(int x)
+{
+ int result = 0;
+ for (x--; x > 0; result++)
+ x >>= 1;
+ return result;
+}
+
+std::vector<int> ezSAT::vec_shift_right(const std::vector<int> &vec1, const std::vector<int> &vec2, bool vec2_signed, int extend_left, int extend_right)
+{
+ int vec2_bits = std::min(my_clog2(vec1.size()) + (vec2_signed ? 1 : 0), int(vec2.size()));
+
+ std::vector<int> overflow_bits(vec2.begin() + vec2_bits, vec2.end());
+ int overflow_left = CONST_FALSE, overflow_right = CONST_FALSE;
+
+ if (vec2_signed) {
+ int overflow = CONST_FALSE;
+ for (auto bit : overflow_bits)
+ overflow = OR(overflow, XOR(bit, vec2[vec2_bits-1]));
+ overflow_left = AND(overflow, NOT(vec2.back()));
+ overflow_right = AND(overflow, vec2.back());
+ } else
+ overflow_left = vec_reduce_or(overflow_bits);
+
+ std::vector<int> buffer = vec1;
+
+ if (vec2_signed)
+ while (buffer.size() < vec1.size() + (1 << vec2_bits))
+ buffer.push_back(extend_left);
+
+ std::vector<int> overflow_pattern_left(buffer.size(), extend_left);
+ std::vector<int> overflow_pattern_right(buffer.size(), extend_right);
+
+ buffer = vec_ite(overflow_left, overflow_pattern_left, buffer);
+
+ if (vec2_signed)
+ buffer = vec_ite(overflow_right, overflow_pattern_left, buffer);
+
+ for (int i = vec2_bits-1; i >= 0; i--) {
+ std::vector<int> shifted_buffer;
+ if (vec2_signed && i == vec2_bits-1)
+ shifted_buffer = vec_shift(buffer, -(1 << i), extend_left, extend_right);
+ else
+ shifted_buffer = vec_shift(buffer, 1 << i, extend_left, extend_right);
+ buffer = vec_ite(vec2[i], shifted_buffer, buffer);
+ }
+
+ buffer.resize(vec1.size());
+ return buffer;
+}
+
+std::vector<int> ezSAT::vec_shift_left(const std::vector<int> &vec1, const std::vector<int> &vec2, bool vec2_signed, int extend_left, int extend_right)
+{
+ // vec2_signed is not implemented in vec_shift_left() yet
+ if (vec2_signed) assert(vec2_signed == false);
+
+ int vec2_bits = std::min(my_clog2(vec1.size()), int(vec2.size()));
+
+ std::vector<int> overflow_bits(vec2.begin() + vec2_bits, vec2.end());
+ int overflow = vec_reduce_or(overflow_bits);
+
+ std::vector<int> buffer = vec1;
+ std::vector<int> overflow_pattern_right(buffer.size(), extend_right);
+ buffer = vec_ite(overflow, overflow_pattern_right, buffer);
+
+ for (int i = 0; i < vec2_bits; i++) {
+ std::vector<int> shifted_buffer;
+ shifted_buffer = vec_shift(buffer, -(1 << i), extend_left, extend_right);
+ buffer = vec_ite(vec2[i], shifted_buffer, buffer);
+ }
+
+ buffer.resize(vec1.size());
+ return buffer;
+}
+
+void ezSAT::vec_append(std::vector<int> &vec, const std::vector<int> &vec1) const
+{
+ for (auto bit : vec1)
+ vec.push_back(bit);
+}
+
+void ezSAT::vec_append_signed(std::vector<int> &vec, const std::vector<int> &vec1, int64_t value)
+{
+ assert(int(vec1.size()) <= 64);
+ for (int i = 0; i < int(vec1.size()); i++) {
+ if (((value >> i) & 1) != 0)
+ vec.push_back(vec1[i]);
+ else
+ vec.push_back(NOT(vec1[i]));
+ }
+}
+
+void ezSAT::vec_append_unsigned(std::vector<int> &vec, const std::vector<int> &vec1, uint64_t value)
+{
+ assert(int(vec1.size()) <= 64);
+ for (int i = 0; i < int(vec1.size()); i++) {
+ if (((value >> i) & 1) != 0)
+ vec.push_back(vec1[i]);
+ else
+ vec.push_back(NOT(vec1[i]));
+ }
+}
+
+int64_t ezSAT::vec_model_get_signed(const std::vector<int> &modelExpressions, const std::vector<bool> &modelValues, const std::vector<int> &vec1) const
+{
+ int64_t value = 0;
+ std::map<int, bool> modelMap;
+ assert(modelExpressions.size() == modelValues.size());
+ for (int i = 0; i < int(modelExpressions.size()); i++)
+ modelMap[modelExpressions[i]] = modelValues[i];
+ for (int i = 0; i < 64; i++) {
+ int j = i < int(vec1.size()) ? i : vec1.size()-1;
+ if (modelMap.at(vec1[j]))
+ value |= int64_t(1) << i;
+ }
+ return value;
+}
+
+uint64_t ezSAT::vec_model_get_unsigned(const std::vector<int> &modelExpressions, const std::vector<bool> &modelValues, const std::vector<int> &vec1) const
+{
+ uint64_t value = 0;
+ std::map<int, bool> modelMap;
+ assert(modelExpressions.size() == modelValues.size());
+ for (int i = 0; i < int(modelExpressions.size()); i++)
+ modelMap[modelExpressions[i]] = modelValues[i];
+ for (int i = 0; i < int(vec1.size()); i++)
+ if (modelMap.at(vec1[i]))
+ value |= uint64_t(1) << i;
+ return value;
+}
+
+int ezSAT::vec_reduce_and(const std::vector<int> &vec1)
+{
+ return expression(OpAnd, vec1);
+}
+
+int ezSAT::vec_reduce_or(const std::vector<int> &vec1)
+{
+ return expression(OpOr, vec1);
+}
+
+void ezSAT::vec_set(const std::vector<int> &vec1, const std::vector<int> &vec2)
+{
+ assert(vec1.size() == vec2.size());
+ for (int i = 0; i < int(vec1.size()); i++)
+ SET(vec1[i], vec2[i]);
+}
+
+void ezSAT::vec_set_signed(const std::vector<int> &vec1, int64_t value)
+{
+ assert(int(vec1.size()) <= 64);
+ for (int i = 0; i < int(vec1.size()); i++) {
+ if (((value >> i) & 1) != 0)
+ assume(vec1[i]);
+ else
+ assume(NOT(vec1[i]));
+ }
+}
+
+void ezSAT::vec_set_unsigned(const std::vector<int> &vec1, uint64_t value)
+{
+ assert(int(vec1.size()) <= 64);
+ for (int i = 0; i < int(vec1.size()); i++) {
+ if (((value >> i) & 1) != 0)
+ assume(vec1[i]);
+ else
+ assume(NOT(vec1[i]));
+ }
+}
+
+ezSATbit ezSAT::bit(_V a)
+{
+ return ezSATbit(*this, a);
+}
+
+ezSATvec ezSAT::vec(const std::vector<int> &vec)
+{
+ return ezSATvec(*this, vec);
+}
+
+void ezSAT::printDIMACS(FILE *f, bool verbose) const
+{
+ if (cnfConsumed) {
+ fprintf(stderr, "Usage error: printDIMACS() must not be called after cnfConsumed()!");
+ abort();
+ }
+
+ int digits = ceil(log10f(cnfVariableCount)) + 2;
+
+ fprintf(f, "c generated by ezSAT\n");
+
+ if (verbose)
+ {
+ fprintf(f, "c\n");
+ fprintf(f, "c mapping of variables to literals:\n");
+ for (int i = 0; i < int(cnfLiteralVariables.size()); i++)
+ if (cnfLiteralVariables[i] != 0)
+ fprintf(f, "c %*d: %s\n", digits, cnfLiteralVariables[i], literals[i].c_str());
+
+ fprintf(f, "c\n");
+ fprintf(f, "c mapping of variables to expressions:\n");
+ for (int i = 0; i < int(cnfExpressionVariables.size()); i++)
+ if (cnfExpressionVariables[i] != 0)
+ fprintf(f, "c %*d: %d\n", digits, cnfExpressionVariables[i], -i-1);
+
+ if (mode_keep_cnf()) {
+ fprintf(f, "c\n");
+ fprintf(f, "c %d clauses from backup, %d from current buffer\n",
+ int(cnfClausesBackup.size()), int(cnfClauses.size()));
+ }
+
+ fprintf(f, "c\n");
+ }
+
+ std::vector<std::vector<int>> all_clauses;
+ getFullCnf(all_clauses);
+ assert(cnfClausesCount == int(all_clauses.size()));
+
+ fprintf(f, "p cnf %d %d\n", cnfVariableCount, cnfClausesCount);
+ int maxClauseLen = 0;
+ for (auto &clause : all_clauses)
+ maxClauseLen = std::max(int(clause.size()), maxClauseLen);
+ if (!verbose)
+ maxClauseLen = std::min(maxClauseLen, 3);
+ for (auto &clause : all_clauses) {
+ for (auto idx : clause)
+ fprintf(f, " %*d", digits, idx);
+ if (maxClauseLen >= int(clause.size()))
+ fprintf(f, " %*d\n", (digits + 1)*int(maxClauseLen - clause.size()) + digits, 0);
+ else
+ fprintf(f, " %*d\n", digits, 0);
+ }
+}
+
+static std::string expression2str(const std::pair<ezSAT::OpId, std::vector<int>> &data)
+{
+ std::string text;
+ switch (data.first) {
+#define X(op) case ezSAT::op: text += #op; break;
+ X(OpNot)
+ X(OpAnd)
+ X(OpOr)
+ X(OpXor)
+ X(OpIFF)
+ X(OpITE)
+ default:
+ abort();
+#undef X
+ }
+ text += ":";
+ for (auto it : data.second)
+ text += " " + my_int_to_string(it);
+ return text;
+}
+
+void ezSAT::printInternalState(FILE *f) const
+{
+ fprintf(f, "--8<-- snip --8<--\n");
+
+ fprintf(f, "literalsCache:\n");
+ for (auto &it : literalsCache)
+ fprintf(f, " `%s' -> %d\n", it.first.c_str(), it.second);
+
+ fprintf(f, "literals:\n");
+ for (int i = 0; i < int(literals.size()); i++)
+ fprintf(f, " %d: `%s'\n", i+1, literals[i].c_str());
+
+ fprintf(f, "expressionsCache:\n");
+ for (auto &it : expressionsCache)
+ fprintf(f, " `%s' -> %d\n", expression2str(it.first).c_str(), it.second);
+
+ fprintf(f, "expressions:\n");
+ for (int i = 0; i < int(expressions.size()); i++)
+ fprintf(f, " %d: `%s'\n", -i-1, expression2str(expressions[i]).c_str());
+
+ fprintf(f, "cnfVariables (count=%d):\n", cnfVariableCount);
+ for (int i = 0; i < int(cnfLiteralVariables.size()); i++)
+ if (cnfLiteralVariables[i] != 0)
+ fprintf(f, " literal %d -> %d (%s)\n", i+1, cnfLiteralVariables[i], to_string(i+1).c_str());
+ for (int i = 0; i < int(cnfExpressionVariables.size()); i++)
+ if (cnfExpressionVariables[i] != 0)
+ fprintf(f, " expression %d -> %d (%s)\n", -i-1, cnfExpressionVariables[i], to_string(-i-1).c_str());
+
+ fprintf(f, "cnfClauses:\n");
+ for (auto &i1 : cnfClauses) {
+ for (auto &i2 : i1)
+ fprintf(f, " %4d", i2);
+ fprintf(f, "\n");
+ }
+ if (cnfConsumed)
+ fprintf(f, " *** more clauses consumed via cnfConsume() ***\n");
+
+ fprintf(f, "--8<-- snap --8<--\n");
+}
+
+static int clog2(int x)
+{
+ int y = (x & (x - 1));
+ y = (y | -y) >> 31;
+
+ x |= (x >> 1);
+ x |= (x >> 2);
+ x |= (x >> 4);
+ x |= (x >> 8);
+ x |= (x >> 16);
+
+ x >>= 1;
+ x -= ((x >> 1) & 0x55555555);
+ x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
+ x = (((x >> 4) + x) & 0x0f0f0f0f);
+ x += (x >> 8);
+ x += (x >> 16);
+ x = x & 0x0000003f;
+
+ return x - y;
+}
+
+int ezSAT::onehot(const std::vector<int> &vec, bool max_only)
+{
+ // Mixed one-hot/binary encoding as described by Claessen in Sec. 4.2 of
+ // "Successful SAT Encoding Techniques. Magnus Bjiirk. 25th July 2009".
+ // http://jsat.ewi.tudelft.nl/addendum/Bjork_encoding.pdf
+
+ std::vector<int> formula;
+
+ // add at-leat-one constraint
+ if (max_only == false)
+ formula.push_back(expression(OpOr, vec));
+
+ // create binary vector
+ int num_bits = clog2(vec.size());
+ std::vector<int> bits;
+ for (int k = 0; k < num_bits; k++)
+ bits.push_back(literal());
+
+ // add at-most-one clauses using binary encoding
+ for (size_t i = 0; i < vec.size(); i++)
+ for (int k = 0; k < num_bits; k++) {
+ std::vector<int> clause;
+ clause.push_back(NOT(vec[i]));
+ clause.push_back((i & (1 << k)) != 0 ? bits[k] : NOT(bits[k]));
+ formula.push_back(expression(OpOr, clause));
+ }
+
+ return expression(OpAnd, formula);
+}
+
+int ezSAT::manyhot(const std::vector<int> &vec, int min_hot, int max_hot)
+{
+ // many-hot encoding using a simple sorting network
+
+ if (max_hot < 0)
+ max_hot = min_hot;
+
+ std::vector<int> formula;
+ int M = max_hot+1, N = vec.size();
+ std::map<std::pair<int,int>, int> x;
+
+ for (int i = -1; i < N; i++)
+ for (int j = -1; j < M; j++)
+ x[std::pair<int,int>(i,j)] = j < 0 ? CONST_TRUE : i < 0 ? CONST_FALSE : literal();
+
+ for (int i = 0; i < N; i++)
+ for (int j = 0; j < M; j++) {
+ formula.push_back(OR(NOT(vec[i]), x[std::pair<int,int>(i-1,j-1)], NOT(x[std::pair<int,int>(i,j)])));
+ formula.push_back(OR(NOT(vec[i]), NOT(x[std::pair<int,int>(i-1,j-1)]), x[std::pair<int,int>(i,j)]));
+ formula.push_back(OR(vec[i], x[std::pair<int,int>(i-1,j)], NOT(x[std::pair<int,int>(i,j)])));
+ formula.push_back(OR(vec[i], NOT(x[std::pair<int,int>(i-1,j)]), x[std::pair<int,int>(i,j)]));
+#if 0
+ // explicit resolution clauses -- in tests it was better to let the sat solver figure those out
+ formula.push_back(OR(NOT(x[std::pair<int,int>(i-1,j-1)]), NOT(x[std::pair<int,int>(i-1,j)]), x[std::pair<int,int>(i,j)]));
+ formula.push_back(OR(x[std::pair<int,int>(i-1,j-1)], x[std::pair<int,int>(i-1,j)], NOT(x[std::pair<int,int>(i,j)])));
+#endif
+ }
+
+ for (int j = 0; j < M; j++) {
+ if (j+1 <= min_hot)
+ formula.push_back(x[std::pair<int,int>(N-1,j)]);
+ else if (j+1 > max_hot)
+ formula.push_back(NOT(x[std::pair<int,int>(N-1,j)]));
+ }
+
+ return expression(OpAnd, formula);
+}
+
+int ezSAT::ordered(const std::vector<int> &vec1, const std::vector<int> &vec2, bool allow_equal)
+{
+ std::vector<int> formula;
+ int last_x = CONST_FALSE;
+
+ assert(vec1.size() == vec2.size());
+ for (size_t i = 0; i < vec1.size(); i++)
+ {
+ int a = vec1[i], b = vec2[i];
+ formula.push_back(OR(NOT(a), b, last_x));
+
+ int next_x = i+1 < vec1.size() ? literal() : allow_equal ? CONST_FALSE : CONST_TRUE;
+ formula.push_back(OR(a, b, last_x, NOT(next_x)));
+ formula.push_back(OR(NOT(a), NOT(b), last_x, NOT(next_x)));
+ last_x = next_x;
+ }
+
+ return expression(OpAnd, formula);
+}
+
diff --git a/libs/ezsat/ezsat.h b/libs/ezsat/ezsat.h
new file mode 100644
index 00000000..85b13685
--- /dev/null
+++ b/libs/ezsat/ezsat.h
@@ -0,0 +1,359 @@
+/*
+ * ezSAT -- A simple and easy to use CNF generator for SAT solvers
+ *
+ * Copyright (C) 2013 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.
+ *
+ */
+
+#ifndef EZSAT_H
+#define EZSAT_H
+
+#include <set>
+#include <map>
+#include <vector>
+#include <string>
+#include <stdio.h>
+#include <stdint.h>
+
+class ezSAT
+{
+ // each token (terminal or non-terminal) is represented by an integer number
+ //
+ // the zero token:
+ // the number zero is not used as valid token number and is used to encode
+ // unused parameters for the functions.
+ //
+ // positive numbers are literals, with 1 = CONST_TRUE and 2 = CONST_FALSE;
+ //
+ // negative numbers are non-literal expressions. each expression is represented
+ // by an operator id and a list of expressions (literals or non-literals).
+
+public:
+ enum OpId {
+ OpNot, OpAnd, OpOr, OpXor, OpIFF, OpITE
+ };
+
+ static const int CONST_TRUE;
+ static const int CONST_FALSE;
+
+private:
+ bool flag_keep_cnf;
+ bool flag_non_incremental;
+
+ bool non_incremental_solve_used_up;
+
+ std::map<std::string, int> literalsCache;
+ std::vector<std::string> literals;
+
+ std::map<std::pair<OpId, std::vector<int>>, int> expressionsCache;
+ std::vector<std::pair<OpId, std::vector<int>>> expressions;
+
+ bool cnfConsumed;
+ int cnfVariableCount, cnfClausesCount;
+ std::vector<int> cnfLiteralVariables, cnfExpressionVariables;
+ std::vector<std::vector<int>> cnfClauses, cnfClausesBackup;
+
+ void add_clause(const std::vector<int> &args);
+ void add_clause(const std::vector<int> &args, bool argsPolarity, int a = 0, int b = 0, int c = 0);
+ void add_clause(int a, int b = 0, int c = 0);
+
+ int bind_cnf_not(const std::vector<int> &args);
+ int bind_cnf_and(const std::vector<int> &args);
+ int bind_cnf_or(const std::vector<int> &args);
+
+protected:
+ void preSolverCallback();
+
+public:
+ int solverTimeout;
+ bool solverTimoutStatus;
+
+ ezSAT();
+ virtual ~ezSAT();
+
+ unsigned int statehash;
+ void addhash(unsigned int);
+
+ void keep_cnf() { flag_keep_cnf = true; }
+ void non_incremental() { flag_non_incremental = true; }
+
+ bool mode_keep_cnf() const { return flag_keep_cnf; }
+ bool mode_non_incremental() const { return flag_non_incremental; }
+
+ // manage expressions
+
+ int value(bool val);
+ int literal();
+ int literal(const std::string &name);
+ int frozen_literal();
+ int frozen_literal(const std::string &name);
+ int expression(OpId op, int a = 0, int b = 0, int c = 0, int d = 0, int e = 0, int f = 0);
+ int expression(OpId op, const std::vector<int> &args);
+
+ void lookup_literal(int id, std::string &name) const;
+ const std::string &lookup_literal(int id) const;
+
+ void lookup_expression(int id, OpId &op, std::vector<int> &args) const;
+ const std::vector<int> &lookup_expression(int id, OpId &op) const;
+
+ int parse_string(const std::string &text);
+ std::string to_string(int id) const;
+
+ int numLiterals() const { return literals.size(); }
+ int numExpressions() const { return expressions.size(); }
+
+ int eval(int id, const std::vector<int> &values) const;
+
+ // SAT solver interface
+ // If you are planning on using the solver API (and not simply create a CNF) you must use a child class
+ // of ezSAT that actually implements a solver backend, such as ezMiniSAT (see ezminisat.h).
+
+ virtual bool solver(const std::vector<int> &modelExpressions, std::vector<bool> &modelValues, const std::vector<int> &assumptions);
+
+ bool solve(const std::vector<int> &modelExpressions, std::vector<bool> &modelValues, const std::vector<int> &assumptions) {
+ return solver(modelExpressions, modelValues, assumptions);
+ }
+
+ bool solve(const std::vector<int> &modelExpressions, std::vector<bool> &modelValues, int a = 0, int b = 0, int c = 0, int d = 0, int e = 0, int f = 0) {
+ std::vector<int> assumptions;
+ if (a != 0) assumptions.push_back(a);
+ if (b != 0) assumptions.push_back(b);
+ if (c != 0) assumptions.push_back(c);
+ if (d != 0) assumptions.push_back(d);
+ if (e != 0) assumptions.push_back(e);
+ if (f != 0) assumptions.push_back(f);
+ return solver(modelExpressions, modelValues, assumptions);
+ }
+
+ bool solve(int a = 0, int b = 0, int c = 0, int d = 0, int e = 0, int f = 0) {
+ std::vector<int> assumptions, modelExpressions;
+ std::vector<bool> modelValues;
+ if (a != 0) assumptions.push_back(a);
+ if (b != 0) assumptions.push_back(b);
+ if (c != 0) assumptions.push_back(c);
+ if (d != 0) assumptions.push_back(d);
+ if (e != 0) assumptions.push_back(e);
+ if (f != 0) assumptions.push_back(f);
+ return solver(modelExpressions, modelValues, assumptions);
+ }
+
+ void setSolverTimeout(int newTimeoutSeconds) {
+ solverTimeout = newTimeoutSeconds;
+ }
+
+ bool getSolverTimoutStatus() {
+ return solverTimoutStatus;
+ }
+
+ // manage CNF (usually only accessed by SAT solvers)
+
+ virtual void clear();
+ virtual void freeze(int id);
+ virtual bool eliminated(int idx);
+ void assume(int id);
+ void assume(int id, int context_id) { assume(OR(id, NOT(context_id))); }
+ int bind(int id, bool auto_freeze = true);
+ int bound(int id) const;
+
+ int numCnfVariables() const { return cnfVariableCount; }
+ int numCnfClauses() const { return cnfClausesCount; }
+ const std::vector<std::vector<int>> &cnf() const { return cnfClauses; }
+
+ void consumeCnf();
+ void consumeCnf(std::vector<std::vector<int>> &cnf);
+
+ // use this function to get the full CNF in keep_cnf mode
+ void getFullCnf(std::vector<std::vector<int>> &full_cnf) const;
+
+ std::string cnfLiteralInfo(int idx) const;
+
+ // simple helpers for build expressions easily
+
+ struct _V {
+ int id;
+ std::string name;
+ _V(int id) : id(id) { }
+ _V(const char *name) : id(0), name(name) { }
+ _V(const std::string &name) : id(0), name(name) { }
+ int get(ezSAT *that) {
+ if (name.empty())
+ return id;
+ return that->frozen_literal(name);
+ }
+ };
+
+ int VAR(_V a) {
+ return a.get(this);
+ }
+
+ int NOT(_V a) {
+ return expression(OpNot, a.get(this));
+ }
+
+ int AND(_V a = 0, _V b = 0, _V c = 0, _V d = 0, _V e = 0, _V f = 0) {
+ return expression(OpAnd, a.get(this), b.get(this), c.get(this), d.get(this), e.get(this), f.get(this));
+ }
+
+ int OR(_V a = 0, _V b = 0, _V c = 0, _V d = 0, _V e = 0, _V f = 0) {
+ return expression(OpOr, a.get(this), b.get(this), c.get(this), d.get(this), e.get(this), f.get(this));
+ }
+
+ int XOR(_V a = 0, _V b = 0, _V c = 0, _V d = 0, _V e = 0, _V f = 0) {
+ return expression(OpXor, a.get(this), b.get(this), c.get(this), d.get(this), e.get(this), f.get(this));
+ }
+
+ int IFF(_V a, _V b = 0, _V c = 0, _V d = 0, _V e = 0, _V f = 0) {
+ return expression(OpIFF, a.get(this), b.get(this), c.get(this), d.get(this), e.get(this), f.get(this));
+ }
+
+ int ITE(_V a, _V b, _V c) {
+ return expression(OpITE, a.get(this), b.get(this), c.get(this));
+ }
+
+ void SET(_V a, _V b) {
+ assume(IFF(a.get(this), b.get(this)));
+ }
+
+ // simple helpers for building expressions with bit vectors
+
+ std::vector<int> vec_const(const std::vector<bool> &bits);
+ std::vector<int> vec_const_signed(int64_t value, int numBits);
+ std::vector<int> vec_const_unsigned(uint64_t value, int numBits);
+ std::vector<int> vec_var(int numBits);
+ std::vector<int> vec_var(std::string name, int numBits);
+ std::vector<int> vec_cast(const std::vector<int> &vec1, int toBits, bool signExtend = false);
+
+ std::vector<int> vec_not(const std::vector<int> &vec1);
+ std::vector<int> vec_and(const std::vector<int> &vec1, const std::vector<int> &vec2);
+ std::vector<int> vec_or(const std::vector<int> &vec1, const std::vector<int> &vec2);
+ std::vector<int> vec_xor(const std::vector<int> &vec1, const std::vector<int> &vec2);
+
+ std::vector<int> vec_iff(const std::vector<int> &vec1, const std::vector<int> &vec2);
+ std::vector<int> vec_ite(const std::vector<int> &vec1, const std::vector<int> &vec2, const std::vector<int> &vec3);
+ std::vector<int> vec_ite(int sel, const std::vector<int> &vec1, const std::vector<int> &vec2);
+
+ std::vector<int> vec_count(const std::vector<int> &vec, int numBits, bool clip = true);
+ std::vector<int> vec_add(const std::vector<int> &vec1, const std::vector<int> &vec2);
+ std::vector<int> vec_sub(const std::vector<int> &vec1, const std::vector<int> &vec2);
+ std::vector<int> vec_neg(const std::vector<int> &vec);
+
+ void vec_cmp(const std::vector<int> &vec1, const std::vector<int> &vec2, int &carry, int &overflow, int &sign, int &zero);
+
+ int vec_lt_signed(const std::vector<int> &vec1, const std::vector<int> &vec2);
+ int vec_le_signed(const std::vector<int> &vec1, const std::vector<int> &vec2);
+ int vec_ge_signed(const std::vector<int> &vec1, const std::vector<int> &vec2);
+ int vec_gt_signed(const std::vector<int> &vec1, const std::vector<int> &vec2);
+
+ int vec_lt_unsigned(const std::vector<int> &vec1, const std::vector<int> &vec2);
+ int vec_le_unsigned(const std::vector<int> &vec1, const std::vector<int> &vec2);
+ int vec_ge_unsigned(const std::vector<int> &vec1, const std::vector<int> &vec2);
+ int vec_gt_unsigned(const std::vector<int> &vec1, const std::vector<int> &vec2);
+
+ int vec_eq(const std::vector<int> &vec1, const std::vector<int> &vec2);
+ int vec_ne(const std::vector<int> &vec1, const std::vector<int> &vec2);
+
+ std::vector<int> vec_shl(const std::vector<int> &vec1, int shift, bool signExtend = false);
+ std::vector<int> vec_srl(const std::vector<int> &vec1, int shift);
+
+ std::vector<int> vec_shr(const std::vector<int> &vec1, int shift, bool signExtend = false) { return vec_shl(vec1, -shift, signExtend); }
+ std::vector<int> vec_srr(const std::vector<int> &vec1, int shift) { return vec_srl(vec1, -shift); }
+
+ std::vector<int> vec_shift(const std::vector<int> &vec1, int shift, int extend_left, int extend_right);
+ std::vector<int> vec_shift_right(const std::vector<int> &vec1, const std::vector<int> &vec2, bool vec2_signed, int extend_left, int extend_right);
+ std::vector<int> vec_shift_left(const std::vector<int> &vec1, const std::vector<int> &vec2, bool vec2_signed, int extend_left, int extend_right);
+
+ void vec_append(std::vector<int> &vec, const std::vector<int> &vec1) const;
+ void vec_append_signed(std::vector<int> &vec, const std::vector<int> &vec1, int64_t value);
+ void vec_append_unsigned(std::vector<int> &vec, const std::vector<int> &vec1, uint64_t value);
+
+ int64_t vec_model_get_signed(const std::vector<int> &modelExpressions, const std::vector<bool> &modelValues, const std::vector<int> &vec1) const;
+ uint64_t vec_model_get_unsigned(const std::vector<int> &modelExpressions, const std::vector<bool> &modelValues, const std::vector<int> &vec1) const;
+
+ int vec_reduce_and(const std::vector<int> &vec1);
+ int vec_reduce_or(const std::vector<int> &vec1);
+
+ void vec_set(const std::vector<int> &vec1, const std::vector<int> &vec2);
+ void vec_set_signed(const std::vector<int> &vec1, int64_t value);
+ void vec_set_unsigned(const std::vector<int> &vec1, uint64_t value);
+
+ // helpers for generating ezSATbit and ezSATvec objects
+
+ struct ezSATbit bit(_V a);
+ struct ezSATvec vec(const std::vector<int> &vec);
+
+ // printing CNF and internal state
+
+ void printDIMACS(FILE *f, bool verbose = false) const;
+ void printInternalState(FILE *f) const;
+
+ // more sophisticated constraints (designed to be used directly with assume(..))
+
+ int onehot(const std::vector<int> &vec, bool max_only = false);
+ int manyhot(const std::vector<int> &vec, int min_hot, int max_hot = -1);
+ int ordered(const std::vector<int> &vec1, const std::vector<int> &vec2, bool allow_equal = true);
+};
+
+// helper classes for using operator overloading when generating complex expressions
+
+struct ezSATbit
+{
+ ezSAT &sat;
+ int id;
+
+ ezSATbit(ezSAT &sat, ezSAT::_V a) : sat(sat), id(sat.VAR(a)) { }
+
+ ezSATbit operator ~() { return ezSATbit(sat, sat.NOT(id)); }
+ ezSATbit operator &(const ezSATbit &other) { return ezSATbit(sat, sat.AND(id, other.id)); }
+ ezSATbit operator |(const ezSATbit &other) { return ezSATbit(sat, sat.OR(id, other.id)); }
+ ezSATbit operator ^(const ezSATbit &other) { return ezSATbit(sat, sat.XOR(id, other.id)); }
+ ezSATbit operator ==(const ezSATbit &other) { return ezSATbit(sat, sat.IFF(id, other.id)); }
+ ezSATbit operator !=(const ezSATbit &other) { return ezSATbit(sat, sat.NOT(sat.IFF(id, other.id))); }
+
+ operator int() const { return id; }
+ operator ezSAT::_V() const { return ezSAT::_V(id); }
+ operator std::vector<int>() const { return std::vector<int>(1, id); }
+};
+
+struct ezSATvec
+{
+ ezSAT &sat;
+ std::vector<int> vec;
+
+ ezSATvec(ezSAT &sat, const std::vector<int> &vec) : sat(sat), vec(vec) { }
+
+ ezSATvec operator ~() { return ezSATvec(sat, sat.vec_not(vec)); }
+ ezSATvec operator -() { return ezSATvec(sat, sat.vec_neg(vec)); }
+
+ ezSATvec operator &(const ezSATvec &other) { return ezSATvec(sat, sat.vec_and(vec, other.vec)); }
+ ezSATvec operator |(const ezSATvec &other) { return ezSATvec(sat, sat.vec_or(vec, other.vec)); }
+ ezSATvec operator ^(const ezSATvec &other) { return ezSATvec(sat, sat.vec_xor(vec, other.vec)); }
+
+ ezSATvec operator +(const ezSATvec &other) { return ezSATvec(sat, sat.vec_add(vec, other.vec)); }
+ ezSATvec operator -(const ezSATvec &other) { return ezSATvec(sat, sat.vec_sub(vec, other.vec)); }
+
+ ezSATbit operator < (const ezSATvec &other) { return ezSATbit(sat, sat.vec_lt_unsigned(vec, other.vec)); }
+ ezSATbit operator <=(const ezSATvec &other) { return ezSATbit(sat, sat.vec_le_unsigned(vec, other.vec)); }
+ ezSATbit operator ==(const ezSATvec &other) { return ezSATbit(sat, sat.vec_eq(vec, other.vec)); }
+ ezSATbit operator !=(const ezSATvec &other) { return ezSATbit(sat, sat.vec_ne(vec, other.vec)); }
+ ezSATbit operator >=(const ezSATvec &other) { return ezSATbit(sat, sat.vec_ge_unsigned(vec, other.vec)); }
+ ezSATbit operator > (const ezSATvec &other) { return ezSATbit(sat, sat.vec_gt_unsigned(vec, other.vec)); }
+
+ ezSATvec operator <<(int shift) { return ezSATvec(sat, sat.vec_shl(vec, shift)); }
+ ezSATvec operator >>(int shift) { return ezSATvec(sat, sat.vec_shr(vec, shift)); }
+
+ operator std::vector<int>() const { return vec; }
+};
+
+#endif
diff --git a/libs/ezsat/puzzle3d.cc b/libs/ezsat/puzzle3d.cc
new file mode 100644
index 00000000..59f840f9
--- /dev/null
+++ b/libs/ezsat/puzzle3d.cc
@@ -0,0 +1,295 @@
+/*
+ * ezSAT -- A simple and easy to use CNF generator for SAT solvers
+ *
+ * Copyright (C) 2013 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 "ezminisat.h"
+#include <stdio.h>
+#include <assert.h>
+
+#define DIM_X 5
+#define DIM_Y 5
+#define DIM_Z 5
+
+#define NUM_124 6
+#define NUM_223 6
+
+ezMiniSAT ez;
+int blockidx = 0;
+std::map<int, std::string> blockinfo;
+std::vector<int> grid[DIM_X][DIM_Y][DIM_Z];
+
+struct blockgeom_t
+{
+ int center_x, center_y, center_z;
+ int size_x, size_y, size_z;
+ int var;
+
+ void mirror_x() { center_x *= -1; }
+ void mirror_y() { center_y *= -1; }
+ void mirror_z() { center_z *= -1; }
+
+ void rotate_x() { int tmp[4] = { center_y, center_z, size_y, size_z }; center_y = tmp[1]; center_z = -tmp[0]; size_y = tmp[3]; size_z = tmp[2]; }
+ void rotate_y() { int tmp[4] = { center_x, center_z, size_x, size_z }; center_x = tmp[1]; center_z = -tmp[0]; size_x = tmp[3]; size_z = tmp[2]; }
+ void rotate_z() { int tmp[4] = { center_x, center_y, size_x, size_y }; center_x = tmp[1]; center_y = -tmp[0]; size_x = tmp[3]; size_y = tmp[2]; }
+
+ bool operator< (const blockgeom_t &other) const {
+ if (center_x != other.center_x) return center_x < other.center_x;
+ if (center_y != other.center_y) return center_y < other.center_y;
+ if (center_z != other.center_z) return center_z < other.center_z;
+ if (size_x != other.size_x) return size_x < other.size_x;
+ if (size_y != other.size_y) return size_y < other.size_y;
+ if (size_z != other.size_z) return size_z < other.size_z;
+ if (var != other.var) return var < other.var;
+ return false;
+ }
+};
+
+// geometry data for spatial symmetry constraints
+std::set<blockgeom_t> blockgeom;
+
+int add_block(int pos_x, int pos_y, int pos_z, int size_x, int size_y, int size_z, int blockidx)
+{
+ char buffer[1024];
+ snprintf(buffer, 1024, "block(%d,%d,%d,%d,%d,%d,%d);", size_x, size_y, size_z, pos_x, pos_y, pos_z, blockidx);
+
+ int var = ez.literal();
+ blockinfo[var] = buffer;
+
+ for (int ix = pos_x; ix < pos_x+size_x; ix++)
+ for (int iy = pos_y; iy < pos_y+size_y; iy++)
+ for (int iz = pos_z; iz < pos_z+size_z; iz++)
+ grid[ix][iy][iz].push_back(var);
+
+ blockgeom_t bg;
+ bg.size_x = 2*size_x;
+ bg.size_y = 2*size_y;
+ bg.size_z = 2*size_z;
+ bg.center_x = (2*pos_x + size_x) - DIM_X;
+ bg.center_y = (2*pos_y + size_y) - DIM_Y;
+ bg.center_z = (2*pos_z + size_z) - DIM_Z;
+ bg.var = var;
+
+ assert(blockgeom.count(bg) == 0);
+ blockgeom.insert(bg);
+
+ return var;
+}
+
+void add_block_positions_124(std::vector<int> &block_positions_124)
+{
+ block_positions_124.clear();
+ for (int size_x = 1; size_x <= 4; size_x *= 2)
+ for (int size_y = 1; size_y <= 4; size_y *= 2)
+ for (int size_z = 1; size_z <= 4; size_z *= 2) {
+ if (size_x == size_y || size_y == size_z || size_z == size_x)
+ continue;
+ for (int ix = 0; ix <= DIM_X-size_x; ix++)
+ for (int iy = 0; iy <= DIM_Y-size_y; iy++)
+ for (int iz = 0; iz <= DIM_Z-size_z; iz++)
+ block_positions_124.push_back(add_block(ix, iy, iz, size_x, size_y, size_z, blockidx++));
+ }
+}
+
+void add_block_positions_223(std::vector<int> &block_positions_223)
+{
+ block_positions_223.clear();
+ for (int orientation = 0; orientation < 3; orientation++) {
+ int size_x = orientation == 0 ? 3 : 2;
+ int size_y = orientation == 1 ? 3 : 2;
+ int size_z = orientation == 2 ? 3 : 2;
+ for (int ix = 0; ix <= DIM_X-size_x; ix++)
+ for (int iy = 0; iy <= DIM_Y-size_y; iy++)
+ for (int iz = 0; iz <= DIM_Z-size_z; iz++)
+ block_positions_223.push_back(add_block(ix, iy, iz, size_x, size_y, size_z, blockidx++));
+ }
+}
+
+// use simple built-in random number generator to
+// ensure determinism of the program across platforms
+uint32_t xorshift32() {
+ static uint32_t x = 314159265;
+ x ^= x << 13;
+ x ^= x >> 17;
+ x ^= x << 5;
+ return x;
+}
+
+void condense_exclusives(std::vector<int> &vars)
+{
+ std::map<int, std::set<int>> exclusive;
+
+ for (int ix = 0; ix < DIM_X; ix++)
+ for (int iy = 0; iy < DIM_Y; iy++)
+ for (int iz = 0; iz < DIM_Z; iz++) {
+ for (int a : grid[ix][iy][iz])
+ for (int b : grid[ix][iy][iz])
+ if (a != b)
+ exclusive[a].insert(b);
+ }
+
+ std::vector<std::vector<int>> pools;
+
+ for (int a : vars)
+ {
+ std::vector<int> candidate_pools;
+ for (size_t i = 0; i < pools.size(); i++)
+ {
+ for (int b : pools[i])
+ if (exclusive[a].count(b) == 0)
+ goto no_candidate_pool;
+ candidate_pools.push_back(i);
+ no_candidate_pool:;
+ }
+
+ if (candidate_pools.size() > 0) {
+ int p = candidate_pools[xorshift32() % candidate_pools.size()];
+ pools[p].push_back(a);
+ } else {
+ pools.push_back(std::vector<int>());
+ pools.back().push_back(a);
+ }
+ }
+
+ std::vector<int> new_vars;
+ for (auto &pool : pools)
+ {
+ std::vector<int> formula;
+ int var = ez.literal();
+
+ for (int a : pool)
+ formula.push_back(ez.OR(ez.NOT(a), var));
+ formula.push_back(ez.OR(ez.expression(ezSAT::OpOr, pool), ez.NOT(var)));
+
+ ez.assume(ez.onehot(pool, true));
+ ez.assume(ez.expression(ezSAT::OpAnd, formula));
+ new_vars.push_back(var);
+ }
+
+ printf("Condensed %d variables into %d one-hot pools.\n", int(vars.size()), int(new_vars.size()));
+ vars.swap(new_vars);
+}
+
+int main()
+{
+ printf("\nCreating SAT encoding..\n");
+
+ // add 1x2x4 blocks
+ std::vector<int> block_positions_124;
+ add_block_positions_124(block_positions_124);
+ condense_exclusives(block_positions_124);
+ ez.assume(ez.manyhot(block_positions_124, NUM_124));
+
+ // add 2x2x3 blocks
+ std::vector<int> block_positions_223;
+ add_block_positions_223(block_positions_223);
+ condense_exclusives(block_positions_223);
+ ez.assume(ez.manyhot(block_positions_223, NUM_223));
+
+ // add constraint for max one block per grid element
+ for (int ix = 0; ix < DIM_X; ix++)
+ for (int iy = 0; iy < DIM_Y; iy++)
+ for (int iz = 0; iz < DIM_Z; iz++) {
+ assert(grid[ix][iy][iz].size() > 0);
+ ez.assume(ez.onehot(grid[ix][iy][iz], true));
+ }
+
+ printf("Found %d possible block positions.\n", int(blockgeom.size()));
+
+ // look for spatial symmetries
+ std::set<std::set<blockgeom_t>> symmetries;
+ symmetries.insert(blockgeom);
+ bool keep_running = true;
+ while (keep_running) {
+ keep_running = false;
+ std::set<std::set<blockgeom_t>> old_sym;
+ old_sym.swap(symmetries);
+ for (auto &old_sym_set : old_sym)
+ {
+ std::set<blockgeom_t> mx, my, mz;
+ std::set<blockgeom_t> rx, ry, rz;
+ for (auto &bg : old_sym_set) {
+ blockgeom_t bg_mx = bg, bg_my = bg, bg_mz = bg;
+ blockgeom_t bg_rx = bg, bg_ry = bg, bg_rz = bg;
+ bg_mx.mirror_x(), bg_my.mirror_y(), bg_mz.mirror_z();
+ bg_rx.rotate_x(), bg_ry.rotate_y(), bg_rz.rotate_z();
+ mx.insert(bg_mx), my.insert(bg_my), mz.insert(bg_mz);
+ rx.insert(bg_rx), ry.insert(bg_ry), rz.insert(bg_rz);
+ }
+ if (!old_sym.count(mx) || !old_sym.count(my) || !old_sym.count(mz) ||
+ !old_sym.count(rx) || !old_sym.count(ry) || !old_sym.count(rz))
+ keep_running = true;
+ symmetries.insert(old_sym_set);
+ symmetries.insert(mx);
+ symmetries.insert(my);
+ symmetries.insert(mz);
+ symmetries.insert(rx);
+ symmetries.insert(ry);
+ symmetries.insert(rz);
+ }
+ }
+
+ // add constraints to eliminate all the spatial symmetries
+ std::vector<std::vector<int>> vecvec;
+ for (auto &sym : symmetries) {
+ std::vector<int> vec;
+ for (auto &bg : sym)
+ vec.push_back(bg.var);
+ vecvec.push_back(vec);
+ }
+ for (size_t i = 1; i < vecvec.size(); i++)
+ ez.assume(ez.ordered(vecvec[0], vecvec[1]));
+
+ printf("Found and eliminated %d spatial symmetries.\n", int(symmetries.size()));
+ printf("Generated %d clauses over %d variables.\n", ez.numCnfClauses(), ez.numCnfVariables());
+
+ std::vector<int> modelExpressions;
+ std::vector<bool> modelValues;
+
+ for (auto &it : blockinfo) {
+ ez.freeze(it.first);
+ modelExpressions.push_back(it.first);
+ }
+
+ int solution_counter = 0;
+ while (1)
+ {
+ printf("\nSolving puzzle..\n");
+ bool ok = ez.solve(modelExpressions, modelValues);
+
+ if (!ok) {
+ printf("No more solutions found!\n");
+ break;
+ }
+
+ printf("Puzzle solution:\n");
+ std::vector<int> constraint;
+ for (size_t i = 0; i < modelExpressions.size(); i++)
+ if (modelValues[i]) {
+ constraint.push_back(ez.NOT(modelExpressions[i]));
+ printf("%s\n", blockinfo.at(modelExpressions[i]).c_str());
+ }
+ ez.assume(ez.expression(ezSAT::OpOr, constraint));
+ solution_counter++;
+ }
+
+ printf("\nFound %d distinct solutions.\n", solution_counter);
+ printf("Have a nice day.\n\n");
+
+ return 0;
+}
+
diff --git a/libs/ezsat/puzzle3d.scad b/libs/ezsat/puzzle3d.scad
new file mode 100644
index 00000000..693f8d85
--- /dev/null
+++ b/libs/ezsat/puzzle3d.scad
@@ -0,0 +1,82 @@
+
+gap = 30;
+layers = 0;
+variant = 1;
+
+module block(size_x, size_y, size_z, pos_x, pos_y, pos_z, idx)
+{
+ col = idx % 6 == 0 ? [ 0, 0, 1 ] :
+ idx % 6 == 1 ? [ 0, 1, 0 ] :
+ idx % 6 == 2 ? [ 0, 1, 1 ] :
+ idx % 6 == 3 ? [ 1, 0, 0 ] :
+ idx % 6 == 4 ? [ 1, 0, 1 ] :
+ idx % 6 == 5 ? [ 1, 1, 0 ] : [ 0, 0, 0 ];
+ translate([-2.5, -2.5, 0] * (100+gap)) difference() {
+ color(col) translate([pos_x, pos_y, pos_z] * (100 + gap))
+ cube([size_x, size_y, size_z] * (100+gap) - [gap, gap, gap], false);
+ if (layers > 0)
+ color([0.3, 0.3, 0.3]) translate([0, 0, layers * (100+gap)] - [0.5, 0.5, 0.5] * gap)
+ cube([5, 5, 5] * (100 + gap), false);
+ }
+}
+
+if (variant == 1) {
+ block(1,4,2,0,1,3,47);
+ block(1,4,2,4,0,0,72);
+ block(2,1,4,0,0,0,80);
+ block(2,1,4,3,4,1,119);
+ block(4,2,1,0,3,0,215);
+ block(4,2,1,1,0,4,224);
+ block(3,2,2,0,3,1,253);
+ block(3,2,2,2,0,2,274);
+ block(2,3,2,1,2,3,311);
+ block(2,3,2,2,0,0,312);
+ block(2,2,3,0,1,0,339);
+ block(2,2,3,3,2,2,380);
+}
+
+if (variant == 2) {
+ block(1,2,4,0,0,1,1);
+ block(1,2,4,4,3,0,38);
+ block(2,4,1,0,1,0,125);
+ block(2,4,1,3,0,4,154);
+ block(4,1,2,0,4,3,179);
+ block(4,1,2,1,0,0,180);
+ block(3,2,2,0,2,3,251);
+ block(3,2,2,2,1,0,276);
+ block(2,3,2,0,2,1,297);
+ block(2,3,2,3,0,2,326);
+ block(2,2,3,1,0,2,350);
+ block(2,2,3,2,3,0,369);
+}
+
+if (variant == 3) {
+ block(1,4,2,0,0,3,43);
+ block(1,4,2,4,1,0,76);
+ block(2,1,4,0,4,0,88);
+ block(2,1,4,3,0,1,111);
+ block(4,2,1,0,0,0,200);
+ block(4,2,1,1,3,4,239);
+ block(3,2,2,0,0,1,241);
+ block(3,2,2,2,3,2,286);
+ block(2,3,2,1,0,3,303);
+ block(2,3,2,2,2,0,320);
+ block(2,2,3,0,2,0,342);
+ block(2,2,3,3,1,2,377);
+}
+
+if (variant == 4) {
+ block(1,2,4,0,3,1,7);
+ block(1,2,4,4,0,0,32);
+ block(2,4,1,0,0,0,120);
+ block(2,4,1,3,1,4,159);
+ block(4,1,2,0,0,3,163);
+ block(4,1,2,1,4,0,196);
+ block(3,2,2,0,1,3,247);
+ block(3,2,2,2,2,0,280);
+ block(2,3,2,0,0,1,289);
+ block(2,3,2,3,2,2,334);
+ block(2,2,3,1,3,2,359);
+ block(2,2,3,2,0,0,360);
+}
+
diff --git a/libs/ezsat/testbench.cc b/libs/ezsat/testbench.cc
new file mode 100644
index 00000000..d6dc41fa
--- /dev/null
+++ b/libs/ezsat/testbench.cc
@@ -0,0 +1,441 @@
+/*
+ * ezSAT -- A simple and easy to use CNF generator for SAT solvers
+ *
+ * Copyright (C) 2013 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 "ezminisat.h"
+#include <stdio.h>
+
+struct xorshift128 {
+ uint32_t x, y, z, w;
+ xorshift128() {
+ x = 123456789;
+ y = 362436069;
+ z = 521288629;
+ w = 88675123;
+ }
+ uint32_t operator()() {
+ uint32_t t = x ^ (x << 11);
+ x = y; y = z; z = w;
+ w ^= (w >> 19) ^ t ^ (t >> 8);
+ return w;
+ }
+};
+
+bool test(ezSAT &sat, int assumption = 0)
+{
+ std::vector<int> modelExpressions;
+ std::vector<bool> modelValues;
+
+ for (int id = 1; id <= sat.numLiterals(); id++)
+ if (sat.bound(id))
+ modelExpressions.push_back(id);
+
+ if (sat.solve(modelExpressions, modelValues, assumption)) {
+ printf("satisfiable:");
+ for (int i = 0; i < int(modelExpressions.size()); i++)
+ printf(" %s=%d", sat.to_string(modelExpressions[i]).c_str(), int(modelValues[i]));
+ printf("\n\n");
+ return true;
+ } else {
+ printf("not satisfiable.\n\n");
+ return false;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------------------
+
+void test_simple()
+{
+ printf("==== %s ====\n\n", __PRETTY_FUNCTION__);
+
+ ezMiniSAT sat;
+ sat.non_incremental();
+ sat.assume(sat.OR("A", "B"));
+ sat.assume(sat.NOT(sat.AND("A", "B")));
+ test(sat);
+}
+
+// ------------------------------------------------------------------------------------------------------------
+
+void test_xorshift32_try(ezSAT &sat, uint32_t input_pattern)
+{
+ uint32_t output_pattern = input_pattern;
+ output_pattern ^= output_pattern << 13;
+ output_pattern ^= output_pattern >> 17;
+ output_pattern ^= output_pattern << 5;
+
+ std::vector<int> modelExpressions;
+ std::vector<int> forwardAssumptions, backwardAssumptions;
+ std::vector<bool> forwardModel, backwardModel;
+
+ sat.vec_append(modelExpressions, sat.vec_var("i", 32));
+ sat.vec_append(modelExpressions, sat.vec_var("o", 32));
+
+ sat.vec_append_unsigned(forwardAssumptions, sat.vec_var("i", 32), input_pattern);
+ sat.vec_append_unsigned(backwardAssumptions, sat.vec_var("o", 32), output_pattern);
+
+ if (!sat.solve(modelExpressions, backwardModel, backwardAssumptions)) {
+ printf("backward solving failed!\n");
+ abort();
+ }
+
+ if (!sat.solve(modelExpressions, forwardModel, forwardAssumptions)) {
+ printf("forward solving failed!\n");
+ abort();
+ }
+
+ printf("xorshift32 test with input pattern 0x%08x:\n", input_pattern);
+
+ printf("forward solution: input=0x%08x output=0x%08x\n",
+ (unsigned int)sat.vec_model_get_unsigned(modelExpressions, forwardModel, sat.vec_var("i", 32)),
+ (unsigned int)sat.vec_model_get_unsigned(modelExpressions, forwardModel, sat.vec_var("o", 32)));
+
+ printf("backward solution: input=0x%08x output=0x%08x\n",
+ (unsigned int)sat.vec_model_get_unsigned(modelExpressions, backwardModel, sat.vec_var("i", 32)),
+ (unsigned int)sat.vec_model_get_unsigned(modelExpressions, backwardModel, sat.vec_var("o", 32)));
+
+ if (forwardModel != backwardModel) {
+ printf("forward and backward results are inconsistend!\n");
+ abort();
+ }
+
+ printf("passed.\n\n");
+}
+
+void test_xorshift32()
+{
+ printf("==== %s ====\n\n", __PRETTY_FUNCTION__);
+
+ ezMiniSAT sat;
+ sat.keep_cnf();
+
+ xorshift128 rng;
+
+ std::vector<int> bits = sat.vec_var("i", 32);
+
+ bits = sat.vec_xor(bits, sat.vec_shl(bits, 13));
+ bits = sat.vec_xor(bits, sat.vec_shr(bits, 17));
+ bits = sat.vec_xor(bits, sat.vec_shl(bits, 5));
+
+ sat.vec_set(bits, sat.vec_var("o", 32));
+
+ test_xorshift32_try(sat, 0);
+ test_xorshift32_try(sat, 314159265);
+ test_xorshift32_try(sat, rng());
+ test_xorshift32_try(sat, rng());
+ test_xorshift32_try(sat, rng());
+ test_xorshift32_try(sat, rng());
+
+ sat.printDIMACS(stdout, true);
+ printf("\n");
+}
+
+// ------------------------------------------------------------------------------------------------------------
+
+#define CHECK(_expr1, _expr2) check(#_expr1, _expr1, #_expr2, _expr2)
+
+void check(const char *expr1_str, bool expr1, const char *expr2_str, bool expr2)
+{
+ if (expr1 == expr2) {
+ printf("[ %s ] == [ %s ] .. ok (%s == %s)\n", expr1_str, expr2_str, expr1 ? "true" : "false", expr2 ? "true" : "false");
+ } else {
+ printf("[ %s ] != [ %s ] .. ERROR (%s != %s)\n", expr1_str, expr2_str, expr1 ? "true" : "false", expr2 ? "true" : "false");
+ abort();
+ }
+}
+
+void test_signed(int8_t a, int8_t b, int8_t c)
+{
+ ezMiniSAT sat;
+
+ std::vector<int> av = sat.vec_const_signed(a, 8);
+ std::vector<int> bv = sat.vec_const_signed(b, 8);
+ std::vector<int> cv = sat.vec_const_signed(c, 8);
+
+ printf("Testing signed arithmetic using: a=%+d, b=%+d, c=%+d\n", int(a), int(b), int(c));
+
+ CHECK(a < b+c, sat.solve(sat.vec_lt_signed(av, sat.vec_add(bv, cv))));
+ CHECK(a <= b-c, sat.solve(sat.vec_le_signed(av, sat.vec_sub(bv, cv))));
+
+ CHECK(a > b+c, sat.solve(sat.vec_gt_signed(av, sat.vec_add(bv, cv))));
+ CHECK(a >= b-c, sat.solve(sat.vec_ge_signed(av, sat.vec_sub(bv, cv))));
+
+ printf("\n");
+}
+
+void test_unsigned(uint8_t a, uint8_t b, uint8_t c)
+{
+ ezMiniSAT sat;
+
+ if (b < c)
+ b ^= c, c ^= b, b ^= c;
+
+ std::vector<int> av = sat.vec_const_unsigned(a, 8);
+ std::vector<int> bv = sat.vec_const_unsigned(b, 8);
+ std::vector<int> cv = sat.vec_const_unsigned(c, 8);
+
+ printf("Testing unsigned arithmetic using: a=%d, b=%d, c=%d\n", int(a), int(b), int(c));
+
+ CHECK(a < b+c, sat.solve(sat.vec_lt_unsigned(av, sat.vec_add(bv, cv))));
+ CHECK(a <= b-c, sat.solve(sat.vec_le_unsigned(av, sat.vec_sub(bv, cv))));
+
+ CHECK(a > b+c, sat.solve(sat.vec_gt_unsigned(av, sat.vec_add(bv, cv))));
+ CHECK(a >= b-c, sat.solve(sat.vec_ge_unsigned(av, sat.vec_sub(bv, cv))));
+
+ printf("\n");
+}
+
+void test_count(uint32_t x)
+{
+ ezMiniSAT sat;
+
+ int count = 0;
+ for (int i = 0; i < 32; i++)
+ if (((x >> i) & 1) != 0)
+ count++;
+
+ printf("Testing bit counting using x=0x%08x (%d set bits) .. ", x, count);
+
+ std::vector<int> v = sat.vec_const_unsigned(x, 32);
+
+ std::vector<int> cv6 = sat.vec_const_unsigned(count, 6);
+ std::vector<int> cv4 = sat.vec_const_unsigned(count <= 15 ? count : 15, 4);
+
+ if (cv6 != sat.vec_count(v, 6, false)) {
+ fprintf(stderr, "FAILED 6bit-no-clipping test!\n");
+ abort();
+ }
+
+ if (cv4 != sat.vec_count(v, 4, true)) {
+ fprintf(stderr, "FAILED 4bit-clipping test!\n");
+ abort();
+ }
+
+ printf("ok.\n");
+}
+
+void test_arith()
+{
+ printf("==== %s ====\n\n", __PRETTY_FUNCTION__);
+
+ xorshift128 rng;
+
+ for (int i = 0; i < 100; i++)
+ test_signed(rng() % 19 - 10, rng() % 19 - 10, rng() % 19 - 10);
+
+ for (int i = 0; i < 100; i++)
+ test_unsigned(rng() % 10, rng() % 10, rng() % 10);
+
+ test_count(0x00000000);
+ test_count(0xffffffff);
+ for (int i = 0; i < 30; i++)
+ test_count(rng());
+
+ printf("\n");
+}
+
+// ------------------------------------------------------------------------------------------------------------
+
+void test_onehot()
+{
+ printf("==== %s ====\n\n", __PRETTY_FUNCTION__);
+ ezMiniSAT ez;
+
+ int a = ez.frozen_literal("a");
+ int b = ez.frozen_literal("b");
+ int c = ez.frozen_literal("c");
+ int d = ez.frozen_literal("d");
+
+ std::vector<int> abcd;
+ abcd.push_back(a);
+ abcd.push_back(b);
+ abcd.push_back(c);
+ abcd.push_back(d);
+
+ ez.assume(ez.onehot(abcd));
+
+ int solution_counter = 0;
+ while (1)
+ {
+ std::vector<bool> modelValues;
+ bool ok = ez.solve(abcd, modelValues);
+
+ if (!ok)
+ break;
+
+ printf("Solution: %d %d %d %d\n", int(modelValues[0]), int(modelValues[1]), int(modelValues[2]), int(modelValues[3]));
+
+ int count_hot = 0;
+ std::vector<int> sol;
+ for (int i = 0; i < 4; i++) {
+ if (modelValues[i])
+ count_hot++;
+ sol.push_back(modelValues[i] ? abcd[i] : ez.NOT(abcd[i]));
+ }
+ ez.assume(ez.NOT(ez.expression(ezSAT::OpAnd, sol)));
+
+ if (count_hot != 1) {
+ fprintf(stderr, "Wrong number of hot bits!\n");
+ abort();
+ }
+
+ solution_counter++;
+ }
+
+ if (solution_counter != 4) {
+ fprintf(stderr, "Wrong number of one-hot solutions!\n");
+ abort();
+ }
+
+ printf("\n");
+}
+
+void test_manyhot()
+{
+ printf("==== %s ====\n\n", __PRETTY_FUNCTION__);
+ ezMiniSAT ez;
+
+ int a = ez.frozen_literal("a");
+ int b = ez.frozen_literal("b");
+ int c = ez.frozen_literal("c");
+ int d = ez.frozen_literal("d");
+
+ std::vector<int> abcd;
+ abcd.push_back(a);
+ abcd.push_back(b);
+ abcd.push_back(c);
+ abcd.push_back(d);
+
+ ez.assume(ez.manyhot(abcd, 1, 2));
+
+ int solution_counter = 0;
+ while (1)
+ {
+ std::vector<bool> modelValues;
+ bool ok = ez.solve(abcd, modelValues);
+
+ if (!ok)
+ break;
+
+ printf("Solution: %d %d %d %d\n", int(modelValues[0]), int(modelValues[1]), int(modelValues[2]), int(modelValues[3]));
+
+ int count_hot = 0;
+ std::vector<int> sol;
+ for (int i = 0; i < 4; i++) {
+ if (modelValues[i])
+ count_hot++;
+ sol.push_back(modelValues[i] ? abcd[i] : ez.NOT(abcd[i]));
+ }
+ ez.assume(ez.NOT(ez.expression(ezSAT::OpAnd, sol)));
+
+ if (count_hot != 1 && count_hot != 2) {
+ fprintf(stderr, "Wrong number of hot bits!\n");
+ abort();
+ }
+
+ solution_counter++;
+ }
+
+ if (solution_counter != 4 + 4*3/2) {
+ fprintf(stderr, "Wrong number of one-hot solutions!\n");
+ abort();
+ }
+
+ printf("\n");
+}
+
+void test_ordered()
+{
+ printf("==== %s ====\n\n", __PRETTY_FUNCTION__);
+ ezMiniSAT ez;
+
+ int a = ez.frozen_literal("a");
+ int b = ez.frozen_literal("b");
+ int c = ez.frozen_literal("c");
+
+ int x = ez.frozen_literal("x");
+ int y = ez.frozen_literal("y");
+ int z = ez.frozen_literal("z");
+
+ std::vector<int> abc;
+ abc.push_back(a);
+ abc.push_back(b);
+ abc.push_back(c);
+
+ std::vector<int> xyz;
+ xyz.push_back(x);
+ xyz.push_back(y);
+ xyz.push_back(z);
+
+ ez.assume(ez.ordered(abc, xyz));
+
+ int solution_counter = 0;
+
+ while (1)
+ {
+ std::vector<int> modelVariables;
+ std::vector<bool> modelValues;
+
+ modelVariables.push_back(a);
+ modelVariables.push_back(b);
+ modelVariables.push_back(c);
+
+ modelVariables.push_back(x);
+ modelVariables.push_back(y);
+ modelVariables.push_back(z);
+
+ bool ok = ez.solve(modelVariables, modelValues);
+
+ if (!ok)
+ break;
+
+ printf("Solution: %d %d %d | %d %d %d\n",
+ int(modelValues[0]), int(modelValues[1]), int(modelValues[2]),
+ int(modelValues[3]), int(modelValues[4]), int(modelValues[5]));
+
+ std::vector<int> sol;
+ for (size_t i = 0; i < modelVariables.size(); i++)
+ sol.push_back(modelValues[i] ? modelVariables[i] : ez.NOT(modelVariables[i]));
+ ez.assume(ez.NOT(ez.expression(ezSAT::OpAnd, sol)));
+
+ solution_counter++;
+ }
+
+ if (solution_counter != 8+7+6+5+4+3+2+1) {
+ fprintf(stderr, "Wrong number of solutions!\n");
+ abort();
+ }
+
+ printf("\n");
+}
+
+// ------------------------------------------------------------------------------------------------------------
+
+
+int main()
+{
+ test_simple();
+ test_xorshift32();
+ test_arith();
+ test_onehot();
+ test_manyhot();
+ test_ordered();
+ printf("Passed all tests.\n\n");
+ return 0;
+}
+
diff --git a/libs/minisat/00_PATCH_mkLit_default_arg.patch b/libs/minisat/00_PATCH_mkLit_default_arg.patch
new file mode 100644
index 00000000..e21683f9
--- /dev/null
+++ b/libs/minisat/00_PATCH_mkLit_default_arg.patch
@@ -0,0 +1,20 @@
+--- SolverTypes.h
++++ SolverTypes.h
+@@ -52,7 +52,7 @@ struct Lit {
+ int x;
+
+ // Use this as a constructor:
+- friend Lit mkLit(Var var, bool sign = false);
++ friend Lit mkLit(Var var, bool sign);
+
+ bool operator == (Lit p) const { return x == p.x; }
+ bool operator != (Lit p) const { return x != p.x; }
+@@ -60,7 +60,7 @@ struct Lit {
+ };
+
+
+-inline Lit mkLit (Var var, bool sign) { Lit p; p.x = var + var + (int)sign; return p; }
++inline Lit mkLit (Var var, bool sign = false) { Lit p; p.x = var + var + (int)sign; return p; }
+ inline Lit operator ~(Lit p) { Lit q; q.x = p.x ^ 1; return q; }
+ inline Lit operator ^(Lit p, bool b) { Lit q; q.x = p.x ^ (unsigned int)b; return q; }
+ inline bool sign (Lit p) { return p.x & 1; }
diff --git a/libs/minisat/00_PATCH_remove_zlib.patch b/libs/minisat/00_PATCH_remove_zlib.patch
new file mode 100644
index 00000000..61a36f7e
--- /dev/null
+++ b/libs/minisat/00_PATCH_remove_zlib.patch
@@ -0,0 +1,38 @@
+--- ParseUtils.h
++++ ParseUtils.h
+@@ -24,8 +24,6 @@ OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWA
+ #include <stdlib.h>
+ #include <stdio.h>
+
+-#include <zlib.h>
+-
+ #include "XAlloc.h"
+
+ namespace Minisat {
+@@ -36,24 +34,16 @@ namespace Minisat {
+
+
+ class StreamBuffer {
+- gzFile in;
+ unsigned char* buf;
+ int pos;
+ int size;
+
+ enum { buffer_size = 64*1024 };
+
+- void assureLookahead() {
+- if (pos >= size) {
+- pos = 0;
+- size = gzread(in, buf, buffer_size); } }
++ virtual void assureLookahead() = 0;
+
+ public:
+- explicit StreamBuffer(gzFile i) : in(i), pos(0), size(0){
+- buf = (unsigned char*)xrealloc(NULL, buffer_size);
+- assureLookahead();
+- }
+- ~StreamBuffer() { free(buf); }
++ virtual ~StreamBuffer() { }
+
+ int operator * () const { return (pos >= size) ? EOF : buf[pos]; }
+ void operator ++ () { pos++; assureLookahead(); }
diff --git a/libs/minisat/00_UPDATE.sh b/libs/minisat/00_UPDATE.sh
new file mode 100644
index 00000000..96a34ec9
--- /dev/null
+++ b/libs/minisat/00_UPDATE.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+rm -f LICENSE *.cc *.h
+git clone --depth 1 https://github.com/niklasso/minisat minisat_upstream
+rm minisat_upstream/minisat/*/Main.cc
+mv minisat_upstream/LICENSE minisat_upstream/minisat/*/*.{h,cc} .
+rm -rf minisat_upstream
+
+sed -i -e 's,^#include *"minisat/[^/]\+/\?,#include ",' *.cc *.h
+sed -i -e 's/Minisat::memUsedPeak()/Minisat::memUsedPeak(bool)/' System.cc
+sed -i -e 's/PRI[iu]64/ & /' Options.h Solver.cc
+sed -i -e '1 i #define __STDC_LIMIT_MACROS' *.cc
+sed -i -e '1 i #define __STDC_FORMAT_MACROS' *.cc
+
+patch -p0 < 00_PATCH_mkLit_default_arg.patch
+patch -p0 < 00_PATCH_remove_zlib.patch
+
diff --git a/libs/minisat/Alg.h b/libs/minisat/Alg.h
new file mode 100644
index 00000000..ddb972e7
--- /dev/null
+++ b/libs/minisat/Alg.h
@@ -0,0 +1,84 @@
+/*******************************************************************************************[Alg.h]
+Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson
+Copyright (c) 2007-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+#ifndef Minisat_Alg_h
+#define Minisat_Alg_h
+
+#include "Vec.h"
+
+namespace Minisat {
+
+//=================================================================================================
+// Useful functions on vector-like types:
+
+//=================================================================================================
+// Removing and searching for elements:
+//
+
+template<class V, class T>
+static inline void remove(V& ts, const T& t)
+{
+ int j = 0;
+ for (; j < (int)ts.size() && ts[j] != t; j++);
+ assert(j < (int)ts.size());
+ for (; j < (int)ts.size()-1; j++) ts[j] = ts[j+1];
+ ts.pop();
+}
+
+
+template<class V, class T>
+static inline bool find(V& ts, const T& t)
+{
+ int j = 0;
+ for (; j < (int)ts.size() && ts[j] != t; j++);
+ return j < (int)ts.size();
+}
+
+
+//=================================================================================================
+// Copying vectors with support for nested vector types:
+//
+
+// Base case:
+template<class T>
+static inline void copy(const T& from, T& to)
+{
+ to = from;
+}
+
+// Recursive case:
+template<class T>
+static inline void copy(const vec<T>& from, vec<T>& to, bool append = false)
+{
+ if (!append)
+ to.clear();
+ for (int i = 0; i < from.size(); i++){
+ to.push();
+ copy(from[i], to.last());
+ }
+}
+
+template<class T>
+static inline void append(const vec<T>& from, vec<T>& to){ copy(from, to, true); }
+
+//=================================================================================================
+}
+
+#endif
diff --git a/libs/minisat/Alloc.h b/libs/minisat/Alloc.h
new file mode 100644
index 00000000..6591dcd5
--- /dev/null
+++ b/libs/minisat/Alloc.h
@@ -0,0 +1,131 @@
+/*****************************************************************************************[Alloc.h]
+Copyright (c) 2008-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+
+#ifndef Minisat_Alloc_h
+#define Minisat_Alloc_h
+
+#include "XAlloc.h"
+#include "Vec.h"
+
+namespace Minisat {
+
+//=================================================================================================
+// Simple Region-based memory allocator:
+
+template<class T>
+class RegionAllocator
+{
+ T* memory;
+ uint32_t sz;
+ uint32_t cap;
+ uint32_t wasted_;
+
+ void capacity(uint32_t min_cap);
+
+ public:
+ // TODO: make this a class for better type-checking?
+ typedef uint32_t Ref;
+ enum { Ref_Undef = UINT32_MAX };
+ enum { Unit_Size = sizeof(T) };
+
+ explicit RegionAllocator(uint32_t start_cap = 1024*1024) : memory(NULL), sz(0), cap(0), wasted_(0){ capacity(start_cap); }
+ ~RegionAllocator()
+ {
+ if (memory != NULL)
+ ::free(memory);
+ }
+
+
+ uint32_t size () const { return sz; }
+ uint32_t wasted () const { return wasted_; }
+
+ Ref alloc (int size);
+ void free (int size) { wasted_ += size; }
+
+ // Deref, Load Effective Address (LEA), Inverse of LEA (AEL):
+ T& operator[](Ref r) { assert(r < sz); return memory[r]; }
+ const T& operator[](Ref r) const { assert(r < sz); return memory[r]; }
+
+ T* lea (Ref r) { assert(r < sz); return &memory[r]; }
+ const T* lea (Ref r) const { assert(r < sz); return &memory[r]; }
+ Ref ael (const T* t) { assert((void*)t >= (void*)&memory[0] && (void*)t < (void*)&memory[sz-1]);
+ return (Ref)(t - &memory[0]); }
+
+ void moveTo(RegionAllocator& to) {
+ if (to.memory != NULL) ::free(to.memory);
+ to.memory = memory;
+ to.sz = sz;
+ to.cap = cap;
+ to.wasted_ = wasted_;
+
+ memory = NULL;
+ sz = cap = wasted_ = 0;
+ }
+
+
+};
+
+template<class T>
+void RegionAllocator<T>::capacity(uint32_t min_cap)
+{
+ if (cap >= min_cap) return;
+
+ uint32_t prev_cap = cap;
+ while (cap < min_cap){
+ // NOTE: Multiply by a factor (13/8) without causing overflow, then add 2 and make the
+ // result even by clearing the least significant bit. The resulting sequence of capacities
+ // is carefully chosen to hit a maximum capacity that is close to the '2^32-1' limit when
+ // using 'uint32_t' as indices so that as much as possible of this space can be used.
+ uint32_t delta = ((cap >> 1) + (cap >> 3) + 2) & ~1;
+ cap += delta;
+
+ if (cap <= prev_cap)
+ throw OutOfMemoryException();
+ }
+ // printf(" .. (%p) cap = %u\n", this, cap);
+
+ assert(cap > 0);
+ memory = (T*)xrealloc(memory, sizeof(T)*cap);
+}
+
+
+template<class T>
+typename RegionAllocator<T>::Ref
+RegionAllocator<T>::alloc(int size)
+{
+ // printf("ALLOC called (this = %p, size = %d)\n", this, size); fflush(stdout);
+ assert(size > 0);
+ capacity(sz + size);
+
+ uint32_t prev_sz = sz;
+ sz += size;
+
+ // Handle overflow:
+ if (sz < prev_sz)
+ throw OutOfMemoryException();
+
+ return prev_sz;
+}
+
+
+//=================================================================================================
+}
+
+#endif
diff --git a/libs/minisat/Dimacs.h b/libs/minisat/Dimacs.h
new file mode 100644
index 00000000..ccfa1c01
--- /dev/null
+++ b/libs/minisat/Dimacs.h
@@ -0,0 +1,87 @@
+/****************************************************************************************[Dimacs.h]
+Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson
+Copyright (c) 2007-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+#ifndef Minisat_Dimacs_h
+#define Minisat_Dimacs_h
+
+#include <stdio.h>
+
+#include "ParseUtils.h"
+#include "SolverTypes.h"
+
+namespace Minisat {
+
+//=================================================================================================
+// DIMACS Parser:
+
+template<class B, class Solver>
+static void readClause(B& in, Solver& S, vec<Lit>& lits) {
+ int parsed_lit, var;
+ lits.clear();
+ for (;;){
+ parsed_lit = parseInt(in);
+ if (parsed_lit == 0) break;
+ var = abs(parsed_lit)-1;
+ while (var >= S.nVars()) S.newVar();
+ lits.push( (parsed_lit > 0) ? mkLit(var) : ~mkLit(var) );
+ }
+}
+
+template<class B, class Solver>
+static void parse_DIMACS_main(B& in, Solver& S, bool strictp = false) {
+ vec<Lit> lits;
+ int vars = 0;
+ int clauses = 0;
+ int cnt = 0;
+ for (;;){
+ skipWhitespace(in);
+ if (*in == EOF) break;
+ else if (*in == 'p'){
+ if (eagerMatch(in, "p cnf")){
+ vars = parseInt(in);
+ clauses = parseInt(in);
+ // SATRACE'06 hack
+ // if (clauses > 4000000)
+ // S.eliminate(true);
+ }else{
+ printf("PARSE ERROR! Unexpected char: %c\n", *in), exit(3);
+ }
+ } else if (*in == 'c' || *in == 'p')
+ skipLine(in);
+ else{
+ cnt++;
+ readClause(in, S, lits);
+ S.addClause_(lits); }
+ }
+ if (strictp && cnt != clauses)
+ printf("PARSE ERROR! DIMACS header mismatch: wrong number of clauses\n");
+}
+
+// Inserts problem into solver.
+//
+template<class Solver>
+static void parse_DIMACS(gzFile input_stream, Solver& S, bool strictp = false) {
+ StreamBuffer in(input_stream);
+ parse_DIMACS_main(in, S, strictp); }
+
+//=================================================================================================
+}
+
+#endif
diff --git a/libs/minisat/Heap.h b/libs/minisat/Heap.h
new file mode 100644
index 00000000..057a3cdf
--- /dev/null
+++ b/libs/minisat/Heap.h
@@ -0,0 +1,168 @@
+/******************************************************************************************[Heap.h]
+Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson
+Copyright (c) 2007-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+#ifndef Minisat_Heap_h
+#define Minisat_Heap_h
+
+#include "Vec.h"
+#include "IntMap.h"
+
+namespace Minisat {
+
+//=================================================================================================
+// A heap implementation with support for decrease/increase key.
+
+
+template<class K, class Comp, class MkIndex = MkIndexDefault<K> >
+class Heap {
+ vec<K> heap; // Heap of Keys
+ IntMap<K,int,MkIndex> indices; // Each Key's position (index) in the Heap
+ Comp lt; // The heap is a minimum-heap with respect to this comparator
+
+ // Index "traversal" functions
+ static inline int left (int i) { return i*2+1; }
+ static inline int right (int i) { return (i+1)*2; }
+ static inline int parent(int i) { return (i-1) >> 1; }
+
+
+ void percolateUp(int i)
+ {
+ K x = heap[i];
+ int p = parent(i);
+
+ while (i != 0 && lt(x, heap[p])){
+ heap[i] = heap[p];
+ indices[heap[p]] = i;
+ i = p;
+ p = parent(p);
+ }
+ heap [i] = x;
+ indices[x] = i;
+ }
+
+
+ void percolateDown(int i)
+ {
+ K x = heap[i];
+ while (left(i) < heap.size()){
+ int child = right(i) < heap.size() && lt(heap[right(i)], heap[left(i)]) ? right(i) : left(i);
+ if (!lt(heap[child], x)) break;
+ heap[i] = heap[child];
+ indices[heap[i]] = i;
+ i = child;
+ }
+ heap [i] = x;
+ indices[x] = i;
+ }
+
+
+ public:
+ Heap(const Comp& c, MkIndex _index = MkIndex()) : indices(_index), lt(c) {}
+
+ int size () const { return heap.size(); }
+ bool empty () const { return heap.size() == 0; }
+ bool inHeap (K k) const { return indices.has(k) && indices[k] >= 0; }
+ int operator[](int index) const { assert(index < heap.size()); return heap[index]; }
+
+ void decrease (K k) { assert(inHeap(k)); percolateUp (indices[k]); }
+ void increase (K k) { assert(inHeap(k)); percolateDown(indices[k]); }
+
+
+ // Safe variant of insert/decrease/increase:
+ void update(K k)
+ {
+ if (!inHeap(k))
+ insert(k);
+ else {
+ percolateUp(indices[k]);
+ percolateDown(indices[k]); }
+ }
+
+
+ void insert(K k)
+ {
+ indices.reserve(k, -1);
+ assert(!inHeap(k));
+
+ indices[k] = heap.size();
+ heap.push(k);
+ percolateUp(indices[k]);
+ }
+
+
+ void remove(K k)
+ {
+ assert(inHeap(k));
+
+ int k_pos = indices[k];
+ indices[k] = -1;
+
+ if (k_pos < heap.size()-1){
+ heap[k_pos] = heap.last();
+ indices[heap[k_pos]] = k_pos;
+ heap.pop();
+ percolateDown(k_pos);
+ }else
+ heap.pop();
+ }
+
+
+ K removeMin()
+ {
+ K x = heap[0];
+ heap[0] = heap.last();
+ indices[heap[0]] = 0;
+ indices[x] = -1;
+ heap.pop();
+ if (heap.size() > 1) percolateDown(0);
+ return x;
+ }
+
+
+ // Rebuild the heap from scratch, using the elements in 'ns':
+ void build(const vec<K>& ns) {
+ for (int i = 0; i < heap.size(); i++)
+ indices[heap[i]] = -1;
+ heap.clear();
+
+ for (int i = 0; i < ns.size(); i++){
+ // TODO: this should probably call reserve instead of relying on it being reserved already.
+ assert(indices.has(ns[i]));
+ indices[ns[i]] = i;
+ heap.push(ns[i]); }
+
+ for (int i = heap.size() / 2 - 1; i >= 0; i--)
+ percolateDown(i);
+ }
+
+ void clear(bool dispose = false)
+ {
+ // TODO: shouldn't the 'indices' map also be dispose-cleared?
+ for (int i = 0; i < heap.size(); i++)
+ indices[heap[i]] = -1;
+ heap.clear(dispose);
+ }
+};
+
+
+//=================================================================================================
+}
+
+#endif
diff --git a/libs/minisat/IntMap.h b/libs/minisat/IntMap.h
new file mode 100644
index 00000000..9a66315d
--- /dev/null
+++ b/libs/minisat/IntMap.h
@@ -0,0 +1,106 @@
+/****************************************************************************************[IntMap.h]
+Copyright (c) 2011, Niklas Sorensson
+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.
+**************************************************************************************************/
+
+#ifndef Minisat_IntMap_h
+#define Minisat_IntMap_h
+
+#include "Vec.h"
+
+namespace Minisat {
+
+ template<class T> struct MkIndexDefault {
+ typename vec<T>::Size operator()(T t) const { return (typename vec<T>::Size)t; }
+ };
+
+ template<class K, class V, class MkIndex = MkIndexDefault<K> >
+ class IntMap {
+ vec<V> map;
+ MkIndex index;
+ public:
+ explicit IntMap(MkIndex _index = MkIndex()) : index(_index){}
+
+ bool has (K k) const { return index(k) < map.size(); }
+
+ const V& operator[](K k) const { assert(has(k)); return map[index(k)]; }
+ V& operator[](K k) { assert(has(k)); return map[index(k)]; }
+
+ const V* begin () const { return &map[0]; }
+ const V* end () const { return &map[map.size()]; }
+ V* begin () { return &map[0]; }
+ V* end () { return &map[map.size()]; }
+
+ void reserve(K key, V pad) { map.growTo(index(key)+1, pad); }
+ void reserve(K key) { map.growTo(index(key)+1); }
+ void insert (K key, V val, V pad){ reserve(key, pad); operator[](key) = val; }
+ void insert (K key, V val) { reserve(key); operator[](key) = val; }
+
+ void clear (bool dispose = false) { map.clear(dispose); }
+ void moveTo (IntMap& to) { map.moveTo(to.map); to.index = index; }
+ void copyTo (IntMap& to) const { map.copyTo(to.map); to.index = index; }
+ };
+
+
+ template<class K, class MkIndex = MkIndexDefault<K> >
+ class IntSet
+ {
+ IntMap<K, char, MkIndex> in_set;
+ vec<K> xs;
+
+ public:
+ // Size operations:
+ int size (void) const { return xs.size(); }
+ void clear (bool free = false){
+ if (free)
+ in_set.clear(true);
+ else
+ for (int i = 0; i < xs.size(); i++)
+ in_set[xs[i]] = 0;
+ xs.clear(free);
+ }
+
+ // Allow inspecting the internal vector:
+ const vec<K>&
+ toVec () const { return xs; }
+
+ // Vector interface:
+ K operator [] (int index) const { return xs[index]; }
+
+
+ void insert (K k) { in_set.reserve(k, 0); if (!in_set[k]) { in_set[k] = 1; xs.push(k); } }
+ bool has (K k) { in_set.reserve(k, 0); return in_set[k]; }
+ };
+
+ #if 0
+ template<class K, class V, V nil, class MkIndex = MkIndexDefault<K> >
+ class IntMapNil {
+ vec<V> map;
+ V nil;
+
+ public:
+ IntMap(){}
+
+ void reserve(K);
+ V& find (K);
+ const V& operator[](K k) const;
+
+ };
+ #endif
+
+//=================================================================================================
+} // namespace Minisat
+#endif
diff --git a/libs/minisat/IntTypes.h b/libs/minisat/IntTypes.h
new file mode 100644
index 00000000..c4881628
--- /dev/null
+++ b/libs/minisat/IntTypes.h
@@ -0,0 +1,42 @@
+/**************************************************************************************[IntTypes.h]
+Copyright (c) 2009-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+#ifndef Minisat_IntTypes_h
+#define Minisat_IntTypes_h
+
+#ifdef __sun
+ // Not sure if there are newer versions that support C99 headers. The
+ // needed features are implemented in the headers below though:
+
+# include <sys/int_types.h>
+# include <sys/int_fmtio.h>
+# include <sys/int_limits.h>
+
+#else
+
+# include <stdint.h>
+# include <inttypes.h>
+
+#endif
+
+#include <limits.h>
+
+//=================================================================================================
+
+#endif
diff --git a/libs/minisat/LICENSE b/libs/minisat/LICENSE
new file mode 100644
index 00000000..22816ff3
--- /dev/null
+++ b/libs/minisat/LICENSE
@@ -0,0 +1,21 @@
+MiniSat -- Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson
+ Copyright (c) 2007-2010 Niklas Sorensson
+
+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.
diff --git a/libs/minisat/Map.h b/libs/minisat/Map.h
new file mode 100644
index 00000000..a6f83200
--- /dev/null
+++ b/libs/minisat/Map.h
@@ -0,0 +1,193 @@
+/*******************************************************************************************[Map.h]
+Copyright (c) 2006-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+#ifndef Minisat_Map_h
+#define Minisat_Map_h
+
+#include "IntTypes.h"
+#include "Vec.h"
+
+namespace Minisat {
+
+//=================================================================================================
+// Default hash/equals functions
+//
+
+template<class K> struct Hash { uint32_t operator()(const K& k) const { return hash(k); } };
+template<class K> struct Equal { bool operator()(const K& k1, const K& k2) const { return k1 == k2; } };
+
+template<class K> struct DeepHash { uint32_t operator()(const K* k) const { return hash(*k); } };
+template<class K> struct DeepEqual { bool operator()(const K* k1, const K* k2) const { return *k1 == *k2; } };
+
+static inline uint32_t hash(uint32_t x){ return x; }
+static inline uint32_t hash(uint64_t x){ return (uint32_t)x; }
+static inline uint32_t hash(int32_t x) { return (uint32_t)x; }
+static inline uint32_t hash(int64_t x) { return (uint32_t)x; }
+
+
+//=================================================================================================
+// Some primes
+//
+
+static const int nprimes = 25;
+static const int primes [nprimes] = { 31, 73, 151, 313, 643, 1291, 2593, 5233, 10501, 21013, 42073, 84181, 168451, 337219, 674701, 1349473, 2699299, 5398891, 10798093, 21596719, 43193641, 86387383, 172775299, 345550609, 691101253 };
+
+//=================================================================================================
+// Hash table implementation of Maps
+//
+
+template<class K, class D, class H = Hash<K>, class E = Equal<K> >
+class Map {
+ public:
+ struct Pair { K key; D data; };
+
+ private:
+ H hash;
+ E equals;
+
+ vec<Pair>* table;
+ int cap;
+ int size;
+
+ // Don't allow copying (error prone):
+ Map<K,D,H,E>& operator = (Map<K,D,H,E>& other);
+ Map (Map<K,D,H,E>& other);
+
+ bool checkCap(int new_size) const { return new_size > cap; }
+
+ int32_t index (const K& k) const { return hash(k) % cap; }
+ void _insert (const K& k, const D& d) {
+ vec<Pair>& ps = table[index(k)];
+ ps.push(); ps.last().key = k; ps.last().data = d; }
+
+ void rehash () {
+ const vec<Pair>* old = table;
+
+ int old_cap = cap;
+ int newsize = primes[0];
+ for (int i = 1; newsize <= cap && i < nprimes; i++)
+ newsize = primes[i];
+
+ table = new vec<Pair>[newsize];
+ cap = newsize;
+
+ for (int i = 0; i < old_cap; i++){
+ for (int j = 0; j < old[i].size(); j++){
+ _insert(old[i][j].key, old[i][j].data); }}
+
+ delete [] old;
+
+ // printf(" --- rehashing, old-cap=%d, new-cap=%d\n", cap, newsize);
+ }
+
+
+ public:
+
+ Map () : table(NULL), cap(0), size(0) {}
+ Map (const H& h, const E& e) : hash(h), equals(e), table(NULL), cap(0), size(0){}
+ ~Map () { delete [] table; }
+
+ // PRECONDITION: the key must already exist in the map.
+ const D& operator [] (const K& k) const
+ {
+ assert(size != 0);
+ const D* res = NULL;
+ const vec<Pair>& ps = table[index(k)];
+ for (int i = 0; i < ps.size(); i++)
+ if (equals(ps[i].key, k))
+ res = &ps[i].data;
+ assert(res != NULL);
+ return *res;
+ }
+
+ // PRECONDITION: the key must already exist in the map.
+ D& operator [] (const K& k)
+ {
+ assert(size != 0);
+ D* res = NULL;
+ vec<Pair>& ps = table[index(k)];
+ for (int i = 0; i < ps.size(); i++)
+ if (equals(ps[i].key, k))
+ res = &ps[i].data;
+ assert(res != NULL);
+ return *res;
+ }
+
+ // PRECONDITION: the key must *NOT* exist in the map.
+ void insert (const K& k, const D& d) { if (checkCap(size+1)) rehash(); _insert(k, d); size++; }
+ bool peek (const K& k, D& d) const {
+ if (size == 0) return false;
+ const vec<Pair>& ps = table[index(k)];
+ for (int i = 0; i < ps.size(); i++)
+ if (equals(ps[i].key, k)){
+ d = ps[i].data;
+ return true; }
+ return false;
+ }
+
+ bool has (const K& k) const {
+ if (size == 0) return false;
+ const vec<Pair>& ps = table[index(k)];
+ for (int i = 0; i < ps.size(); i++)
+ if (equals(ps[i].key, k))
+ return true;
+ return false;
+ }
+
+ // PRECONDITION: the key must exist in the map.
+ void remove(const K& k) {
+ assert(table != NULL);
+ vec<Pair>& ps = table[index(k)];
+ int j = 0;
+ for (; j < ps.size() && !equals(ps[j].key, k); j++);
+ assert(j < ps.size());
+ ps[j] = ps.last();
+ ps.pop();
+ size--;
+ }
+
+ void clear () {
+ cap = size = 0;
+ delete [] table;
+ table = NULL;
+ }
+
+ int elems() const { return size; }
+ int bucket_count() const { return cap; }
+
+ // NOTE: the hash and equality objects are not moved by this method:
+ void moveTo(Map& other){
+ delete [] other.table;
+
+ other.table = table;
+ other.cap = cap;
+ other.size = size;
+
+ table = NULL;
+ size = cap = 0;
+ }
+
+ // NOTE: given a bit more time, I could make a more C++-style iterator out of this:
+ const vec<Pair>& bucket(int i) const { return table[i]; }
+};
+
+//=================================================================================================
+}
+
+#endif
diff --git a/libs/minisat/Options.cc b/libs/minisat/Options.cc
new file mode 100644
index 00000000..1aff3fab
--- /dev/null
+++ b/libs/minisat/Options.cc
@@ -0,0 +1,94 @@
+#define __STDC_FORMAT_MACROS
+#define __STDC_LIMIT_MACROS
+/**************************************************************************************[Options.cc]
+Copyright (c) 2008-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+#include "Sort.h"
+#include "Options.h"
+#include "ParseUtils.h"
+
+using namespace Minisat;
+
+void Minisat::parseOptions(int& argc, char** argv, bool strict)
+{
+ int i, j;
+ for (i = j = 1; i < argc; i++){
+ const char* str = argv[i];
+ if (match(str, "--") && match(str, Option::getHelpPrefixString()) && match(str, "help")){
+ if (*str == '\0')
+ printUsageAndExit(argc, argv);
+ else if (match(str, "-verb"))
+ printUsageAndExit(argc, argv, true);
+ } else {
+ bool parsed_ok = false;
+
+ for (int k = 0; !parsed_ok && k < Option::getOptionList().size(); k++){
+ parsed_ok = Option::getOptionList()[k]->parse(argv[i]);
+
+ // fprintf(stderr, "checking %d: %s against flag <%s> (%s)\n", i, argv[i], Option::getOptionList()[k]->name, parsed_ok ? "ok" : "skip");
+ }
+
+ if (!parsed_ok){
+ if (strict && match(argv[i], "-"))
+ fprintf(stderr, "ERROR! Unknown flag \"%s\". Use '--%shelp' for help.\n", argv[i], Option::getHelpPrefixString()), exit(1);
+ else
+ argv[j++] = argv[i];
+ }
+ }
+ }
+
+ argc -= (i - j);
+}
+
+
+void Minisat::setUsageHelp (const char* str){ Option::getUsageString() = str; }
+void Minisat::setHelpPrefixStr (const char* str){ Option::getHelpPrefixString() = str; }
+void Minisat::printUsageAndExit (int /*argc*/, char** argv, bool verbose)
+{
+ const char* usage = Option::getUsageString();
+ if (usage != NULL)
+ fprintf(stderr, usage, argv[0]);
+
+ sort(Option::getOptionList(), Option::OptionLt());
+
+ const char* prev_cat = NULL;
+ const char* prev_type = NULL;
+
+ for (int i = 0; i < Option::getOptionList().size(); i++){
+ const char* cat = Option::getOptionList()[i]->category;
+ const char* type = Option::getOptionList()[i]->type_name;
+
+ if (cat != prev_cat)
+ fprintf(stderr, "\n%s OPTIONS:\n\n", cat);
+ else if (type != prev_type)
+ fprintf(stderr, "\n");
+
+ Option::getOptionList()[i]->help(verbose);
+
+ prev_cat = Option::getOptionList()[i]->category;
+ prev_type = Option::getOptionList()[i]->type_name;
+ }
+
+ fprintf(stderr, "\nHELP OPTIONS:\n\n");
+ fprintf(stderr, " --%shelp Print help message.\n", Option::getHelpPrefixString());
+ fprintf(stderr, " --%shelp-verb Print verbose help message.\n", Option::getHelpPrefixString());
+ fprintf(stderr, "\n");
+ exit(0);
+}
+
diff --git a/libs/minisat/Options.h b/libs/minisat/Options.h
new file mode 100644
index 00000000..d602769c
--- /dev/null
+++ b/libs/minisat/Options.h
@@ -0,0 +1,386 @@
+/***************************************************************************************[Options.h]
+Copyright (c) 2008-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+#ifndef Minisat_Options_h
+#define Minisat_Options_h
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#include "IntTypes.h"
+#include "Vec.h"
+#include "ParseUtils.h"
+
+namespace Minisat {
+
+//==================================================================================================
+// Top-level option parse/help functions:
+
+
+extern void parseOptions (int& argc, char** argv, bool strict = false);
+extern void printUsageAndExit(int argc, char** argv, bool verbose = false);
+extern void setUsageHelp (const char* str);
+extern void setHelpPrefixStr (const char* str);
+
+
+//==================================================================================================
+// Options is an abstract class that gives the interface for all types options:
+
+
+class Option
+{
+ protected:
+ const char* name;
+ const char* description;
+ const char* category;
+ const char* type_name;
+
+ static vec<Option*>& getOptionList () { static vec<Option*> options; return options; }
+ static const char*& getUsageString() { static const char* usage_str; return usage_str; }
+ static const char*& getHelpPrefixString() { static const char* help_prefix_str = ""; return help_prefix_str; }
+
+ struct OptionLt {
+ bool operator()(const Option* x, const Option* y) {
+ int test1 = strcmp(x->category, y->category);
+ return test1 < 0 || (test1 == 0 && strcmp(x->type_name, y->type_name) < 0);
+ }
+ };
+
+ Option(const char* name_,
+ const char* desc_,
+ const char* cate_,
+ const char* type_) :
+ name (name_)
+ , description(desc_)
+ , category (cate_)
+ , type_name (type_)
+ {
+ getOptionList().push(this);
+ }
+
+ public:
+ virtual ~Option() {}
+
+ virtual bool parse (const char* str) = 0;
+ virtual void help (bool verbose = false) = 0;
+
+ friend void parseOptions (int& argc, char** argv, bool strict);
+ friend void printUsageAndExit (int argc, char** argv, bool verbose);
+ friend void setUsageHelp (const char* str);
+ friend void setHelpPrefixStr (const char* str);
+};
+
+
+//==================================================================================================
+// Range classes with specialization for floating types:
+
+
+struct IntRange {
+ int begin;
+ int end;
+ IntRange(int b, int e) : begin(b), end(e) {}
+};
+
+struct Int64Range {
+ int64_t begin;
+ int64_t end;
+ Int64Range(int64_t b, int64_t e) : begin(b), end(e) {}
+};
+
+struct DoubleRange {
+ double begin;
+ double end;
+ bool begin_inclusive;
+ bool end_inclusive;
+ DoubleRange(double b, bool binc, double e, bool einc) : begin(b), end(e), begin_inclusive(binc), end_inclusive(einc) {}
+};
+
+
+//==================================================================================================
+// Double options:
+
+
+class DoubleOption : public Option
+{
+ protected:
+ DoubleRange range;
+ double value;
+
+ public:
+ DoubleOption(const char* c, const char* n, const char* d, double def = double(), DoubleRange r = DoubleRange(-HUGE_VAL, false, HUGE_VAL, false))
+ : Option(n, d, c, "<double>"), range(r), value(def) {
+ // FIXME: set LC_NUMERIC to "C" to make sure that strtof/strtod parses decimal point correctly.
+ }
+
+ operator double (void) const { return value; }
+ operator double& (void) { return value; }
+ DoubleOption& operator=(double x) { value = x; return *this; }
+
+ virtual bool parse(const char* str){
+ const char* span = str;
+
+ if (!match(span, "-") || !match(span, name) || !match(span, "="))
+ return false;
+
+ char* end;
+ double tmp = strtod(span, &end);
+
+ if (end == NULL)
+ return false;
+ else if (tmp >= range.end && (!range.end_inclusive || tmp != range.end)){
+ fprintf(stderr, "ERROR! value <%s> is too large for option \"%s\".\n", span, name);
+ exit(1);
+ }else if (tmp <= range.begin && (!range.begin_inclusive || tmp != range.begin)){
+ fprintf(stderr, "ERROR! value <%s> is too small for option \"%s\".\n", span, name);
+ exit(1); }
+
+ value = tmp;
+ // fprintf(stderr, "READ VALUE: %g\n", value);
+
+ return true;
+ }
+
+ virtual void help (bool verbose = false){
+ fprintf(stderr, " -%-12s = %-8s %c%4.2g .. %4.2g%c (default: %g)\n",
+ name, type_name,
+ range.begin_inclusive ? '[' : '(',
+ range.begin,
+ range.end,
+ range.end_inclusive ? ']' : ')',
+ value);
+ if (verbose){
+ fprintf(stderr, "\n %s\n", description);
+ fprintf(stderr, "\n");
+ }
+ }
+};
+
+
+//==================================================================================================
+// Int options:
+
+
+class IntOption : public Option
+{
+ protected:
+ IntRange range;
+ int32_t value;
+
+ public:
+ IntOption(const char* c, const char* n, const char* d, int32_t def = int32_t(), IntRange r = IntRange(INT32_MIN, INT32_MAX))
+ : Option(n, d, c, "<int32>"), range(r), value(def) {}
+
+ operator int32_t (void) const { return value; }
+ operator int32_t& (void) { return value; }
+ IntOption& operator= (int32_t x) { value = x; return *this; }
+
+ virtual bool parse(const char* str){
+ const char* span = str;
+
+ if (!match(span, "-") || !match(span, name) || !match(span, "="))
+ return false;
+
+ char* end;
+ int32_t tmp = strtol(span, &end, 10);
+
+ if (end == NULL)
+ return false;
+ else if (tmp > range.end){
+ fprintf(stderr, "ERROR! value <%s> is too large for option \"%s\".\n", span, name);
+ exit(1);
+ }else if (tmp < range.begin){
+ fprintf(stderr, "ERROR! value <%s> is too small for option \"%s\".\n", span, name);
+ exit(1); }
+
+ value = tmp;
+
+ return true;
+ }
+
+ virtual void help (bool verbose = false){
+ fprintf(stderr, " -%-12s = %-8s [", name, type_name);
+ if (range.begin == INT32_MIN)
+ fprintf(stderr, "imin");
+ else
+ fprintf(stderr, "%4d", range.begin);
+
+ fprintf(stderr, " .. ");
+ if (range.end == INT32_MAX)
+ fprintf(stderr, "imax");
+ else
+ fprintf(stderr, "%4d", range.end);
+
+ fprintf(stderr, "] (default: %d)\n", value);
+ if (verbose){
+ fprintf(stderr, "\n %s\n", description);
+ fprintf(stderr, "\n");
+ }
+ }
+};
+
+
+// Leave this out for visual C++ until Microsoft implements C99 and gets support for strtoll.
+#ifndef _MSC_VER
+
+class Int64Option : public Option
+{
+ protected:
+ Int64Range range;
+ int64_t value;
+
+ public:
+ Int64Option(const char* c, const char* n, const char* d, int64_t def = int64_t(), Int64Range r = Int64Range(INT64_MIN, INT64_MAX))
+ : Option(n, d, c, "<int64>"), range(r), value(def) {}
+
+ operator int64_t (void) const { return value; }
+ operator int64_t& (void) { return value; }
+ Int64Option& operator= (int64_t x) { value = x; return *this; }
+
+ virtual bool parse(const char* str){
+ const char* span = str;
+
+ if (!match(span, "-") || !match(span, name) || !match(span, "="))
+ return false;
+
+ char* end;
+ int64_t tmp = strtoll(span, &end, 10);
+
+ if (end == NULL)
+ return false;
+ else if (tmp > range.end){
+ fprintf(stderr, "ERROR! value <%s> is too large for option \"%s\".\n", span, name);
+ exit(1);
+ }else if (tmp < range.begin){
+ fprintf(stderr, "ERROR! value <%s> is too small for option \"%s\".\n", span, name);
+ exit(1); }
+
+ value = tmp;
+
+ return true;
+ }
+
+ virtual void help (bool verbose = false){
+ fprintf(stderr, " -%-12s = %-8s [", name, type_name);
+ if (range.begin == INT64_MIN)
+ fprintf(stderr, "imin");
+ else
+ fprintf(stderr, "%4" PRIi64 , range.begin);
+
+ fprintf(stderr, " .. ");
+ if (range.end == INT64_MAX)
+ fprintf(stderr, "imax");
+ else
+ fprintf(stderr, "%4" PRIi64 , range.end);
+
+ fprintf(stderr, "] (default: %" PRIi64 ")\n", value);
+ if (verbose){
+ fprintf(stderr, "\n %s\n", description);
+ fprintf(stderr, "\n");
+ }
+ }
+};
+#endif
+
+//==================================================================================================
+// String option:
+
+
+class StringOption : public Option
+{
+ const char* value;
+ public:
+ StringOption(const char* c, const char* n, const char* d, const char* def = NULL)
+ : Option(n, d, c, "<string>"), value(def) {}
+
+ operator const char* (void) const { return value; }
+ operator const char*& (void) { return value; }
+ StringOption& operator= (const char* x) { value = x; return *this; }
+
+ virtual bool parse(const char* str){
+ const char* span = str;
+
+ if (!match(span, "-") || !match(span, name) || !match(span, "="))
+ return false;
+
+ value = span;
+ return true;
+ }
+
+ virtual void help (bool verbose = false){
+ fprintf(stderr, " -%-10s = %8s\n", name, type_name);
+ if (verbose){
+ fprintf(stderr, "\n %s\n", description);
+ fprintf(stderr, "\n");
+ }
+ }
+};
+
+
+//==================================================================================================
+// Bool option:
+
+
+class BoolOption : public Option
+{
+ bool value;
+
+ public:
+ BoolOption(const char* c, const char* n, const char* d, bool v)
+ : Option(n, d, c, "<bool>"), value(v) {}
+
+ operator bool (void) const { return value; }
+ operator bool& (void) { return value; }
+ BoolOption& operator=(bool b) { value = b; return *this; }
+
+ virtual bool parse(const char* str){
+ const char* span = str;
+
+ if (match(span, "-")){
+ bool b = !match(span, "no-");
+
+ if (strcmp(span, name) == 0){
+ value = b;
+ return true; }
+ }
+
+ return false;
+ }
+
+ virtual void help (bool verbose = false){
+
+ fprintf(stderr, " -%s, -no-%s", name, name);
+
+ for (uint32_t i = 0; i < 32 - strlen(name)*2; i++)
+ fprintf(stderr, " ");
+
+ fprintf(stderr, " ");
+ fprintf(stderr, "(default: %s)\n", value ? "on" : "off");
+ if (verbose){
+ fprintf(stderr, "\n %s\n", description);
+ fprintf(stderr, "\n");
+ }
+ }
+};
+
+//=================================================================================================
+}
+
+#endif
diff --git a/libs/minisat/ParseUtils.h b/libs/minisat/ParseUtils.h
new file mode 100644
index 00000000..04911c70
--- /dev/null
+++ b/libs/minisat/ParseUtils.h
@@ -0,0 +1,119 @@
+/************************************************************************************[ParseUtils.h]
+Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson
+Copyright (c) 2007-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+#ifndef Minisat_ParseUtils_h
+#define Minisat_ParseUtils_h
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "XAlloc.h"
+
+namespace Minisat {
+
+//-------------------------------------------------------------------------------------------------
+// A simple buffered character stream class:
+
+
+
+class StreamBuffer {
+ unsigned char* buf;
+ int pos;
+ int size;
+
+ enum { buffer_size = 64*1024 };
+
+ virtual void assureLookahead() = 0;
+
+public:
+ virtual ~StreamBuffer() { }
+
+ int operator * () const { return (pos >= size) ? EOF : buf[pos]; }
+ void operator ++ () { pos++; assureLookahead(); }
+ int position () const { return pos; }
+};
+
+
+//-------------------------------------------------------------------------------------------------
+// End-of-file detection functions for StreamBuffer and char*:
+
+
+static inline bool isEof(StreamBuffer& in) { return *in == EOF; }
+static inline bool isEof(const char* in) { return *in == '\0'; }
+
+//-------------------------------------------------------------------------------------------------
+// Generic parse functions parametrized over the input-stream type.
+
+
+template<class B>
+static void skipWhitespace(B& in) {
+ while ((*in >= 9 && *in <= 13) || *in == 32)
+ ++in; }
+
+
+template<class B>
+static void skipLine(B& in) {
+ for (;;){
+ if (isEof(in)) return;
+ if (*in == '\n') { ++in; return; }
+ ++in; } }
+
+
+template<class B>
+static int parseInt(B& in) {
+ int val = 0;
+ bool neg = false;
+ skipWhitespace(in);
+ if (*in == '-') neg = true, ++in;
+ else if (*in == '+') ++in;
+ if (*in < '0' || *in > '9') fprintf(stderr, "PARSE ERROR! Unexpected char: %c\n", *in), exit(3);
+ while (*in >= '0' && *in <= '9')
+ val = val*10 + (*in - '0'),
+ ++in;
+ return neg ? -val : val; }
+
+
+// String matching: in case of a match the input iterator will be advanced the corresponding
+// number of characters.
+template<class B>
+static bool match(B& in, const char* str) {
+ int i;
+ for (i = 0; str[i] != '\0'; i++)
+ if (in[i] != str[i])
+ return false;
+
+ in += i;
+
+ return true;
+}
+
+// String matching: consumes characters eagerly, but does not require random access iterator.
+template<class B>
+static bool eagerMatch(B& in, const char* str) {
+ for (; *str != '\0'; ++str, ++in)
+ if (*str != *in)
+ return false;
+ return true; }
+
+
+//=================================================================================================
+}
+
+#endif
diff --git a/libs/minisat/Queue.h b/libs/minisat/Queue.h
new file mode 100644
index 00000000..5ba50cd2
--- /dev/null
+++ b/libs/minisat/Queue.h
@@ -0,0 +1,69 @@
+/*****************************************************************************************[Queue.h]
+Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson
+Copyright (c) 2007-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+#ifndef Minisat_Queue_h
+#define Minisat_Queue_h
+
+#include "Vec.h"
+
+namespace Minisat {
+
+//=================================================================================================
+
+template<class T>
+class Queue {
+ vec<T> buf;
+ int first;
+ int end;
+
+public:
+ typedef T Key;
+
+ Queue() : buf(1), first(0), end(0) {}
+
+ void clear (bool dealloc = false) { buf.clear(dealloc); buf.growTo(1); first = end = 0; }
+ int size () const { return (end >= first) ? end - first : end - first + buf.size(); }
+
+ const T& operator [] (int index) const { assert(index >= 0); assert(index < size()); return buf[(first + index) % buf.size()]; }
+ T& operator [] (int index) { assert(index >= 0); assert(index < size()); return buf[(first + index) % buf.size()]; }
+
+ T peek () const { assert(first != end); return buf[first]; }
+ void pop () { assert(first != end); first++; if (first == buf.size()) first = 0; }
+ void insert(T elem) { // INVARIANT: buf[end] is always unused
+ buf[end++] = elem;
+ if (end == buf.size()) end = 0;
+ if (first == end){ // Resize:
+ vec<T> tmp((buf.size()*3 + 1) >> 1);
+ //**/printf("queue alloc: %d elems (%.1f MB)\n", tmp.size(), tmp.size() * sizeof(T) / 1000000.0);
+ int i = 0;
+ for (int j = first; j < buf.size(); j++) tmp[i++] = buf[j];
+ for (int j = 0 ; j < end ; j++) tmp[i++] = buf[j];
+ first = 0;
+ end = buf.size();
+ tmp.moveTo(buf);
+ }
+ }
+};
+
+
+//=================================================================================================
+}
+
+#endif
diff --git a/libs/minisat/Rnd.h b/libs/minisat/Rnd.h
new file mode 100644
index 00000000..ccb94c6c
--- /dev/null
+++ b/libs/minisat/Rnd.h
@@ -0,0 +1,67 @@
+/*******************************************************************************************[Rnd.h]
+Copyright (c) 2012, Niklas Sorensson
+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.
+**************************************************************************************************/
+
+#ifndef Minisat_Rnd_h
+#define Minisat_Rnd_h
+
+#include "Vec.h"
+
+namespace Minisat {
+
+// Generate a random double:
+static inline double drand(double& seed)
+{
+ seed *= 1389796;
+ int q = (int)(seed / 2147483647);
+ seed -= (double)q * 2147483647;
+ return seed / 2147483647;
+}
+
+
+// Generate a random integer:
+static inline int irand(double& seed, int size) { return (int)(drand(seed) * size); }
+
+
+// Randomly shuffle the contents of a vector:
+template<class T>
+static void randomShuffle(double& seed, vec<T>& xs)
+{
+ for (int i = 0; i < xs.size(); i++){
+ int pick = i + irand(seed, xs.size() - i);
+ T tmp = xs[i];
+ xs[i] = xs[pick];
+ xs[pick] = tmp;
+ }
+}
+
+// Randomly shuffle a vector of a vector (ugly)
+template<class T>
+static void randomShuffle(double& seed, vec<vec<T> >& xs)
+{
+ for (int i = 0; i < xs.size(); i++){
+ int pick = i + irand(seed, xs.size() - i);
+ vec<T> tmp; xs[i].moveTo(tmp);
+ xs[pick].moveTo(xs[i]);
+ tmp.moveTo(xs[pick]);
+ }
+}
+
+
+//=================================================================================================
+} // namespace Minisat
+#endif
diff --git a/libs/minisat/SimpSolver.cc b/libs/minisat/SimpSolver.cc
new file mode 100644
index 00000000..fd5774e0
--- /dev/null
+++ b/libs/minisat/SimpSolver.cc
@@ -0,0 +1,727 @@
+#define __STDC_FORMAT_MACROS
+#define __STDC_LIMIT_MACROS
+/***********************************************************************************[SimpSolver.cc]
+Copyright (c) 2006, Niklas Een, Niklas Sorensson
+Copyright (c) 2007-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+#include "Sort.h"
+#include "SimpSolver.h"
+#include "System.h"
+
+using namespace Minisat;
+
+//=================================================================================================
+// Options:
+
+
+static const char* _cat = "SIMP";
+
+static BoolOption opt_use_asymm (_cat, "asymm", "Shrink clauses by asymmetric branching.", false);
+static BoolOption opt_use_rcheck (_cat, "rcheck", "Check if a clause is already implied. (costly)", false);
+static BoolOption opt_use_elim (_cat, "elim", "Perform variable elimination.", true);
+static IntOption opt_grow (_cat, "grow", "Allow a variable elimination step to grow by a number of clauses.", 0);
+static IntOption opt_clause_lim (_cat, "cl-lim", "Variables are not eliminated if it produces a resolvent with a length above this limit. -1 means no limit", 20, IntRange(-1, INT32_MAX));
+static IntOption opt_subsumption_lim (_cat, "sub-lim", "Do not check if subsumption against a clause larger than this. -1 means no limit.", 1000, IntRange(-1, INT32_MAX));
+static DoubleOption opt_simp_garbage_frac(_cat, "simp-gc-frac", "The fraction of wasted memory allowed before a garbage collection is triggered during simplification.", 0.5, DoubleRange(0, false, HUGE_VAL, false));
+
+
+//=================================================================================================
+// Constructor/Destructor:
+
+
+SimpSolver::SimpSolver() :
+ grow (opt_grow)
+ , clause_lim (opt_clause_lim)
+ , subsumption_lim (opt_subsumption_lim)
+ , simp_garbage_frac (opt_simp_garbage_frac)
+ , use_asymm (opt_use_asymm)
+ , use_rcheck (opt_use_rcheck)
+ , use_elim (opt_use_elim)
+ , extend_model (true)
+ , merges (0)
+ , asymm_lits (0)
+ , eliminated_vars (0)
+ , elimorder (1)
+ , use_simplification (true)
+ , occurs (ClauseDeleted(ca))
+ , elim_heap (ElimLt(n_occ))
+ , bwdsub_assigns (0)
+ , n_touched (0)
+{
+ vec<Lit> dummy(1,lit_Undef);
+ ca.extra_clause_field = true; // NOTE: must happen before allocating the dummy clause below.
+ bwdsub_tmpunit = ca.alloc(dummy);
+ remove_satisfied = false;
+}
+
+
+SimpSolver::~SimpSolver()
+{
+}
+
+
+Var SimpSolver::newVar(lbool upol, bool dvar) {
+ Var v = Solver::newVar(upol, dvar);
+
+ frozen .insert(v, (char)false);
+ eliminated.insert(v, (char)false);
+
+ if (use_simplification){
+ n_occ .insert( mkLit(v), 0);
+ n_occ .insert(~mkLit(v), 0);
+ occurs .init (v);
+ touched .insert(v, 0);
+ elim_heap .insert(v);
+ }
+ return v; }
+
+
+void SimpSolver::releaseVar(Lit l)
+{
+ assert(!isEliminated(var(l)));
+ if (!use_simplification && var(l) >= max_simp_var)
+ // Note: Guarantees that no references to this variable is
+ // left in model extension datastructure. Could be improved!
+ Solver::releaseVar(l);
+ else
+ // Otherwise, don't allow variable to be reused.
+ Solver::addClause(l);
+}
+
+
+lbool SimpSolver::solve_(bool do_simp, bool turn_off_simp)
+{
+ vec<Var> extra_frozen;
+ lbool result = l_True;
+
+ do_simp &= use_simplification;
+
+ if (do_simp){
+ // Assumptions must be temporarily frozen to run variable elimination:
+ for (int i = 0; i < assumptions.size(); i++){
+ Var v = var(assumptions[i]);
+
+ // If an assumption has been eliminated, remember it.
+ assert(!isEliminated(v));
+
+ if (!frozen[v]){
+ // Freeze and store.
+ setFrozen(v, true);
+ extra_frozen.push(v);
+ } }
+
+ result = lbool(eliminate(turn_off_simp));
+ }
+
+ if (result == l_True)
+ result = Solver::solve_();
+ else if (verbosity >= 1)
+ printf("===============================================================================\n");
+
+ if (result == l_True && extend_model)
+ extendModel();
+
+ if (do_simp)
+ // Unfreeze the assumptions that were frozen:
+ for (int i = 0; i < extra_frozen.size(); i++)
+ setFrozen(extra_frozen[i], false);
+
+ return result;
+}
+
+
+
+bool SimpSolver::addClause_(vec<Lit>& ps)
+{
+#ifndef NDEBUG
+ for (int i = 0; i < ps.size(); i++)
+ assert(!isEliminated(var(ps[i])));
+#endif
+
+ int nclauses = clauses.size();
+
+ if (use_rcheck && implied(ps))
+ return true;
+
+ if (!Solver::addClause_(ps))
+ return false;
+
+ if (use_simplification && clauses.size() == nclauses + 1){
+ CRef cr = clauses.last();
+ const Clause& c = ca[cr];
+
+ // NOTE: the clause is added to the queue immediately and then
+ // again during 'gatherTouchedClauses()'. If nothing happens
+ // in between, it will only be checked once. Otherwise, it may
+ // be checked twice unnecessarily. This is an unfortunate
+ // consequence of how backward subsumption is used to mimic
+ // forward subsumption.
+ subsumption_queue.insert(cr);
+ for (int i = 0; i < c.size(); i++){
+ occurs[var(c[i])].push(cr);
+ n_occ[c[i]]++;
+ touched[var(c[i])] = 1;
+ n_touched++;
+ if (elim_heap.inHeap(var(c[i])))
+ elim_heap.increase(var(c[i]));
+ }
+ }
+
+ return true;
+}
+
+
+void SimpSolver::removeClause(CRef cr)
+{
+ const Clause& c = ca[cr];
+
+ if (use_simplification)
+ for (int i = 0; i < c.size(); i++){
+ n_occ[c[i]]--;
+ updateElimHeap(var(c[i]));
+ occurs.smudge(var(c[i]));
+ }
+
+ Solver::removeClause(cr);
+}
+
+
+bool SimpSolver::strengthenClause(CRef cr, Lit l)
+{
+ Clause& c = ca[cr];
+ assert(decisionLevel() == 0);
+ assert(use_simplification);
+
+ // FIX: this is too inefficient but would be nice to have (properly implemented)
+ // if (!find(subsumption_queue, &c))
+ subsumption_queue.insert(cr);
+
+ if (c.size() == 2){
+ removeClause(cr);
+ c.strengthen(l);
+ }else{
+ detachClause(cr, true);
+ c.strengthen(l);
+ attachClause(cr);
+ remove(occurs[var(l)], cr);
+ n_occ[l]--;
+ updateElimHeap(var(l));
+ }
+
+ return c.size() == 1 ? enqueue(c[0]) && propagate() == CRef_Undef : true;
+}
+
+
+// Returns FALSE if clause is always satisfied ('out_clause' should not be used).
+bool SimpSolver::merge(const Clause& _ps, const Clause& _qs, Var v, vec<Lit>& out_clause)
+{
+ merges++;
+ out_clause.clear();
+
+ bool ps_smallest = _ps.size() < _qs.size();
+ const Clause& ps = ps_smallest ? _qs : _ps;
+ const Clause& qs = ps_smallest ? _ps : _qs;
+
+ for (int i = 0; i < qs.size(); i++){
+ if (var(qs[i]) != v){
+ for (int j = 0; j < ps.size(); j++)
+ if (var(ps[j]) == var(qs[i])){
+ if (ps[j] == ~qs[i])
+ return false;
+ else
+ goto next;
+ }
+ out_clause.push(qs[i]);
+ }
+ next:;
+ }
+
+ for (int i = 0; i < ps.size(); i++)
+ if (var(ps[i]) != v)
+ out_clause.push(ps[i]);
+
+ return true;
+}
+
+
+// Returns FALSE if clause is always satisfied.
+bool SimpSolver::merge(const Clause& _ps, const Clause& _qs, Var v, int& size)
+{
+ merges++;
+
+ bool ps_smallest = _ps.size() < _qs.size();
+ const Clause& ps = ps_smallest ? _qs : _ps;
+ const Clause& qs = ps_smallest ? _ps : _qs;
+ const Lit* __ps = (const Lit*)ps;
+ const Lit* __qs = (const Lit*)qs;
+
+ size = ps.size()-1;
+
+ for (int i = 0; i < qs.size(); i++){
+ if (var(__qs[i]) != v){
+ for (int j = 0; j < ps.size(); j++)
+ if (var(__ps[j]) == var(__qs[i])){
+ if (__ps[j] == ~__qs[i])
+ return false;
+ else
+ goto next;
+ }
+ size++;
+ }
+ next:;
+ }
+
+ return true;
+}
+
+
+void SimpSolver::gatherTouchedClauses()
+{
+ if (n_touched == 0) return;
+
+ int i,j;
+ for (i = j = 0; i < subsumption_queue.size(); i++)
+ if (ca[subsumption_queue[i]].mark() == 0)
+ ca[subsumption_queue[i]].mark(2);
+
+ for (i = 0; i < nVars(); i++)
+ if (touched[i]){
+ const vec<CRef>& cs = occurs.lookup(i);
+ for (j = 0; j < cs.size(); j++)
+ if (ca[cs[j]].mark() == 0){
+ subsumption_queue.insert(cs[j]);
+ ca[cs[j]].mark(2);
+ }
+ touched[i] = 0;
+ }
+
+ for (i = 0; i < subsumption_queue.size(); i++)
+ if (ca[subsumption_queue[i]].mark() == 2)
+ ca[subsumption_queue[i]].mark(0);
+
+ n_touched = 0;
+}
+
+
+bool SimpSolver::implied(const vec<Lit>& c)
+{
+ assert(decisionLevel() == 0);
+
+ trail_lim.push(trail.size());
+ for (int i = 0; i < c.size(); i++)
+ if (value(c[i]) == l_True){
+ cancelUntil(0);
+ return true;
+ }else if (value(c[i]) != l_False){
+ assert(value(c[i]) == l_Undef);
+ uncheckedEnqueue(~c[i]);
+ }
+
+ bool result = propagate() != CRef_Undef;
+ cancelUntil(0);
+ return result;
+}
+
+
+// Backward subsumption + backward subsumption resolution
+bool SimpSolver::backwardSubsumptionCheck(bool verbose)
+{
+ int cnt = 0;
+ int subsumed = 0;
+ int deleted_literals = 0;
+ assert(decisionLevel() == 0);
+
+ while (subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()){
+
+ // Empty subsumption queue and return immediately on user-interrupt:
+ if (asynch_interrupt){
+ subsumption_queue.clear();
+ bwdsub_assigns = trail.size();
+ break; }
+
+ // Check top-level assignments by creating a dummy clause and placing it in the queue:
+ if (subsumption_queue.size() == 0 && bwdsub_assigns < trail.size()){
+ Lit l = trail[bwdsub_assigns++];
+ ca[bwdsub_tmpunit][0] = l;
+ ca[bwdsub_tmpunit].calcAbstraction();
+ subsumption_queue.insert(bwdsub_tmpunit); }
+
+ CRef cr = subsumption_queue.peek(); subsumption_queue.pop();
+ Clause& c = ca[cr];
+
+ if (c.mark()) continue;
+
+ if (verbose && verbosity >= 2 && cnt++ % 1000 == 0)
+ printf("subsumption left: %10d (%10d subsumed, %10d deleted literals)\r", subsumption_queue.size(), subsumed, deleted_literals);
+
+ assert(c.size() > 1 || value(c[0]) == l_True); // Unit-clauses should have been propagated before this point.
+
+ // Find best variable to scan:
+ Var best = var(c[0]);
+ for (int i = 1; i < c.size(); i++)
+ if (occurs[var(c[i])].size() < occurs[best].size())
+ best = var(c[i]);
+
+ // Search all candidates:
+ vec<CRef>& _cs = occurs.lookup(best);
+ CRef* cs = (CRef*)_cs;
+
+ for (int j = 0; j < _cs.size(); j++)
+ if (c.mark())
+ break;
+ else if (!ca[cs[j]].mark() && cs[j] != cr && (subsumption_lim == -1 || ca[cs[j]].size() < subsumption_lim)){
+ Lit l = c.subsumes(ca[cs[j]]);
+
+ if (l == lit_Undef)
+ subsumed++, removeClause(cs[j]);
+ else if (l != lit_Error){
+ deleted_literals++;
+
+ if (!strengthenClause(cs[j], ~l))
+ return false;
+
+ // Did current candidate get deleted from cs? Then check candidate at index j again:
+ if (var(l) == best)
+ j--;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+bool SimpSolver::asymm(Var v, CRef cr)
+{
+ Clause& c = ca[cr];
+ assert(decisionLevel() == 0);
+
+ if (c.mark() || satisfied(c)) return true;
+
+ trail_lim.push(trail.size());
+ Lit l = lit_Undef;
+ for (int i = 0; i < c.size(); i++)
+ if (var(c[i]) != v && value(c[i]) != l_False)
+ uncheckedEnqueue(~c[i]);
+ else
+ l = c[i];
+
+ if (propagate() != CRef_Undef){
+ cancelUntil(0);
+ asymm_lits++;
+ if (!strengthenClause(cr, l))
+ return false;
+ }else
+ cancelUntil(0);
+
+ return true;
+}
+
+
+bool SimpSolver::asymmVar(Var v)
+{
+ assert(use_simplification);
+
+ const vec<CRef>& cls = occurs.lookup(v);
+
+ if (value(v) != l_Undef || cls.size() == 0)
+ return true;
+
+ for (int i = 0; i < cls.size(); i++)
+ if (!asymm(v, cls[i]))
+ return false;
+
+ return backwardSubsumptionCheck();
+}
+
+
+static void mkElimClause(vec<uint32_t>& elimclauses, Lit x)
+{
+ elimclauses.push(toInt(x));
+ elimclauses.push(1);
+}
+
+
+static void mkElimClause(vec<uint32_t>& elimclauses, Var v, Clause& c)
+{
+ int first = elimclauses.size();
+ int v_pos = -1;
+
+ // Copy clause to elimclauses-vector. Remember position where the
+ // variable 'v' occurs:
+ for (int i = 0; i < c.size(); i++){
+ elimclauses.push(toInt(c[i]));
+ if (var(c[i]) == v)
+ v_pos = i + first;
+ }
+ assert(v_pos != -1);
+
+ // Swap the first literal with the 'v' literal, so that the literal
+ // containing 'v' will occur first in the clause:
+ uint32_t tmp = elimclauses[v_pos];
+ elimclauses[v_pos] = elimclauses[first];
+ elimclauses[first] = tmp;
+
+ // Store the length of the clause last:
+ elimclauses.push(c.size());
+}
+
+
+
+bool SimpSolver::eliminateVar(Var v)
+{
+ assert(!frozen[v]);
+ assert(!isEliminated(v));
+ assert(value(v) == l_Undef);
+
+ // Split the occurrences into positive and negative:
+ //
+ const vec<CRef>& cls = occurs.lookup(v);
+ vec<CRef> pos, neg;
+ for (int i = 0; i < cls.size(); i++)
+ (find(ca[cls[i]], mkLit(v)) ? pos : neg).push(cls[i]);
+
+ // Check wether the increase in number of clauses stays within the allowed ('grow'). Moreover, no
+ // clause must exceed the limit on the maximal clause size (if it is set):
+ //
+ int cnt = 0;
+ int clause_size = 0;
+
+ for (int i = 0; i < pos.size(); i++)
+ for (int j = 0; j < neg.size(); j++)
+ if (merge(ca[pos[i]], ca[neg[j]], v, clause_size) &&
+ (++cnt > cls.size() + grow || (clause_lim != -1 && clause_size > clause_lim)))
+ return true;
+
+ // Delete and store old clauses:
+ eliminated[v] = true;
+ setDecisionVar(v, false);
+ eliminated_vars++;
+
+ if (pos.size() > neg.size()){
+ for (int i = 0; i < neg.size(); i++)
+ mkElimClause(elimclauses, v, ca[neg[i]]);
+ mkElimClause(elimclauses, mkLit(v));
+ }else{
+ for (int i = 0; i < pos.size(); i++)
+ mkElimClause(elimclauses, v, ca[pos[i]]);
+ mkElimClause(elimclauses, ~mkLit(v));
+ }
+
+ for (int i = 0; i < cls.size(); i++)
+ removeClause(cls[i]);
+
+ // Produce clauses in cross product:
+ vec<Lit>& resolvent = add_tmp;
+ for (int i = 0; i < pos.size(); i++)
+ for (int j = 0; j < neg.size(); j++)
+ if (merge(ca[pos[i]], ca[neg[j]], v, resolvent) && !addClause_(resolvent))
+ return false;
+
+ // Free occurs list for this variable:
+ occurs[v].clear(true);
+
+ // Free watchers lists for this variable, if possible:
+ if (watches[ mkLit(v)].size() == 0) watches[ mkLit(v)].clear(true);
+ if (watches[~mkLit(v)].size() == 0) watches[~mkLit(v)].clear(true);
+
+ return backwardSubsumptionCheck();
+}
+
+
+bool SimpSolver::substitute(Var v, Lit x)
+{
+ assert(!frozen[v]);
+ assert(!isEliminated(v));
+ assert(value(v) == l_Undef);
+
+ if (!ok) return false;
+
+ eliminated[v] = true;
+ setDecisionVar(v, false);
+ const vec<CRef>& cls = occurs.lookup(v);
+
+ vec<Lit>& subst_clause = add_tmp;
+ for (int i = 0; i < cls.size(); i++){
+ Clause& c = ca[cls[i]];
+
+ subst_clause.clear();
+ for (int j = 0; j < c.size(); j++){
+ Lit p = c[j];
+ subst_clause.push(var(p) == v ? x ^ sign(p) : p);
+ }
+
+ removeClause(cls[i]);
+
+ if (!addClause_(subst_clause))
+ return ok = false;
+ }
+
+ return true;
+}
+
+
+void SimpSolver::extendModel()
+{
+ int i, j;
+ Lit x;
+
+ for (i = elimclauses.size()-1; i > 0; i -= j){
+ for (j = elimclauses[i--]; j > 1; j--, i--)
+ if (modelValue(toLit(elimclauses[i])) != l_False)
+ goto next;
+
+ x = toLit(elimclauses[i]);
+ model[var(x)] = lbool(!sign(x));
+ next:;
+ }
+}
+
+
+bool SimpSolver::eliminate(bool turn_off_elim)
+{
+ if (!simplify())
+ return false;
+ else if (!use_simplification)
+ return true;
+
+ // Main simplification loop:
+ //
+ while (n_touched > 0 || bwdsub_assigns < trail.size() || elim_heap.size() > 0){
+
+ gatherTouchedClauses();
+ // printf(" ## (time = %6.2f s) BWD-SUB: queue = %d, trail = %d\n", cpuTime(), subsumption_queue.size(), trail.size() - bwdsub_assigns);
+ if ((subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()) &&
+ !backwardSubsumptionCheck(true)){
+ ok = false; goto cleanup; }
+
+ // Empty elim_heap and return immediately on user-interrupt:
+ if (asynch_interrupt){
+ assert(bwdsub_assigns == trail.size());
+ assert(subsumption_queue.size() == 0);
+ assert(n_touched == 0);
+ elim_heap.clear();
+ goto cleanup; }
+
+ // printf(" ## (time = %6.2f s) ELIM: vars = %d\n", cpuTime(), elim_heap.size());
+ for (int cnt = 0; !elim_heap.empty(); cnt++){
+ Var elim = elim_heap.removeMin();
+
+ if (asynch_interrupt) break;
+
+ if (isEliminated(elim) || value(elim) != l_Undef) continue;
+
+ if (verbosity >= 2 && cnt % 100 == 0)
+ printf("elimination left: %10d\r", elim_heap.size());
+
+ if (use_asymm){
+ // Temporarily freeze variable. Otherwise, it would immediately end up on the queue again:
+ bool was_frozen = frozen[elim];
+ frozen[elim] = true;
+ if (!asymmVar(elim)){
+ ok = false; goto cleanup; }
+ frozen[elim] = was_frozen; }
+
+ // At this point, the variable may have been set by assymetric branching, so check it
+ // again. Also, don't eliminate frozen variables:
+ if (use_elim && value(elim) == l_Undef && !frozen[elim] && !eliminateVar(elim)){
+ ok = false; goto cleanup; }
+
+ checkGarbage(simp_garbage_frac);
+ }
+
+ assert(subsumption_queue.size() == 0);
+ }
+ cleanup:
+
+ // If no more simplification is needed, free all simplification-related data structures:
+ if (turn_off_elim){
+ touched .clear(true);
+ occurs .clear(true);
+ n_occ .clear(true);
+ elim_heap.clear(true);
+ subsumption_queue.clear(true);
+
+ use_simplification = false;
+ remove_satisfied = true;
+ ca.extra_clause_field = false;
+ max_simp_var = nVars();
+
+ // Force full cleanup (this is safe and desirable since it only happens once):
+ rebuildOrderHeap();
+ garbageCollect();
+ }else{
+ // Cheaper cleanup:
+ checkGarbage();
+ }
+
+ if (verbosity >= 1 && elimclauses.size() > 0)
+ printf("| Eliminated clauses: %10.2f Mb |\n",
+ double(elimclauses.size() * sizeof(uint32_t)) / (1024*1024));
+
+ return ok;
+}
+
+
+//=================================================================================================
+// Garbage Collection methods:
+
+
+void SimpSolver::relocAll(ClauseAllocator& to)
+{
+ if (!use_simplification) return;
+
+ // All occurs lists:
+ //
+ for (int i = 0; i < nVars(); i++){
+ occurs.clean(i);
+ vec<CRef>& cs = occurs[i];
+ for (int j = 0; j < cs.size(); j++)
+ ca.reloc(cs[j], to);
+ }
+
+ // Subsumption queue:
+ //
+ for (int i = subsumption_queue.size(); i > 0; i--){
+ CRef cr = subsumption_queue.peek(); subsumption_queue.pop();
+ if (ca[cr].mark()) continue;
+ ca.reloc(cr, to);
+ subsumption_queue.insert(cr);
+ }
+
+ // Temporary clause:
+ //
+ ca.reloc(bwdsub_tmpunit, to);
+}
+
+
+void SimpSolver::garbageCollect()
+{
+ // Initialize the next region to a size corresponding to the estimated utilization degree. This
+ // is not precise but should avoid some unnecessary reallocations for the new region:
+ ClauseAllocator to(ca.size() - ca.wasted());
+
+ to.extra_clause_field = ca.extra_clause_field; // NOTE: this is important to keep (or lose) the extra fields.
+ relocAll(to);
+ Solver::relocAll(to);
+ if (verbosity >= 2)
+ printf("| Garbage collection: %12d bytes => %12d bytes |\n",
+ ca.size()*ClauseAllocator::Unit_Size, to.size()*ClauseAllocator::Unit_Size);
+ to.moveTo(ca);
+}
diff --git a/libs/minisat/SimpSolver.h b/libs/minisat/SimpSolver.h
new file mode 100644
index 00000000..76d5aca1
--- /dev/null
+++ b/libs/minisat/SimpSolver.h
@@ -0,0 +1,222 @@
+/************************************************************************************[SimpSolver.h]
+Copyright (c) 2006, Niklas Een, Niklas Sorensson
+Copyright (c) 2007-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+#ifndef Minisat_SimpSolver_h
+#define Minisat_SimpSolver_h
+
+#include "Queue.h"
+#include "Solver.h"
+
+
+namespace Minisat {
+
+//=================================================================================================
+
+
+class SimpSolver : public Solver {
+ public:
+ // Constructor/Destructor:
+ //
+ SimpSolver();
+ ~SimpSolver();
+
+ // Problem specification:
+ //
+ Var newVar (lbool upol = l_Undef, bool dvar = true);
+ void releaseVar(Lit l);
+ bool addClause (const vec<Lit>& ps);
+ bool addEmptyClause(); // Add the empty clause to the solver.
+ bool addClause (Lit p); // Add a unit clause to the solver.
+ bool addClause (Lit p, Lit q); // Add a binary clause to the solver.
+ bool addClause (Lit p, Lit q, Lit r); // Add a ternary clause to the solver.
+ bool addClause (Lit p, Lit q, Lit r, Lit s); // Add a quaternary clause to the solver.
+ bool addClause_( vec<Lit>& ps);
+ bool substitute(Var v, Lit x); // Replace all occurences of v with x (may cause a contradiction).
+
+ // Variable mode:
+ //
+ void setFrozen (Var v, bool b); // If a variable is frozen it will not be eliminated.
+ bool isEliminated(Var v) const;
+
+ // Alternative freeze interface (may replace 'setFrozen()'):
+ void freezeVar (Var v); // Freeze one variable so it will not be eliminated.
+ void thaw (); // Thaw all frozen variables.
+
+
+ // Solving:
+ //
+ bool solve (const vec<Lit>& assumps, bool do_simp = true, bool turn_off_simp = false);
+ lbool solveLimited(const vec<Lit>& assumps, bool do_simp = true, bool turn_off_simp = false);
+ bool solve ( bool do_simp = true, bool turn_off_simp = false);
+ bool solve (Lit p , bool do_simp = true, bool turn_off_simp = false);
+ bool solve (Lit p, Lit q, bool do_simp = true, bool turn_off_simp = false);
+ bool solve (Lit p, Lit q, Lit r, bool do_simp = true, bool turn_off_simp = false);
+ bool eliminate (bool turn_off_elim = false); // Perform variable elimination based simplification.
+
+ // Memory managment:
+ //
+ virtual void garbageCollect();
+
+
+ // Generate a (possibly simplified) DIMACS file:
+ //
+#if 0
+ void toDimacs (const char* file, const vec<Lit>& assumps);
+ void toDimacs (const char* file);
+ void toDimacs (const char* file, Lit p);
+ void toDimacs (const char* file, Lit p, Lit q);
+ void toDimacs (const char* file, Lit p, Lit q, Lit r);
+#endif
+
+ // Mode of operation:
+ //
+ int grow; // Allow a variable elimination step to grow by a number of clauses (default to zero).
+ int clause_lim; // Variables are not eliminated if it produces a resolvent with a length above this limit.
+ // -1 means no limit.
+ int subsumption_lim; // Do not check if subsumption against a clause larger than this. -1 means no limit.
+ double simp_garbage_frac; // A different limit for when to issue a GC during simplification (Also see 'garbage_frac').
+
+ bool use_asymm; // Shrink clauses by asymmetric branching.
+ bool use_rcheck; // Check if a clause is already implied. Prett costly, and subsumes subsumptions :)
+ bool use_elim; // Perform variable elimination.
+ bool extend_model; // Flag to indicate whether the user needs to look at the full model.
+
+ // Statistics:
+ //
+ int merges;
+ int asymm_lits;
+ int eliminated_vars;
+
+ protected:
+
+ // Helper structures:
+ //
+ struct ElimLt {
+ const LMap<int>& n_occ;
+ explicit ElimLt(const LMap<int>& no) : n_occ(no) {}
+
+ // TODO: are 64-bit operations here noticably bad on 32-bit platforms? Could use a saturating
+ // 32-bit implementation instead then, but this will have to do for now.
+ uint64_t cost (Var x) const { return (uint64_t)n_occ[mkLit(x)] * (uint64_t)n_occ[~mkLit(x)]; }
+ bool operator()(Var x, Var y) const { return cost(x) < cost(y); }
+
+ // TODO: investigate this order alternative more.
+ // bool operator()(Var x, Var y) const {
+ // int c_x = cost(x);
+ // int c_y = cost(y);
+ // return c_x < c_y || c_x == c_y && x < y; }
+ };
+
+ struct ClauseDeleted {
+ const ClauseAllocator& ca;
+ explicit ClauseDeleted(const ClauseAllocator& _ca) : ca(_ca) {}
+ bool operator()(const CRef& cr) const { return ca[cr].mark() == 1; } };
+
+ // Solver state:
+ //
+ int elimorder;
+ bool use_simplification;
+ Var max_simp_var; // Max variable at the point simplification was turned off.
+ vec<uint32_t> elimclauses;
+ VMap<char> touched;
+ OccLists<Var, vec<CRef>, ClauseDeleted>
+ occurs;
+ LMap<int> n_occ;
+ Heap<Var,ElimLt> elim_heap;
+ Queue<CRef> subsumption_queue;
+ VMap<char> frozen;
+ vec<Var> frozen_vars;
+ VMap<char> eliminated;
+ int bwdsub_assigns;
+ int n_touched;
+
+ // Temporaries:
+ //
+ CRef bwdsub_tmpunit;
+
+ // Main internal methods:
+ //
+ lbool solve_ (bool do_simp = true, bool turn_off_simp = false);
+ bool asymm (Var v, CRef cr);
+ bool asymmVar (Var v);
+ void updateElimHeap (Var v);
+ void gatherTouchedClauses ();
+ bool merge (const Clause& _ps, const Clause& _qs, Var v, vec<Lit>& out_clause);
+ bool merge (const Clause& _ps, const Clause& _qs, Var v, int& size);
+ bool backwardSubsumptionCheck (bool verbose = false);
+ bool eliminateVar (Var v);
+ void extendModel ();
+
+ void removeClause (CRef cr);
+ bool strengthenClause (CRef cr, Lit l);
+ bool implied (const vec<Lit>& c);
+ void relocAll (ClauseAllocator& to);
+};
+
+
+//=================================================================================================
+// Implementation of inline methods:
+
+
+inline bool SimpSolver::isEliminated (Var v) const { return eliminated[v]; }
+inline void SimpSolver::updateElimHeap(Var v) {
+ assert(use_simplification);
+ // if (!frozen[v] && !isEliminated(v) && value(v) == l_Undef)
+ if (elim_heap.inHeap(v) || (!frozen[v] && !isEliminated(v) && value(v) == l_Undef))
+ elim_heap.update(v); }
+
+
+inline bool SimpSolver::addClause (const vec<Lit>& ps) { ps.copyTo(add_tmp); return addClause_(add_tmp); }
+inline bool SimpSolver::addEmptyClause() { add_tmp.clear(); return addClause_(add_tmp); }
+inline bool SimpSolver::addClause (Lit p) { add_tmp.clear(); add_tmp.push(p); return addClause_(add_tmp); }
+inline bool SimpSolver::addClause (Lit p, Lit q) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); return addClause_(add_tmp); }
+inline bool SimpSolver::addClause (Lit p, Lit q, Lit r) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); add_tmp.push(r); return addClause_(add_tmp); }
+inline bool SimpSolver::addClause (Lit p, Lit q, Lit r, Lit s){ add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); add_tmp.push(r); add_tmp.push(s); return addClause_(add_tmp); }
+inline void SimpSolver::setFrozen (Var v, bool b) { frozen[v] = (char)b; if (use_simplification && !b) { updateElimHeap(v); } }
+
+inline void SimpSolver::freezeVar(Var v){
+ if (!frozen[v]){
+ frozen[v] = 1;
+ frozen_vars.push(v);
+ } }
+
+inline void SimpSolver::thaw(){
+ for (int i = 0; i < frozen_vars.size(); i++){
+ Var v = frozen_vars[i];
+ frozen[v] = 0;
+ if (use_simplification)
+ updateElimHeap(v);
+ }
+ frozen_vars.clear(); }
+
+inline bool SimpSolver::solve ( bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); return solve_(do_simp, turn_off_simp) == l_True; }
+inline bool SimpSolver::solve (Lit p , bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); return solve_(do_simp, turn_off_simp) == l_True; }
+inline bool SimpSolver::solve (Lit p, Lit q, bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); return solve_(do_simp, turn_off_simp) == l_True; }
+inline bool SimpSolver::solve (Lit p, Lit q, Lit r, bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); assumptions.push(r); return solve_(do_simp, turn_off_simp) == l_True; }
+inline bool SimpSolver::solve (const vec<Lit>& assumps, bool do_simp, bool turn_off_simp){
+ budgetOff(); assumps.copyTo(assumptions); return solve_(do_simp, turn_off_simp) == l_True; }
+
+inline lbool SimpSolver::solveLimited (const vec<Lit>& assumps, bool do_simp, bool turn_off_simp){
+ assumps.copyTo(assumptions); return solve_(do_simp, turn_off_simp); }
+
+//=================================================================================================
+}
+
+#endif
diff --git a/libs/minisat/Solver.cc b/libs/minisat/Solver.cc
new file mode 100644
index 00000000..ab476853
--- /dev/null
+++ b/libs/minisat/Solver.cc
@@ -0,0 +1,1068 @@
+#define __STDC_FORMAT_MACROS
+#define __STDC_LIMIT_MACROS
+/***************************************************************************************[Solver.cc]
+Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson
+Copyright (c) 2007-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+#include <math.h>
+
+#include "Alg.h"
+#include "Sort.h"
+#include "System.h"
+#include "Solver.h"
+
+using namespace Minisat;
+
+//=================================================================================================
+// Options:
+
+
+static const char* _cat = "CORE";
+
+static DoubleOption opt_var_decay (_cat, "var-decay", "The variable activity decay factor", 0.95, DoubleRange(0, false, 1, false));
+static DoubleOption opt_clause_decay (_cat, "cla-decay", "The clause activity decay factor", 0.999, DoubleRange(0, false, 1, false));
+static DoubleOption opt_random_var_freq (_cat, "rnd-freq", "The frequency with which the decision heuristic tries to choose a random variable", 0, DoubleRange(0, true, 1, true));
+static DoubleOption opt_random_seed (_cat, "rnd-seed", "Used by the random variable selection", 91648253, DoubleRange(0, false, HUGE_VAL, false));
+static IntOption opt_ccmin_mode (_cat, "ccmin-mode", "Controls conflict clause minimization (0=none, 1=basic, 2=deep)", 2, IntRange(0, 2));
+static IntOption opt_phase_saving (_cat, "phase-saving", "Controls the level of phase saving (0=none, 1=limited, 2=full)", 2, IntRange(0, 2));
+static BoolOption opt_rnd_init_act (_cat, "rnd-init", "Randomize the initial activity", false);
+static BoolOption opt_luby_restart (_cat, "luby", "Use the Luby restart sequence", true);
+static IntOption opt_restart_first (_cat, "rfirst", "The base restart interval", 100, IntRange(1, INT32_MAX));
+static DoubleOption opt_restart_inc (_cat, "rinc", "Restart interval increase factor", 2, DoubleRange(1, false, HUGE_VAL, false));
+static DoubleOption opt_garbage_frac (_cat, "gc-frac", "The fraction of wasted memory allowed before a garbage collection is triggered", 0.20, DoubleRange(0, false, HUGE_VAL, false));
+static IntOption opt_min_learnts_lim (_cat, "min-learnts", "Minimum learnt clause limit", 0, IntRange(0, INT32_MAX));
+
+
+//=================================================================================================
+// Constructor/Destructor:
+
+
+Solver::Solver() :
+
+ // Parameters (user settable):
+ //
+ verbosity (0)
+ , var_decay (opt_var_decay)
+ , clause_decay (opt_clause_decay)
+ , random_var_freq (opt_random_var_freq)
+ , random_seed (opt_random_seed)
+ , luby_restart (opt_luby_restart)
+ , ccmin_mode (opt_ccmin_mode)
+ , phase_saving (opt_phase_saving)
+ , rnd_pol (false)
+ , rnd_init_act (opt_rnd_init_act)
+ , garbage_frac (opt_garbage_frac)
+ , min_learnts_lim (opt_min_learnts_lim)
+ , restart_first (opt_restart_first)
+ , restart_inc (opt_restart_inc)
+
+ // Parameters (the rest):
+ //
+ , learntsize_factor((double)1/(double)3), learntsize_inc(1.1)
+
+ // Parameters (experimental):
+ //
+ , learntsize_adjust_start_confl (100)
+ , learntsize_adjust_inc (1.5)
+
+ // Statistics: (formerly in 'SolverStats')
+ //
+ , solves(0), starts(0), decisions(0), rnd_decisions(0), propagations(0), conflicts(0)
+ , dec_vars(0), num_clauses(0), num_learnts(0), clauses_literals(0), learnts_literals(0), max_literals(0), tot_literals(0)
+
+ , watches (WatcherDeleted(ca))
+ , order_heap (VarOrderLt(activity))
+ , ok (true)
+ , cla_inc (1)
+ , var_inc (1)
+ , qhead (0)
+ , simpDB_assigns (-1)
+ , simpDB_props (0)
+ , progress_estimate (0)
+ , remove_satisfied (true)
+ , next_var (0)
+
+ // Resource constraints:
+ //
+ , conflict_budget (-1)
+ , propagation_budget (-1)
+ , asynch_interrupt (false)
+{}
+
+
+Solver::~Solver()
+{
+}
+
+
+//=================================================================================================
+// Minor methods:
+
+
+// Creates a new SAT variable in the solver. If 'decision' is cleared, variable will not be
+// used as a decision variable (NOTE! This has effects on the meaning of a SATISFIABLE result).
+//
+Var Solver::newVar(lbool upol, bool dvar)
+{
+ Var v;
+ if (free_vars.size() > 0){
+ v = free_vars.last();
+ free_vars.pop();
+ }else
+ v = next_var++;
+
+ watches .init(mkLit(v, false));
+ watches .init(mkLit(v, true ));
+ assigns .insert(v, l_Undef);
+ vardata .insert(v, mkVarData(CRef_Undef, 0));
+ activity .insert(v, rnd_init_act ? drand(random_seed) * 0.00001 : 0);
+ seen .insert(v, 0);
+ polarity .insert(v, true);
+ user_pol .insert(v, upol);
+ decision .reserve(v);
+ trail .capacity(v+1);
+ setDecisionVar(v, dvar);
+ return v;
+}
+
+
+// Note: at the moment, only unassigned variable will be released (this is to avoid duplicate
+// releases of the same variable).
+void Solver::releaseVar(Lit l)
+{
+ if (value(l) == l_Undef){
+ addClause(l);
+ released_vars.push(var(l));
+ }
+}
+
+
+bool Solver::addClause_(vec<Lit>& ps)
+{
+ assert(decisionLevel() == 0);
+ if (!ok) return false;
+
+ // Check if clause is satisfied and remove false/duplicate literals:
+ sort(ps);
+ Lit p; int i, j;
+ for (i = j = 0, p = lit_Undef; i < ps.size(); i++)
+ if (value(ps[i]) == l_True || ps[i] == ~p)
+ return true;
+ else if (value(ps[i]) != l_False && ps[i] != p)
+ ps[j++] = p = ps[i];
+ ps.shrink(i - j);
+
+ if (ps.size() == 0)
+ return ok = false;
+ else if (ps.size() == 1){
+ uncheckedEnqueue(ps[0]);
+ return ok = (propagate() == CRef_Undef);
+ }else{
+ CRef cr = ca.alloc(ps, false);
+ clauses.push(cr);
+ attachClause(cr);
+ }
+
+ return true;
+}
+
+
+void Solver::attachClause(CRef cr){
+ const Clause& c = ca[cr];
+ assert(c.size() > 1);
+ watches[~c[0]].push(Watcher(cr, c[1]));
+ watches[~c[1]].push(Watcher(cr, c[0]));
+ if (c.learnt()) num_learnts++, learnts_literals += c.size();
+ else num_clauses++, clauses_literals += c.size();
+}
+
+
+void Solver::detachClause(CRef cr, bool strict){
+ const Clause& c = ca[cr];
+ assert(c.size() > 1);
+
+ // Strict or lazy detaching:
+ if (strict){
+ remove(watches[~c[0]], Watcher(cr, c[1]));
+ remove(watches[~c[1]], Watcher(cr, c[0]));
+ }else{
+ watches.smudge(~c[0]);
+ watches.smudge(~c[1]);
+ }
+
+ if (c.learnt()) num_learnts--, learnts_literals -= c.size();
+ else num_clauses--, clauses_literals -= c.size();
+}
+
+
+void Solver::removeClause(CRef cr) {
+ Clause& c = ca[cr];
+ detachClause(cr);
+ // Don't leave pointers to free'd memory!
+ if (locked(c)) vardata[var(c[0])].reason = CRef_Undef;
+ c.mark(1);
+ ca.free(cr);
+}
+
+
+bool Solver::satisfied(const Clause& c) const {
+ for (int i = 0; i < c.size(); i++)
+ if (value(c[i]) == l_True)
+ return true;
+ return false; }
+
+
+// Revert to the state at given level (keeping all assignment at 'level' but not beyond).
+//
+void Solver::cancelUntil(int level) {
+ if (decisionLevel() > level){
+ for (int c = trail.size()-1; c >= trail_lim[level]; c--){
+ Var x = var(trail[c]);
+ assigns [x] = l_Undef;
+ if (phase_saving > 1 || (phase_saving == 1 && c > trail_lim.last()))
+ polarity[x] = sign(trail[c]);
+ insertVarOrder(x); }
+ qhead = trail_lim[level];
+ trail.shrink(trail.size() - trail_lim[level]);
+ trail_lim.shrink(trail_lim.size() - level);
+ } }
+
+
+//=================================================================================================
+// Major methods:
+
+
+Lit Solver::pickBranchLit()
+{
+ Var next = var_Undef;
+
+ // Random decision:
+ if (drand(random_seed) < random_var_freq && !order_heap.empty()){
+ next = order_heap[irand(random_seed,order_heap.size())];
+ if (value(next) == l_Undef && decision[next])
+ rnd_decisions++; }
+
+ // Activity based decision:
+ while (next == var_Undef || value(next) != l_Undef || !decision[next])
+ if (order_heap.empty()){
+ next = var_Undef;
+ break;
+ }else
+ next = order_heap.removeMin();
+
+ // Choose polarity based on different polarity modes (global or per-variable):
+ if (next == var_Undef)
+ return lit_Undef;
+ else if (user_pol[next] != l_Undef)
+ return mkLit(next, user_pol[next] == l_True);
+ else if (rnd_pol)
+ return mkLit(next, drand(random_seed) < 0.5);
+ else
+ return mkLit(next, polarity[next]);
+}
+
+
+/*_________________________________________________________________________________________________
+|
+| analyze : (confl : Clause*) (out_learnt : vec<Lit>&) (out_btlevel : int&) -> [void]
+|
+| Description:
+| Analyze conflict and produce a reason clause.
+|
+| Pre-conditions:
+| * 'out_learnt' is assumed to be cleared.
+| * Current decision level must be greater than root level.
+|
+| Post-conditions:
+| * 'out_learnt[0]' is the asserting literal at level 'out_btlevel'.
+| * If out_learnt.size() > 1 then 'out_learnt[1]' has the greatest decision level of the
+| rest of literals. There may be others from the same level though.
+|
+|________________________________________________________________________________________________@*/
+void Solver::analyze(CRef confl, vec<Lit>& out_learnt, int& out_btlevel)
+{
+ int pathC = 0;
+ Lit p = lit_Undef;
+
+ // Generate conflict clause:
+ //
+ out_learnt.push(); // (leave room for the asserting literal)
+ int index = trail.size() - 1;
+
+ do{
+ assert(confl != CRef_Undef); // (otherwise should be UIP)
+ Clause& c = ca[confl];
+
+ if (c.learnt())
+ claBumpActivity(c);
+
+ for (int j = (p == lit_Undef) ? 0 : 1; j < c.size(); j++){
+ Lit q = c[j];
+
+ if (!seen[var(q)] && level(var(q)) > 0){
+ varBumpActivity(var(q));
+ seen[var(q)] = 1;
+ if (level(var(q)) >= decisionLevel())
+ pathC++;
+ else
+ out_learnt.push(q);
+ }
+ }
+
+ // Select next clause to look at:
+ while (!seen[var(trail[index--])]);
+ p = trail[index+1];
+ confl = reason(var(p));
+ seen[var(p)] = 0;
+ pathC--;
+
+ }while (pathC > 0);
+ out_learnt[0] = ~p;
+
+ // Simplify conflict clause:
+ //
+ int i, j;
+ out_learnt.copyTo(analyze_toclear);
+ if (ccmin_mode == 2){
+ for (i = j = 1; i < out_learnt.size(); i++)
+ if (reason(var(out_learnt[i])) == CRef_Undef || !litRedundant(out_learnt[i]))
+ out_learnt[j++] = out_learnt[i];
+
+ }else if (ccmin_mode == 1){
+ for (i = j = 1; i < out_learnt.size(); i++){
+ Var x = var(out_learnt[i]);
+
+ if (reason(x) == CRef_Undef)
+ out_learnt[j++] = out_learnt[i];
+ else{
+ Clause& c = ca[reason(var(out_learnt[i]))];
+ for (int k = 1; k < c.size(); k++)
+ if (!seen[var(c[k])] && level(var(c[k])) > 0){
+ out_learnt[j++] = out_learnt[i];
+ break; }
+ }
+ }
+ }else
+ i = j = out_learnt.size();
+
+ max_literals += out_learnt.size();
+ out_learnt.shrink(i - j);
+ tot_literals += out_learnt.size();
+
+ // Find correct backtrack level:
+ //
+ if (out_learnt.size() == 1)
+ out_btlevel = 0;
+ else{
+ int max_i = 1;
+ // Find the first literal assigned at the next-highest level:
+ for (int i = 2; i < out_learnt.size(); i++)
+ if (level(var(out_learnt[i])) > level(var(out_learnt[max_i])))
+ max_i = i;
+ // Swap-in this literal at index 1:
+ Lit p = out_learnt[max_i];
+ out_learnt[max_i] = out_learnt[1];
+ out_learnt[1] = p;
+ out_btlevel = level(var(p));
+ }
+
+ for (int j = 0; j < analyze_toclear.size(); j++) seen[var(analyze_toclear[j])] = 0; // ('seen[]' is now cleared)
+}
+
+
+// Check if 'p' can be removed from a conflict clause.
+bool Solver::litRedundant(Lit p)
+{
+ enum { seen_undef = 0, seen_source = 1, seen_removable = 2, seen_failed = 3 };
+ assert(seen[var(p)] == seen_undef || seen[var(p)] == seen_source);
+ assert(reason(var(p)) != CRef_Undef);
+
+ Clause* c = &ca[reason(var(p))];
+ vec<ShrinkStackElem>& stack = analyze_stack;
+ stack.clear();
+
+ for (uint32_t i = 1; ; i++){
+ if (i < (uint32_t)c->size()){
+ // Checking 'p'-parents 'l':
+ Lit l = (*c)[i];
+
+ // Variable at level 0 or previously removable:
+ if (level(var(l)) == 0 || seen[var(l)] == seen_source || seen[var(l)] == seen_removable){
+ continue; }
+
+ // Check variable can not be removed for some local reason:
+ if (reason(var(l)) == CRef_Undef || seen[var(l)] == seen_failed){
+ stack.push(ShrinkStackElem(0, p));
+ for (int i = 0; i < stack.size(); i++)
+ if (seen[var(stack[i].l)] == seen_undef){
+ seen[var(stack[i].l)] = seen_failed;
+ analyze_toclear.push(stack[i].l);
+ }
+
+ return false;
+ }
+
+ // Recursively check 'l':
+ stack.push(ShrinkStackElem(i, p));
+ i = 0;
+ p = l;
+ c = &ca[reason(var(p))];
+ }else{
+ // Finished with current element 'p' and reason 'c':
+ if (seen[var(p)] == seen_undef){
+ seen[var(p)] = seen_removable;
+ analyze_toclear.push(p);
+ }
+
+ // Terminate with success if stack is empty:
+ if (stack.size() == 0) break;
+
+ // Continue with top element on stack:
+ i = stack.last().i;
+ p = stack.last().l;
+ c = &ca[reason(var(p))];
+
+ stack.pop();
+ }
+ }
+
+ return true;
+}
+
+
+/*_________________________________________________________________________________________________
+|
+| analyzeFinal : (p : Lit) -> [void]
+|
+| Description:
+| Specialized analysis procedure to express the final conflict in terms of assumptions.
+| Calculates the (possibly empty) set of assumptions that led to the assignment of 'p', and
+| stores the result in 'out_conflict'.
+|________________________________________________________________________________________________@*/
+void Solver::analyzeFinal(Lit p, LSet& out_conflict)
+{
+ out_conflict.clear();
+ out_conflict.insert(p);
+
+ if (decisionLevel() == 0)
+ return;
+
+ seen[var(p)] = 1;
+
+ for (int i = trail.size()-1; i >= trail_lim[0]; i--){
+ Var x = var(trail[i]);
+ if (seen[x]){
+ if (reason(x) == CRef_Undef){
+ assert(level(x) > 0);
+ out_conflict.insert(~trail[i]);
+ }else{
+ Clause& c = ca[reason(x)];
+ for (int j = 1; j < c.size(); j++)
+ if (level(var(c[j])) > 0)
+ seen[var(c[j])] = 1;
+ }
+ seen[x] = 0;
+ }
+ }
+
+ seen[var(p)] = 0;
+}
+
+
+void Solver::uncheckedEnqueue(Lit p, CRef from)
+{
+ assert(value(p) == l_Undef);
+ assigns[var(p)] = lbool(!sign(p));
+ vardata[var(p)] = mkVarData(from, decisionLevel());
+ trail.push_(p);
+}
+
+
+/*_________________________________________________________________________________________________
+|
+| propagate : [void] -> [Clause*]
+|
+| Description:
+| Propagates all enqueued facts. If a conflict arises, the conflicting clause is returned,
+| otherwise CRef_Undef.
+|
+| Post-conditions:
+| * the propagation queue is empty, even if there was a conflict.
+|________________________________________________________________________________________________@*/
+CRef Solver::propagate()
+{
+ CRef confl = CRef_Undef;
+ int num_props = 0;
+
+ while (qhead < trail.size()){
+ Lit p = trail[qhead++]; // 'p' is enqueued fact to propagate.
+ vec<Watcher>& ws = watches.lookup(p);
+ Watcher *i, *j, *end;
+ num_props++;
+
+ for (i = j = (Watcher*)ws, end = i + ws.size(); i != end;){
+ // Try to avoid inspecting the clause:
+ Lit blocker = i->blocker;
+ if (value(blocker) == l_True){
+ *j++ = *i++; continue; }
+
+ // Make sure the false literal is data[1]:
+ CRef cr = i->cref;
+ Clause& c = ca[cr];
+ Lit false_lit = ~p;
+ if (c[0] == false_lit)
+ c[0] = c[1], c[1] = false_lit;
+ assert(c[1] == false_lit);
+ i++;
+
+ // If 0th watch is true, then clause is already satisfied.
+ Lit first = c[0];
+ Watcher w = Watcher(cr, first);
+ if (first != blocker && value(first) == l_True){
+ *j++ = w; continue; }
+
+ // Look for new watch:
+ for (int k = 2; k < c.size(); k++)
+ if (value(c[k]) != l_False){
+ c[1] = c[k]; c[k] = false_lit;
+ watches[~c[1]].push(w);
+ goto NextClause; }
+
+ // Did not find watch -- clause is unit under assignment:
+ *j++ = w;
+ if (value(first) == l_False){
+ confl = cr;
+ qhead = trail.size();
+ // Copy the remaining watches:
+ while (i < end)
+ *j++ = *i++;
+ }else
+ uncheckedEnqueue(first, cr);
+
+ NextClause:;
+ }
+ ws.shrink(i - j);
+ }
+ propagations += num_props;
+ simpDB_props -= num_props;
+
+ return confl;
+}
+
+
+/*_________________________________________________________________________________________________
+|
+| reduceDB : () -> [void]
+|
+| Description:
+| Remove half of the learnt clauses, minus the clauses locked by the current assignment. Locked
+| clauses are clauses that are reason to some assignment. Binary clauses are never removed.
+|________________________________________________________________________________________________@*/
+struct reduceDB_lt {
+ ClauseAllocator& ca;
+ reduceDB_lt(ClauseAllocator& ca_) : ca(ca_) {}
+ bool operator () (CRef x, CRef y) {
+ return ca[x].size() > 2 && (ca[y].size() == 2 || ca[x].activity() < ca[y].activity()); }
+};
+void Solver::reduceDB()
+{
+ int i, j;
+ double extra_lim = cla_inc / learnts.size(); // Remove any clause below this activity
+
+ sort(learnts, reduceDB_lt(ca));
+ // Don't delete binary or locked clauses. From the rest, delete clauses from the first half
+ // and clauses with activity smaller than 'extra_lim':
+ for (i = j = 0; i < learnts.size(); i++){
+ Clause& c = ca[learnts[i]];
+ if (c.size() > 2 && !locked(c) && (i < learnts.size() / 2 || c.activity() < extra_lim))
+ removeClause(learnts[i]);
+ else
+ learnts[j++] = learnts[i];
+ }
+ learnts.shrink(i - j);
+ checkGarbage();
+}
+
+
+void Solver::removeSatisfied(vec<CRef>& cs)
+{
+ int i, j;
+ for (i = j = 0; i < cs.size(); i++){
+ Clause& c = ca[cs[i]];
+ if (satisfied(c))
+ removeClause(cs[i]);
+ else{
+ // Trim clause:
+ assert(value(c[0]) == l_Undef && value(c[1]) == l_Undef);
+ for (int k = 2; k < c.size(); k++)
+ if (value(c[k]) == l_False){
+ c[k--] = c[c.size()-1];
+ c.pop();
+ }
+ cs[j++] = cs[i];
+ }
+ }
+ cs.shrink(i - j);
+}
+
+
+void Solver::rebuildOrderHeap()
+{
+ vec<Var> vs;
+ for (Var v = 0; v < nVars(); v++)
+ if (decision[v] && value(v) == l_Undef)
+ vs.push(v);
+ order_heap.build(vs);
+}
+
+
+/*_________________________________________________________________________________________________
+|
+| simplify : [void] -> [bool]
+|
+| Description:
+| Simplify the clause database according to the current top-level assigment. Currently, the only
+| thing done here is the removal of satisfied clauses, but more things can be put here.
+|________________________________________________________________________________________________@*/
+bool Solver::simplify()
+{
+ assert(decisionLevel() == 0);
+
+ if (!ok || propagate() != CRef_Undef)
+ return ok = false;
+
+ if (nAssigns() == simpDB_assigns || (simpDB_props > 0))
+ return true;
+
+ // Remove satisfied clauses:
+ removeSatisfied(learnts);
+ if (remove_satisfied){ // Can be turned off.
+ removeSatisfied(clauses);
+
+ // TODO: what todo in if 'remove_satisfied' is false?
+
+ // Remove all released variables from the trail:
+ for (int i = 0; i < released_vars.size(); i++){
+ assert(seen[released_vars[i]] == 0);
+ seen[released_vars[i]] = 1;
+ }
+
+ int i, j;
+ for (i = j = 0; i < trail.size(); i++)
+ if (seen[var(trail[i])] == 0)
+ trail[j++] = trail[i];
+ trail.shrink(i - j);
+ //printf("trail.size()= %d, qhead = %d\n", trail.size(), qhead);
+ qhead = trail.size();
+
+ for (int i = 0; i < released_vars.size(); i++)
+ seen[released_vars[i]] = 0;
+
+ // Released variables are now ready to be reused:
+ append(released_vars, free_vars);
+ released_vars.clear();
+ }
+ checkGarbage();
+ rebuildOrderHeap();
+
+ simpDB_assigns = nAssigns();
+ simpDB_props = clauses_literals + learnts_literals; // (shouldn't depend on stats really, but it will do for now)
+
+ return true;
+}
+
+
+/*_________________________________________________________________________________________________
+|
+| search : (nof_conflicts : int) (params : const SearchParams&) -> [lbool]
+|
+| Description:
+| Search for a model the specified number of conflicts.
+| NOTE! Use negative value for 'nof_conflicts' indicate infinity.
+|
+| Output:
+| 'l_True' if a partial assigment that is consistent with respect to the clauseset is found. If
+| all variables are decision variables, this means that the clause set is satisfiable. 'l_False'
+| if the clause set is unsatisfiable. 'l_Undef' if the bound on number of conflicts is reached.
+|________________________________________________________________________________________________@*/
+lbool Solver::search(int nof_conflicts)
+{
+ assert(ok);
+ int backtrack_level;
+ int conflictC = 0;
+ vec<Lit> learnt_clause;
+ starts++;
+
+ for (;;){
+ CRef confl = propagate();
+ if (confl != CRef_Undef){
+ // CONFLICT
+ conflicts++; conflictC++;
+ if (decisionLevel() == 0) return l_False;
+
+ learnt_clause.clear();
+ analyze(confl, learnt_clause, backtrack_level);
+ cancelUntil(backtrack_level);
+
+ if (learnt_clause.size() == 1){
+ uncheckedEnqueue(learnt_clause[0]);
+ }else{
+ CRef cr = ca.alloc(learnt_clause, true);
+ learnts.push(cr);
+ attachClause(cr);
+ claBumpActivity(ca[cr]);
+ uncheckedEnqueue(learnt_clause[0], cr);
+ }
+
+ varDecayActivity();
+ claDecayActivity();
+
+ if (--learntsize_adjust_cnt == 0){
+ learntsize_adjust_confl *= learntsize_adjust_inc;
+ learntsize_adjust_cnt = (int)learntsize_adjust_confl;
+ max_learnts *= learntsize_inc;
+
+ if (verbosity >= 1)
+ printf("| %9d | %7d %8d %8d | %8d %8d %6.0f | %6.3f %% |\n",
+ (int)conflicts,
+ (int)dec_vars - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]), nClauses(), (int)clauses_literals,
+ (int)max_learnts, nLearnts(), (double)learnts_literals/nLearnts(), progressEstimate()*100);
+ }
+
+ }else{
+ // NO CONFLICT
+ if ((nof_conflicts >= 0 && conflictC >= nof_conflicts) || !withinBudget()){
+ // Reached bound on number of conflicts:
+ progress_estimate = progressEstimate();
+ cancelUntil(0);
+ return l_Undef; }
+
+ // Simplify the set of problem clauses:
+ if (decisionLevel() == 0 && !simplify())
+ return l_False;
+
+ if (learnts.size()-nAssigns() >= max_learnts)
+ // Reduce the set of learnt clauses:
+ reduceDB();
+
+ Lit next = lit_Undef;
+ while (decisionLevel() < assumptions.size()){
+ // Perform user provided assumption:
+ Lit p = assumptions[decisionLevel()];
+ if (value(p) == l_True){
+ // Dummy decision level:
+ newDecisionLevel();
+ }else if (value(p) == l_False){
+ analyzeFinal(~p, conflict);
+ return l_False;
+ }else{
+ next = p;
+ break;
+ }
+ }
+
+ if (next == lit_Undef){
+ // New variable decision:
+ decisions++;
+ next = pickBranchLit();
+
+ if (next == lit_Undef)
+ // Model found:
+ return l_True;
+ }
+
+ // Increase decision level and enqueue 'next'
+ newDecisionLevel();
+ uncheckedEnqueue(next);
+ }
+ }
+}
+
+
+double Solver::progressEstimate() const
+{
+ double progress = 0;
+ double F = 1.0 / nVars();
+
+ for (int i = 0; i <= decisionLevel(); i++){
+ int beg = i == 0 ? 0 : trail_lim[i - 1];
+ int end = i == decisionLevel() ? trail.size() : trail_lim[i];
+ progress += pow(F, i) * (end - beg);
+ }
+
+ return progress / nVars();
+}
+
+/*
+ Finite subsequences of the Luby-sequence:
+
+ 0: 1
+ 1: 1 1 2
+ 2: 1 1 2 1 1 2 4
+ 3: 1 1 2 1 1 2 4 1 1 2 1 1 2 4 8
+ ...
+
+
+ */
+
+static double luby(double y, int x){
+
+ // Find the finite subsequence that contains index 'x', and the
+ // size of that subsequence:
+ int size, seq;
+ for (size = 1, seq = 0; size < x+1; seq++, size = 2*size+1);
+
+ while (size-1 != x){
+ size = (size-1)>>1;
+ seq--;
+ x = x % size;
+ }
+
+ return pow(y, seq);
+}
+
+// NOTE: assumptions passed in member-variable 'assumptions'.
+lbool Solver::solve_()
+{
+ model.clear();
+ conflict.clear();
+ if (!ok) return l_False;
+
+ solves++;
+
+ max_learnts = nClauses() * learntsize_factor;
+ if (max_learnts < min_learnts_lim)
+ max_learnts = min_learnts_lim;
+
+ learntsize_adjust_confl = learntsize_adjust_start_confl;
+ learntsize_adjust_cnt = (int)learntsize_adjust_confl;
+ lbool status = l_Undef;
+
+ if (verbosity >= 1){
+ printf("============================[ Search Statistics ]==============================\n");
+ printf("| Conflicts | ORIGINAL | LEARNT | Progress |\n");
+ printf("| | Vars Clauses Literals | Limit Clauses Lit/Cl | |\n");
+ printf("===============================================================================\n");
+ }
+
+ // Search:
+ int curr_restarts = 0;
+ while (status == l_Undef){
+ double rest_base = luby_restart ? luby(restart_inc, curr_restarts) : pow(restart_inc, curr_restarts);
+ status = search(rest_base * restart_first);
+ if (!withinBudget()) break;
+ curr_restarts++;
+ }
+
+ if (verbosity >= 1)
+ printf("===============================================================================\n");
+
+
+ if (status == l_True){
+ // Extend & copy model:
+ model.growTo(nVars());
+ for (int i = 0; i < nVars(); i++) model[i] = value(i);
+ }else if (status == l_False && conflict.size() == 0)
+ ok = false;
+
+ cancelUntil(0);
+ return status;
+}
+
+
+bool Solver::implies(const vec<Lit>& assumps, vec<Lit>& out)
+{
+ trail_lim.push(trail.size());
+ for (int i = 0; i < assumps.size(); i++){
+ Lit a = assumps[i];
+
+ if (value(a) == l_False){
+ cancelUntil(0);
+ return false;
+ }else if (value(a) == l_Undef)
+ uncheckedEnqueue(a);
+ }
+
+ unsigned trail_before = trail.size();
+ bool ret = true;
+ if (propagate() == CRef_Undef){
+ out.clear();
+ for (int j = trail_before; j < trail.size(); j++)
+ out.push(trail[j]);
+ }else
+ ret = false;
+
+ cancelUntil(0);
+ return ret;
+}
+
+//=================================================================================================
+// Writing CNF to DIMACS:
+//
+// FIXME: this needs to be rewritten completely.
+
+static Var mapVar(Var x, vec<Var>& map, Var& max)
+{
+ if (map.size() <= x || map[x] == -1){
+ map.growTo(x+1, -1);
+ map[x] = max++;
+ }
+ return map[x];
+}
+
+
+void Solver::toDimacs(FILE* f, Clause& c, vec<Var>& map, Var& max)
+{
+ if (satisfied(c)) return;
+
+ for (int i = 0; i < c.size(); i++)
+ if (value(c[i]) != l_False)
+ fprintf(f, "%s%d ", sign(c[i]) ? "-" : "", mapVar(var(c[i]), map, max)+1);
+ fprintf(f, "0\n");
+}
+
+
+void Solver::toDimacs(const char *file, const vec<Lit>& assumps)
+{
+ FILE* f = fopen(file, "wr");
+ if (f == NULL)
+ fprintf(stderr, "could not open file %s\n", file), exit(1);
+ toDimacs(f, assumps);
+ fclose(f);
+}
+
+
+void Solver::toDimacs(FILE* f, const vec<Lit>& assumps)
+{
+ // Handle case when solver is in contradictory state:
+ if (!ok){
+ fprintf(f, "p cnf 1 2\n1 0\n-1 0\n");
+ return; }
+
+ vec<Var> map; Var max = 0;
+
+ // Cannot use removeClauses here because it is not safe
+ // to deallocate them at this point. Could be improved.
+ int cnt = 0;
+ for (int i = 0; i < clauses.size(); i++)
+ if (!satisfied(ca[clauses[i]]))
+ cnt++;
+
+ for (int i = 0; i < clauses.size(); i++)
+ if (!satisfied(ca[clauses[i]])){
+ Clause& c = ca[clauses[i]];
+ for (int j = 0; j < c.size(); j++)
+ if (value(c[j]) != l_False)
+ mapVar(var(c[j]), map, max);
+ }
+
+ // Assumptions are added as unit clauses:
+ cnt += assumps.size();
+
+ fprintf(f, "p cnf %d %d\n", max, cnt);
+
+ for (int i = 0; i < assumps.size(); i++){
+ assert(value(assumps[i]) != l_False);
+ fprintf(f, "%s%d 0\n", sign(assumps[i]) ? "-" : "", mapVar(var(assumps[i]), map, max)+1);
+ }
+
+ for (int i = 0; i < clauses.size(); i++)
+ toDimacs(f, ca[clauses[i]], map, max);
+
+ if (verbosity > 0)
+ printf("Wrote DIMACS with %d variables and %d clauses.\n", max, cnt);
+}
+
+
+void Solver::printStats() const
+{
+ double cpu_time = cpuTime();
+ double mem_used = memUsedPeak();
+ printf("restarts : %" PRIu64 "\n", starts);
+ printf("conflicts : %-12" PRIu64 " (%.0f /sec)\n", conflicts , conflicts /cpu_time);
+ printf("decisions : %-12" PRIu64 " (%4.2f %% random) (%.0f /sec)\n", decisions, (float)rnd_decisions*100 / (float)decisions, decisions /cpu_time);
+ printf("propagations : %-12" PRIu64 " (%.0f /sec)\n", propagations, propagations/cpu_time);
+ printf("conflict literals : %-12" PRIu64 " (%4.2f %% deleted)\n", tot_literals, (max_literals - tot_literals)*100 / (double)max_literals);
+ if (mem_used != 0) printf("Memory used : %.2f MB\n", mem_used);
+ printf("CPU time : %g s\n", cpu_time);
+}
+
+
+//=================================================================================================
+// Garbage Collection methods:
+
+void Solver::relocAll(ClauseAllocator& to)
+{
+ // All watchers:
+ //
+ watches.cleanAll();
+ for (int v = 0; v < nVars(); v++)
+ for (int s = 0; s < 2; s++){
+ Lit p = mkLit(v, s);
+ vec<Watcher>& ws = watches[p];
+ for (int j = 0; j < ws.size(); j++)
+ ca.reloc(ws[j].cref, to);
+ }
+
+ // All reasons:
+ //
+ for (int i = 0; i < trail.size(); i++){
+ Var v = var(trail[i]);
+
+ // Note: it is not safe to call 'locked()' on a relocated clause. This is why we keep
+ // 'dangling' reasons here. It is safe and does not hurt.
+ if (reason(v) != CRef_Undef && (ca[reason(v)].reloced() || locked(ca[reason(v)]))){
+ assert(!isRemoved(reason(v)));
+ ca.reloc(vardata[v].reason, to);
+ }
+ }
+
+ // All learnt:
+ //
+ int i, j;
+ for (i = j = 0; i < learnts.size(); i++)
+ if (!isRemoved(learnts[i])){
+ ca.reloc(learnts[i], to);
+ learnts[j++] = learnts[i];
+ }
+ learnts.shrink(i - j);
+
+ // All original:
+ //
+ for (i = j = 0; i < clauses.size(); i++)
+ if (!isRemoved(clauses[i])){
+ ca.reloc(clauses[i], to);
+ clauses[j++] = clauses[i];
+ }
+ clauses.shrink(i - j);
+}
+
+
+void Solver::garbageCollect()
+{
+ // Initialize the next region to a size corresponding to the estimated utilization degree. This
+ // is not precise but should avoid some unnecessary reallocations for the new region:
+ ClauseAllocator to(ca.size() - ca.wasted());
+
+ relocAll(to);
+ if (verbosity >= 2)
+ printf("| Garbage collection: %12d bytes => %12d bytes |\n",
+ ca.size()*ClauseAllocator::Unit_Size, to.size()*ClauseAllocator::Unit_Size);
+ to.moveTo(ca);
+}
diff --git a/libs/minisat/Solver.h b/libs/minisat/Solver.h
new file mode 100644
index 00000000..44570b0e
--- /dev/null
+++ b/libs/minisat/Solver.h
@@ -0,0 +1,409 @@
+/****************************************************************************************[Solver.h]
+Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson
+Copyright (c) 2007-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+#ifndef Minisat_Solver_h
+#define Minisat_Solver_h
+
+#include "Vec.h"
+#include "Heap.h"
+#include "Alg.h"
+#include "IntMap.h"
+#include "Options.h"
+#include "SolverTypes.h"
+
+
+namespace Minisat {
+
+//=================================================================================================
+// Solver -- the main class:
+
+class Solver {
+public:
+
+ // Constructor/Destructor:
+ //
+ Solver();
+ virtual ~Solver();
+
+ // Problem specification:
+ //
+ Var newVar (lbool upol = l_Undef, bool dvar = true); // Add a new variable with parameters specifying variable mode.
+ void releaseVar(Lit l); // Make literal true and promise to never refer to variable again.
+
+ bool addClause (const vec<Lit>& ps); // Add a clause to the solver.
+ bool addEmptyClause(); // Add the empty clause, making the solver contradictory.
+ bool addClause (Lit p); // Add a unit clause to the solver.
+ bool addClause (Lit p, Lit q); // Add a binary clause to the solver.
+ bool addClause (Lit p, Lit q, Lit r); // Add a ternary clause to the solver.
+ bool addClause (Lit p, Lit q, Lit r, Lit s); // Add a quaternary clause to the solver.
+ bool addClause_( vec<Lit>& ps); // Add a clause to the solver without making superflous internal copy. Will
+ // change the passed vector 'ps'.
+
+ // Solving:
+ //
+ bool simplify (); // Removes already satisfied clauses.
+ bool solve (const vec<Lit>& assumps); // Search for a model that respects a given set of assumptions.
+ lbool solveLimited (const vec<Lit>& assumps); // Search for a model that respects a given set of assumptions (With resource constraints).
+ bool solve (); // Search without assumptions.
+ bool solve (Lit p); // Search for a model that respects a single assumption.
+ bool solve (Lit p, Lit q); // Search for a model that respects two assumptions.
+ bool solve (Lit p, Lit q, Lit r); // Search for a model that respects three assumptions.
+ bool okay () const; // FALSE means solver is in a conflicting state
+
+ bool implies (const vec<Lit>& assumps, vec<Lit>& out);
+
+ // Iterate over clauses and top-level assignments:
+ ClauseIterator clausesBegin() const;
+ ClauseIterator clausesEnd() const;
+ TrailIterator trailBegin() const;
+ TrailIterator trailEnd () const;
+
+ void toDimacs (FILE* f, const vec<Lit>& assumps); // Write CNF to file in DIMACS-format.
+ void toDimacs (const char *file, const vec<Lit>& assumps);
+ void toDimacs (FILE* f, Clause& c, vec<Var>& map, Var& max);
+
+ // Convenience versions of 'toDimacs()':
+ void toDimacs (const char* file);
+ void toDimacs (const char* file, Lit p);
+ void toDimacs (const char* file, Lit p, Lit q);
+ void toDimacs (const char* file, Lit p, Lit q, Lit r);
+
+ // Variable mode:
+ //
+ void setPolarity (Var v, lbool b); // Declare which polarity the decision heuristic should use for a variable. Requires mode 'polarity_user'.
+ void setDecisionVar (Var v, bool b); // Declare if a variable should be eligible for selection in the decision heuristic.
+
+ // Read state:
+ //
+ lbool value (Var x) const; // The current value of a variable.
+ lbool value (Lit p) const; // The current value of a literal.
+ lbool modelValue (Var x) const; // The value of a variable in the last model. The last call to solve must have been satisfiable.
+ lbool modelValue (Lit p) const; // The value of a literal in the last model. The last call to solve must have been satisfiable.
+ int nAssigns () const; // The current number of assigned literals.
+ int nClauses () const; // The current number of original clauses.
+ int nLearnts () const; // The current number of learnt clauses.
+ int nVars () const; // The current number of variables.
+ int nFreeVars () const;
+ void printStats () const; // Print some current statistics to standard output.
+
+ // Resource constraints:
+ //
+ void setConfBudget(int64_t x);
+ void setPropBudget(int64_t x);
+ void budgetOff();
+ void interrupt(); // Trigger a (potentially asynchronous) interruption of the solver.
+ void clearInterrupt(); // Clear interrupt indicator flag.
+
+ // Memory managment:
+ //
+ virtual void garbageCollect();
+ void checkGarbage(double gf);
+ void checkGarbage();
+
+ // Extra results: (read-only member variable)
+ //
+ vec<lbool> model; // If problem is satisfiable, this vector contains the model (if any).
+ LSet conflict; // If problem is unsatisfiable (possibly under assumptions),
+ // this vector represent the final conflict clause expressed in the assumptions.
+
+ // Mode of operation:
+ //
+ int verbosity;
+ double var_decay;
+ double clause_decay;
+ double random_var_freq;
+ double random_seed;
+ bool luby_restart;
+ int ccmin_mode; // Controls conflict clause minimization (0=none, 1=basic, 2=deep).
+ int phase_saving; // Controls the level of phase saving (0=none, 1=limited, 2=full).
+ bool rnd_pol; // Use random polarities for branching heuristics.
+ bool rnd_init_act; // Initialize variable activities with a small random value.
+ double garbage_frac; // The fraction of wasted memory allowed before a garbage collection is triggered.
+ int min_learnts_lim; // Minimum number to set the learnts limit to.
+
+ int restart_first; // The initial restart limit. (default 100)
+ double restart_inc; // The factor with which the restart limit is multiplied in each restart. (default 1.5)
+ double learntsize_factor; // The intitial limit for learnt clauses is a factor of the original clauses. (default 1 / 3)
+ double learntsize_inc; // The limit for learnt clauses is multiplied with this factor each restart. (default 1.1)
+
+ int learntsize_adjust_start_confl;
+ double learntsize_adjust_inc;
+
+ // Statistics: (read-only member variable)
+ //
+ uint64_t solves, starts, decisions, rnd_decisions, propagations, conflicts;
+ uint64_t dec_vars, num_clauses, num_learnts, clauses_literals, learnts_literals, max_literals, tot_literals;
+
+protected:
+
+ // Helper structures:
+ //
+ struct VarData { CRef reason; int level; };
+ static inline VarData mkVarData(CRef cr, int l){ VarData d = {cr, l}; return d; }
+
+ struct Watcher {
+ CRef cref;
+ Lit blocker;
+ Watcher(CRef cr, Lit p) : cref(cr), blocker(p) {}
+ bool operator==(const Watcher& w) const { return cref == w.cref; }
+ bool operator!=(const Watcher& w) const { return cref != w.cref; }
+ };
+
+ struct WatcherDeleted
+ {
+ const ClauseAllocator& ca;
+ WatcherDeleted(const ClauseAllocator& _ca) : ca(_ca) {}
+ bool operator()(const Watcher& w) const { return ca[w.cref].mark() == 1; }
+ };
+
+ struct VarOrderLt {
+ const IntMap<Var, double>& activity;
+ bool operator () (Var x, Var y) const { return activity[x] > activity[y]; }
+ VarOrderLt(const IntMap<Var, double>& act) : activity(act) { }
+ };
+
+ struct ShrinkStackElem {
+ uint32_t i;
+ Lit l;
+ ShrinkStackElem(uint32_t _i, Lit _l) : i(_i), l(_l){}
+ };
+
+ // Solver state:
+ //
+ vec<CRef> clauses; // List of problem clauses.
+ vec<CRef> learnts; // List of learnt clauses.
+ vec<Lit> trail; // Assignment stack; stores all assigments made in the order they were made.
+ vec<int> trail_lim; // Separator indices for different decision levels in 'trail'.
+ vec<Lit> assumptions; // Current set of assumptions provided to solve by the user.
+
+ VMap<double> activity; // A heuristic measurement of the activity of a variable.
+ VMap<lbool> assigns; // The current assignments.
+ VMap<char> polarity; // The preferred polarity of each variable.
+ VMap<lbool> user_pol; // The users preferred polarity of each variable.
+ VMap<char> decision; // Declares if a variable is eligible for selection in the decision heuristic.
+ VMap<VarData> vardata; // Stores reason and level for each variable.
+ OccLists<Lit, vec<Watcher>, WatcherDeleted, MkIndexLit>
+ watches; // 'watches[lit]' is a list of constraints watching 'lit' (will go there if literal becomes true).
+
+ Heap<Var,VarOrderLt>order_heap; // A priority queue of variables ordered with respect to the variable activity.
+
+ bool ok; // If FALSE, the constraints are already unsatisfiable. No part of the solver state may be used!
+ double cla_inc; // Amount to bump next clause with.
+ double var_inc; // Amount to bump next variable with.
+ int qhead; // Head of queue (as index into the trail -- no more explicit propagation queue in MiniSat).
+ int simpDB_assigns; // Number of top-level assignments since last execution of 'simplify()'.
+ int64_t simpDB_props; // Remaining number of propagations that must be made before next execution of 'simplify()'.
+ double progress_estimate;// Set by 'search()'.
+ bool remove_satisfied; // Indicates whether possibly inefficient linear scan for satisfied clauses should be performed in 'simplify'.
+ Var next_var; // Next variable to be created.
+ ClauseAllocator ca;
+
+ vec<Var> released_vars;
+ vec<Var> free_vars;
+
+ // Temporaries (to reduce allocation overhead). Each variable is prefixed by the method in which it is
+ // used, exept 'seen' wich is used in several places.
+ //
+ VMap<char> seen;
+ vec<ShrinkStackElem>analyze_stack;
+ vec<Lit> analyze_toclear;
+ vec<Lit> add_tmp;
+
+ double max_learnts;
+ double learntsize_adjust_confl;
+ int learntsize_adjust_cnt;
+
+ // Resource constraints:
+ //
+ int64_t conflict_budget; // -1 means no budget.
+ int64_t propagation_budget; // -1 means no budget.
+ bool asynch_interrupt;
+
+ // Main internal methods:
+ //
+ void insertVarOrder (Var x); // Insert a variable in the decision order priority queue.
+ Lit pickBranchLit (); // Return the next decision variable.
+ void newDecisionLevel (); // Begins a new decision level.
+ void uncheckedEnqueue (Lit p, CRef from = CRef_Undef); // Enqueue a literal. Assumes value of literal is undefined.
+ bool enqueue (Lit p, CRef from = CRef_Undef); // Test if fact 'p' contradicts current state, enqueue otherwise.
+ CRef propagate (); // Perform unit propagation. Returns possibly conflicting clause.
+ void cancelUntil (int level); // Backtrack until a certain level.
+ void analyze (CRef confl, vec<Lit>& out_learnt, int& out_btlevel); // (bt = backtrack)
+ void analyzeFinal (Lit p, LSet& out_conflict); // COULD THIS BE IMPLEMENTED BY THE ORDINARIY "analyze" BY SOME REASONABLE GENERALIZATION?
+ bool litRedundant (Lit p); // (helper method for 'analyze()')
+ lbool search (int nof_conflicts); // Search for a given number of conflicts.
+ lbool solve_ (); // Main solve method (assumptions given in 'assumptions').
+ void reduceDB (); // Reduce the set of learnt clauses.
+ void removeSatisfied (vec<CRef>& cs); // Shrink 'cs' to contain only non-satisfied clauses.
+ void rebuildOrderHeap ();
+
+ // Maintaining Variable/Clause activity:
+ //
+ void varDecayActivity (); // Decay all variables with the specified factor. Implemented by increasing the 'bump' value instead.
+ void varBumpActivity (Var v, double inc); // Increase a variable with the current 'bump' value.
+ void varBumpActivity (Var v); // Increase a variable with the current 'bump' value.
+ void claDecayActivity (); // Decay all clauses with the specified factor. Implemented by increasing the 'bump' value instead.
+ void claBumpActivity (Clause& c); // Increase a clause with the current 'bump' value.
+
+ // Operations on clauses:
+ //
+ void attachClause (CRef cr); // Attach a clause to watcher lists.
+ void detachClause (CRef cr, bool strict = false); // Detach a clause to watcher lists.
+ void removeClause (CRef cr); // Detach and free a clause.
+ bool isRemoved (CRef cr) const; // Test if a clause has been removed.
+ bool locked (const Clause& c) const; // Returns TRUE if a clause is a reason for some implication in the current state.
+ bool satisfied (const Clause& c) const; // Returns TRUE if a clause is satisfied in the current state.
+
+ // Misc:
+ //
+ int decisionLevel () const; // Gives the current decisionlevel.
+ uint32_t abstractLevel (Var x) const; // Used to represent an abstraction of sets of decision levels.
+ CRef reason (Var x) const;
+ int level (Var x) const;
+ double progressEstimate () const; // DELETE THIS ?? IT'S NOT VERY USEFUL ...
+ bool withinBudget () const;
+ void relocAll (ClauseAllocator& to);
+
+ // Static helpers:
+ //
+
+ // Returns a random float 0 <= x < 1. Seed must never be 0.
+ static inline double drand(double& seed) {
+ seed *= 1389796;
+ int q = (int)(seed / 2147483647);
+ seed -= (double)q * 2147483647;
+ return seed / 2147483647; }
+
+ // Returns a random integer 0 <= x < size. Seed must never be 0.
+ static inline int irand(double& seed, int size) {
+ return (int)(drand(seed) * size); }
+};
+
+
+//=================================================================================================
+// Implementation of inline methods:
+
+inline CRef Solver::reason(Var x) const { return vardata[x].reason; }
+inline int Solver::level (Var x) const { return vardata[x].level; }
+
+inline void Solver::insertVarOrder(Var x) {
+ if (!order_heap.inHeap(x) && decision[x]) order_heap.insert(x); }
+
+inline void Solver::varDecayActivity() { var_inc *= (1 / var_decay); }
+inline void Solver::varBumpActivity(Var v) { varBumpActivity(v, var_inc); }
+inline void Solver::varBumpActivity(Var v, double inc) {
+ if ( (activity[v] += inc) > 1e100 ) {
+ // Rescale:
+ for (int i = 0; i < nVars(); i++)
+ activity[i] *= 1e-100;
+ var_inc *= 1e-100; }
+
+ // Update order_heap with respect to new activity:
+ if (order_heap.inHeap(v))
+ order_heap.decrease(v); }
+
+inline void Solver::claDecayActivity() { cla_inc *= (1 / clause_decay); }
+inline void Solver::claBumpActivity (Clause& c) {
+ if ( (c.activity() += cla_inc) > 1e20 ) {
+ // Rescale:
+ for (int i = 0; i < learnts.size(); i++)
+ ca[learnts[i]].activity() *= 1e-20;
+ cla_inc *= 1e-20; } }
+
+inline void Solver::checkGarbage(void){ return checkGarbage(garbage_frac); }
+inline void Solver::checkGarbage(double gf){
+ if (ca.wasted() > ca.size() * gf)
+ garbageCollect(); }
+
+// NOTE: enqueue does not set the ok flag! (only public methods do)
+inline bool Solver::enqueue (Lit p, CRef from) { return value(p) != l_Undef ? value(p) != l_False : (uncheckedEnqueue(p, from), true); }
+inline bool Solver::addClause (const vec<Lit>& ps) { ps.copyTo(add_tmp); return addClause_(add_tmp); }
+inline bool Solver::addEmptyClause () { add_tmp.clear(); return addClause_(add_tmp); }
+inline bool Solver::addClause (Lit p) { add_tmp.clear(); add_tmp.push(p); return addClause_(add_tmp); }
+inline bool Solver::addClause (Lit p, Lit q) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); return addClause_(add_tmp); }
+inline bool Solver::addClause (Lit p, Lit q, Lit r) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); add_tmp.push(r); return addClause_(add_tmp); }
+inline bool Solver::addClause (Lit p, Lit q, Lit r, Lit s){ add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); add_tmp.push(r); add_tmp.push(s); return addClause_(add_tmp); }
+
+inline bool Solver::isRemoved (CRef cr) const { return ca[cr].mark() == 1; }
+inline bool Solver::locked (const Clause& c) const { return value(c[0]) == l_True && reason(var(c[0])) != CRef_Undef && ca.lea(reason(var(c[0]))) == &c; }
+inline void Solver::newDecisionLevel() { trail_lim.push(trail.size()); }
+
+inline int Solver::decisionLevel () const { return trail_lim.size(); }
+inline uint32_t Solver::abstractLevel (Var x) const { return 1 << (level(x) & 31); }
+inline lbool Solver::value (Var x) const { return assigns[x]; }
+inline lbool Solver::value (Lit p) const { return assigns[var(p)] ^ sign(p); }
+inline lbool Solver::modelValue (Var x) const { return model[x]; }
+inline lbool Solver::modelValue (Lit p) const { return model[var(p)] ^ sign(p); }
+inline int Solver::nAssigns () const { return trail.size(); }
+inline int Solver::nClauses () const { return num_clauses; }
+inline int Solver::nLearnts () const { return num_learnts; }
+inline int Solver::nVars () const { return next_var; }
+// TODO: nFreeVars() is not quite correct, try to calculate right instead of adapting it like below:
+inline int Solver::nFreeVars () const { return (int)dec_vars - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]); }
+inline void Solver::setPolarity (Var v, lbool b){ user_pol[v] = b; }
+inline void Solver::setDecisionVar(Var v, bool b)
+{
+ if ( b && !decision[v]) dec_vars++;
+ else if (!b && decision[v]) dec_vars--;
+
+ decision[v] = b;
+ insertVarOrder(v);
+}
+inline void Solver::setConfBudget(int64_t x){ conflict_budget = conflicts + x; }
+inline void Solver::setPropBudget(int64_t x){ propagation_budget = propagations + x; }
+inline void Solver::interrupt(){ asynch_interrupt = true; }
+inline void Solver::clearInterrupt(){ asynch_interrupt = false; }
+inline void Solver::budgetOff(){ conflict_budget = propagation_budget = -1; }
+inline bool Solver::withinBudget() const {
+ return !asynch_interrupt &&
+ (conflict_budget < 0 || conflicts < (uint64_t)conflict_budget) &&
+ (propagation_budget < 0 || propagations < (uint64_t)propagation_budget); }
+
+// FIXME: after the introduction of asynchronous interrruptions the solve-versions that return a
+// pure bool do not give a safe interface. Either interrupts must be possible to turn off here, or
+// all calls to solve must return an 'lbool'. I'm not yet sure which I prefer.
+inline bool Solver::solve () { budgetOff(); assumptions.clear(); return solve_() == l_True; }
+inline bool Solver::solve (Lit p) { budgetOff(); assumptions.clear(); assumptions.push(p); return solve_() == l_True; }
+inline bool Solver::solve (Lit p, Lit q) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); return solve_() == l_True; }
+inline bool Solver::solve (Lit p, Lit q, Lit r) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); assumptions.push(r); return solve_() == l_True; }
+inline bool Solver::solve (const vec<Lit>& assumps){ budgetOff(); assumps.copyTo(assumptions); return solve_() == l_True; }
+inline lbool Solver::solveLimited (const vec<Lit>& assumps){ assumps.copyTo(assumptions); return solve_(); }
+inline bool Solver::okay () const { return ok; }
+
+inline ClauseIterator Solver::clausesBegin() const { return ClauseIterator(ca, &clauses[0]); }
+inline ClauseIterator Solver::clausesEnd () const { return ClauseIterator(ca, &clauses[clauses.size()]); }
+inline TrailIterator Solver::trailBegin () const { return TrailIterator(&trail[0]); }
+inline TrailIterator Solver::trailEnd () const {
+ return TrailIterator(&trail[decisionLevel() == 0 ? trail.size() : trail_lim[0]]); }
+
+inline void Solver::toDimacs (const char* file){ vec<Lit> as; toDimacs(file, as); }
+inline void Solver::toDimacs (const char* file, Lit p){ vec<Lit> as; as.push(p); toDimacs(file, as); }
+inline void Solver::toDimacs (const char* file, Lit p, Lit q){ vec<Lit> as; as.push(p); as.push(q); toDimacs(file, as); }
+inline void Solver::toDimacs (const char* file, Lit p, Lit q, Lit r){ vec<Lit> as; as.push(p); as.push(q); as.push(r); toDimacs(file, as); }
+
+
+//=================================================================================================
+// Debug etc:
+
+
+//=================================================================================================
+}
+
+#endif
diff --git a/libs/minisat/SolverTypes.h b/libs/minisat/SolverTypes.h
new file mode 100644
index 00000000..a7df5785
--- /dev/null
+++ b/libs/minisat/SolverTypes.h
@@ -0,0 +1,478 @@
+/***********************************************************************************[SolverTypes.h]
+Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson
+Copyright (c) 2007-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+
+#ifndef Minisat_SolverTypes_h
+#define Minisat_SolverTypes_h
+
+#include <assert.h>
+
+#include "IntTypes.h"
+#include "Alg.h"
+#include "Vec.h"
+#include "IntMap.h"
+#include "Map.h"
+#include "Alloc.h"
+
+namespace Minisat {
+
+//=================================================================================================
+// Variables, literals, lifted booleans, clauses:
+
+
+// NOTE! Variables are just integers. No abstraction here. They should be chosen from 0..N,
+// so that they can be used as array indices.
+
+typedef int Var;
+#if defined(MINISAT_CONSTANTS_AS_MACROS)
+#define var_Undef (-1)
+#else
+ const Var var_Undef = -1;
+#endif
+
+
+struct Lit {
+ int x;
+
+ // Use this as a constructor:
+ friend Lit mkLit(Var var, bool sign);
+
+ bool operator == (Lit p) const { return x == p.x; }
+ bool operator != (Lit p) const { return x != p.x; }
+ bool operator < (Lit p) const { return x < p.x; } // '<' makes p, ~p adjacent in the ordering.
+};
+
+
+inline Lit mkLit (Var var, bool sign = false) { Lit p; p.x = var + var + (int)sign; return p; }
+inline Lit operator ~(Lit p) { Lit q; q.x = p.x ^ 1; return q; }
+inline Lit operator ^(Lit p, bool b) { Lit q; q.x = p.x ^ (unsigned int)b; return q; }
+inline bool sign (Lit p) { return p.x & 1; }
+inline int var (Lit p) { return p.x >> 1; }
+
+// Mapping Literals to and from compact integers suitable for array indexing:
+inline int toInt (Var v) { return v; }
+inline int toInt (Lit p) { return p.x; }
+inline Lit toLit (int i) { Lit p; p.x = i; return p; }
+
+//const Lit lit_Undef = mkLit(var_Undef, false); // }- Useful special constants.
+//const Lit lit_Error = mkLit(var_Undef, true ); // }
+
+const Lit lit_Undef = { -2 }; // }- Useful special constants.
+const Lit lit_Error = { -1 }; // }
+
+struct MkIndexLit { vec<Lit>::Size operator()(Lit l) const { return vec<Lit>::Size(l.x); } };
+
+template<class T> class VMap : public IntMap<Var, T>{};
+template<class T> class LMap : public IntMap<Lit, T, MkIndexLit>{};
+class LSet : public IntSet<Lit, MkIndexLit>{};
+
+//=================================================================================================
+// Lifted booleans:
+//
+// NOTE: this implementation is optimized for the case when comparisons between values are mostly
+// between one variable and one constant. Some care had to be taken to make sure that gcc
+// does enough constant propagation to produce sensible code, and this appears to be somewhat
+// fragile unfortunately.
+
+class lbool {
+ uint8_t value;
+
+public:
+ explicit lbool(uint8_t v) : value(v) { }
+
+ lbool() : value(0) { }
+ explicit lbool(bool x) : value(!x) { }
+
+ bool operator == (lbool b) const { return ((b.value&2) & (value&2)) | (!(b.value&2)&(value == b.value)); }
+ bool operator != (lbool b) const { return !(*this == b); }
+ lbool operator ^ (bool b) const { return lbool((uint8_t)(value^(uint8_t)b)); }
+
+ lbool operator && (lbool b) const {
+ uint8_t sel = (this->value << 1) | (b.value << 3);
+ uint8_t v = (0xF7F755F4 >> sel) & 3;
+ return lbool(v); }
+
+ lbool operator || (lbool b) const {
+ uint8_t sel = (this->value << 1) | (b.value << 3);
+ uint8_t v = (0xFCFCF400 >> sel) & 3;
+ return lbool(v); }
+
+ friend int toInt (lbool l);
+ friend lbool toLbool(int v);
+};
+inline int toInt (lbool l) { return l.value; }
+inline lbool toLbool(int v) { return lbool((uint8_t)v); }
+
+#if defined(MINISAT_CONSTANTS_AS_MACROS)
+ #define l_True (lbool((uint8_t)0)) // gcc does not do constant propagation if these are real constants.
+ #define l_False (lbool((uint8_t)1))
+ #define l_Undef (lbool((uint8_t)2))
+#else
+ const lbool l_True ((uint8_t)0);
+ const lbool l_False((uint8_t)1);
+ const lbool l_Undef((uint8_t)2);
+#endif
+
+
+//=================================================================================================
+// Clause -- a simple class for representing a clause:
+
+class Clause;
+typedef RegionAllocator<uint32_t>::Ref CRef;
+
+class Clause {
+ struct {
+ unsigned mark : 2;
+ unsigned learnt : 1;
+ unsigned has_extra : 1;
+ unsigned reloced : 1;
+ unsigned size : 27; } header;
+ union { Lit lit; float act; uint32_t abs; CRef rel; } data[0];
+
+ friend class ClauseAllocator;
+
+ // NOTE: This constructor cannot be used directly (doesn't allocate enough memory).
+ Clause(const vec<Lit>& ps, bool use_extra, bool learnt) {
+ header.mark = 0;
+ header.learnt = learnt;
+ header.has_extra = use_extra;
+ header.reloced = 0;
+ header.size = ps.size();
+
+ for (int i = 0; i < ps.size(); i++)
+ data[i].lit = ps[i];
+
+ if (header.has_extra){
+ if (header.learnt)
+ data[header.size].act = 0;
+ else
+ calcAbstraction();
+ }
+ }
+
+ // NOTE: This constructor cannot be used directly (doesn't allocate enough memory).
+ Clause(const Clause& from, bool use_extra){
+ header = from.header;
+ header.has_extra = use_extra; // NOTE: the copied clause may lose the extra field.
+
+ for (int i = 0; i < from.size(); i++)
+ data[i].lit = from[i];
+
+ if (header.has_extra){
+ if (header.learnt)
+ data[header.size].act = from.data[header.size].act;
+ else
+ data[header.size].abs = from.data[header.size].abs;
+ }
+ }
+
+public:
+ void calcAbstraction() {
+ assert(header.has_extra);
+ uint32_t abstraction = 0;
+ for (int i = 0; i < size(); i++)
+ abstraction |= 1 << (var(data[i].lit) & 31);
+ data[header.size].abs = abstraction; }
+
+
+ int size () const { return header.size; }
+ void shrink (int i) { assert(i <= size()); if (header.has_extra) data[header.size-i] = data[header.size]; header.size -= i; }
+ void pop () { shrink(1); }
+ bool learnt () const { return header.learnt; }
+ bool has_extra () const { return header.has_extra; }
+ uint32_t mark () const { return header.mark; }
+ void mark (uint32_t m) { header.mark = m; }
+ const Lit& last () const { return data[header.size-1].lit; }
+
+ bool reloced () const { return header.reloced; }
+ CRef relocation () const { return data[0].rel; }
+ void relocate (CRef c) { header.reloced = 1; data[0].rel = c; }
+
+ // NOTE: somewhat unsafe to change the clause in-place! Must manually call 'calcAbstraction' afterwards for
+ // subsumption operations to behave correctly.
+ Lit& operator [] (int i) { return data[i].lit; }
+ Lit operator [] (int i) const { return data[i].lit; }
+ operator const Lit* (void) const { return (Lit*)data; }
+
+ float& activity () { assert(header.has_extra); return data[header.size].act; }
+ uint32_t abstraction () const { assert(header.has_extra); return data[header.size].abs; }
+
+ Lit subsumes (const Clause& other) const;
+ void strengthen (Lit p);
+};
+
+
+//=================================================================================================
+// ClauseAllocator -- a simple class for allocating memory for clauses:
+
+const CRef CRef_Undef = RegionAllocator<uint32_t>::Ref_Undef;
+class ClauseAllocator
+{
+ RegionAllocator<uint32_t> ra;
+
+ static uint32_t clauseWord32Size(int size, bool has_extra){
+ return (sizeof(Clause) + (sizeof(Lit) * (size + (int)has_extra))) / sizeof(uint32_t); }
+
+ public:
+ enum { Unit_Size = RegionAllocator<uint32_t>::Unit_Size };
+
+ bool extra_clause_field;
+
+ ClauseAllocator(uint32_t start_cap) : ra(start_cap), extra_clause_field(false){}
+ ClauseAllocator() : extra_clause_field(false){}
+
+ void moveTo(ClauseAllocator& to){
+ to.extra_clause_field = extra_clause_field;
+ ra.moveTo(to.ra); }
+
+ CRef alloc(const vec<Lit>& ps, bool learnt = false)
+ {
+ assert(sizeof(Lit) == sizeof(uint32_t));
+ assert(sizeof(float) == sizeof(uint32_t));
+ bool use_extra = learnt | extra_clause_field;
+ CRef cid = ra.alloc(clauseWord32Size(ps.size(), use_extra));
+ new (lea(cid)) Clause(ps, use_extra, learnt);
+
+ return cid;
+ }
+
+ CRef alloc(const Clause& from)
+ {
+ bool use_extra = from.learnt() | extra_clause_field;
+ CRef cid = ra.alloc(clauseWord32Size(from.size(), use_extra));
+ new (lea(cid)) Clause(from, use_extra);
+ return cid; }
+
+ uint32_t size () const { return ra.size(); }
+ uint32_t wasted () const { return ra.wasted(); }
+
+ // Deref, Load Effective Address (LEA), Inverse of LEA (AEL):
+ Clause& operator[](CRef r) { return (Clause&)ra[r]; }
+ const Clause& operator[](CRef r) const { return (Clause&)ra[r]; }
+ Clause* lea (CRef r) { return (Clause*)ra.lea(r); }
+ const Clause* lea (CRef r) const { return (Clause*)ra.lea(r);; }
+ CRef ael (const Clause* t){ return ra.ael((uint32_t*)t); }
+
+ void free(CRef cid)
+ {
+ Clause& c = operator[](cid);
+ ra.free(clauseWord32Size(c.size(), c.has_extra()));
+ }
+
+ void reloc(CRef& cr, ClauseAllocator& to)
+ {
+ Clause& c = operator[](cr);
+
+ if (c.reloced()) { cr = c.relocation(); return; }
+
+ cr = to.alloc(c);
+ c.relocate(cr);
+ }
+};
+
+//=================================================================================================
+// Simple iterator classes (for iterating over clauses and top-level assignments):
+
+class ClauseIterator {
+ const ClauseAllocator& ca;
+ const CRef* crefs;
+public:
+ ClauseIterator(const ClauseAllocator& _ca, const CRef* _crefs) : ca(_ca), crefs(_crefs){}
+
+ void operator++(){ crefs++; }
+ const Clause& operator*() const { return ca[*crefs]; }
+
+ // NOTE: does not compare that references use the same clause-allocator:
+ bool operator==(const ClauseIterator& ci) const { return crefs == ci.crefs; }
+ bool operator!=(const ClauseIterator& ci) const { return crefs != ci.crefs; }
+};
+
+
+class TrailIterator {
+ const Lit* lits;
+public:
+ TrailIterator(const Lit* _lits) : lits(_lits){}
+
+ void operator++() { lits++; }
+ Lit operator*() const { return *lits; }
+
+ bool operator==(const TrailIterator& ti) const { return lits == ti.lits; }
+ bool operator!=(const TrailIterator& ti) const { return lits != ti.lits; }
+};
+
+
+//=================================================================================================
+// OccLists -- a class for maintaining occurence lists with lazy deletion:
+
+template<class K, class Vec, class Deleted, class MkIndex = MkIndexDefault<K> >
+class OccLists
+{
+ IntMap<K, Vec, MkIndex> occs;
+ IntMap<K, char, MkIndex> dirty;
+ vec<K> dirties;
+ Deleted deleted;
+
+ public:
+ OccLists(const Deleted& d, MkIndex _index = MkIndex()) :
+ occs(_index),
+ dirty(_index),
+ deleted(d){}
+
+ void init (const K& idx){ occs.reserve(idx); occs[idx].clear(); dirty.reserve(idx, 0); }
+ Vec& operator[](const K& idx){ return occs[idx]; }
+ Vec& lookup (const K& idx){ if (dirty[idx]) clean(idx); return occs[idx]; }
+
+ void cleanAll ();
+ void clean (const K& idx);
+ void smudge (const K& idx){
+ if (dirty[idx] == 0){
+ dirty[idx] = 1;
+ dirties.push(idx);
+ }
+ }
+
+ void clear(bool free = true){
+ occs .clear(free);
+ dirty .clear(free);
+ dirties.clear(free);
+ }
+};
+
+
+template<class K, class Vec, class Deleted, class MkIndex>
+void OccLists<K,Vec,Deleted,MkIndex>::cleanAll()
+{
+ for (int i = 0; i < dirties.size(); i++)
+ // Dirties may contain duplicates so check here if a variable is already cleaned:
+ if (dirty[dirties[i]])
+ clean(dirties[i]);
+ dirties.clear();
+}
+
+
+template<class K, class Vec, class Deleted, class MkIndex>
+void OccLists<K,Vec,Deleted,MkIndex>::clean(const K& idx)
+{
+ Vec& vec = occs[idx];
+ int i, j;
+ for (i = j = 0; i < vec.size(); i++)
+ if (!deleted(vec[i]))
+ vec[j++] = vec[i];
+ vec.shrink(i - j);
+ dirty[idx] = 0;
+}
+
+
+//=================================================================================================
+// CMap -- a class for mapping clauses to values:
+
+
+template<class T>
+class CMap
+{
+ struct CRefHash {
+ uint32_t operator()(CRef cr) const { return (uint32_t)cr; } };
+
+ typedef Map<CRef, T, CRefHash> HashTable;
+ HashTable map;
+
+ public:
+ // Size-operations:
+ void clear () { map.clear(); }
+ int size () const { return map.elems(); }
+
+
+ // Insert/Remove/Test mapping:
+ void insert (CRef cr, const T& t){ map.insert(cr, t); }
+ void growTo (CRef cr, const T& t){ map.insert(cr, t); } // NOTE: for compatibility
+ void remove (CRef cr) { map.remove(cr); }
+ bool has (CRef cr, T& t) { return map.peek(cr, t); }
+
+ // Vector interface (the clause 'c' must already exist):
+ const T& operator [] (CRef cr) const { return map[cr]; }
+ T& operator [] (CRef cr) { return map[cr]; }
+
+ // Iteration (not transparent at all at the moment):
+ int bucket_count() const { return map.bucket_count(); }
+ const vec<typename HashTable::Pair>& bucket(int i) const { return map.bucket(i); }
+
+ // Move contents to other map:
+ void moveTo(CMap& other){ map.moveTo(other.map); }
+
+ // TMP debug:
+ void debug(){
+ printf(" --- size = %d, bucket_count = %d\n", size(), map.bucket_count()); }
+};
+
+
+/*_________________________________________________________________________________________________
+|
+| subsumes : (other : const Clause&) -> Lit
+|
+| Description:
+| Checks if clause subsumes 'other', and at the same time, if it can be used to simplify 'other'
+| by subsumption resolution.
+|
+| Result:
+| lit_Error - No subsumption or simplification
+| lit_Undef - Clause subsumes 'other'
+| p - The literal p can be deleted from 'other'
+|________________________________________________________________________________________________@*/
+inline Lit Clause::subsumes(const Clause& other) const
+{
+ //if (other.size() < size() || (extra.abst & ~other.extra.abst) != 0)
+ //if (other.size() < size() || (!learnt() && !other.learnt() && (extra.abst & ~other.extra.abst) != 0))
+ assert(!header.learnt); assert(!other.header.learnt);
+ assert(header.has_extra); assert(other.header.has_extra);
+ if (other.header.size < header.size || (data[header.size].abs & ~other.data[other.header.size].abs) != 0)
+ return lit_Error;
+
+ Lit ret = lit_Undef;
+ const Lit* c = (const Lit*)(*this);
+ const Lit* d = (const Lit*)other;
+
+ for (unsigned i = 0; i < header.size; i++) {
+ // search for c[i] or ~c[i]
+ for (unsigned j = 0; j < other.header.size; j++)
+ if (c[i] == d[j])
+ goto ok;
+ else if (ret == lit_Undef && c[i] == ~d[j]){
+ ret = c[i];
+ goto ok;
+ }
+
+ // did not find it
+ return lit_Error;
+ ok:;
+ }
+
+ return ret;
+}
+
+inline void Clause::strengthen(Lit p)
+{
+ remove(*this, p);
+ calcAbstraction();
+}
+
+//=================================================================================================
+}
+
+#endif
diff --git a/libs/minisat/Sort.h b/libs/minisat/Sort.h
new file mode 100644
index 00000000..cc96486d
--- /dev/null
+++ b/libs/minisat/Sort.h
@@ -0,0 +1,98 @@
+/******************************************************************************************[Sort.h]
+Copyright (c) 2003-2007, Niklas Een, Niklas Sorensson
+Copyright (c) 2007-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+#ifndef Minisat_Sort_h
+#define Minisat_Sort_h
+
+#include "Vec.h"
+
+//=================================================================================================
+// Some sorting algorithms for vec's
+
+
+namespace Minisat {
+
+template<class T>
+struct LessThan_default {
+ bool operator () (T x, T y) { return x < y; }
+};
+
+
+template <class T, class LessThan>
+void selectionSort(T* array, int size, LessThan lt)
+{
+ int i, j, best_i;
+ T tmp;
+
+ for (i = 0; i < size-1; i++){
+ best_i = i;
+ for (j = i+1; j < size; j++){
+ if (lt(array[j], array[best_i]))
+ best_i = j;
+ }
+ tmp = array[i]; array[i] = array[best_i]; array[best_i] = tmp;
+ }
+}
+template <class T> static inline void selectionSort(T* array, int size) {
+ selectionSort(array, size, LessThan_default<T>()); }
+
+template <class T, class LessThan>
+void sort(T* array, int size, LessThan lt)
+{
+ if (size <= 15)
+ selectionSort(array, size, lt);
+
+ else{
+ T pivot = array[size / 2];
+ T tmp;
+ int i = -1;
+ int j = size;
+
+ for(;;){
+ do i++; while(lt(array[i], pivot));
+ do j--; while(lt(pivot, array[j]));
+
+ if (i >= j) break;
+
+ tmp = array[i]; array[i] = array[j]; array[j] = tmp;
+ }
+
+ sort(array , i , lt);
+ sort(&array[i], size-i, lt);
+ }
+}
+template <class T> static inline void sort(T* array, int size) {
+ sort(array, size, LessThan_default<T>()); }
+
+
+//=================================================================================================
+// For 'vec's:
+
+
+template <class T, class LessThan> void sort(vec<T>& v, LessThan lt) {
+ sort((T*)v, v.size(), lt); }
+template <class T> void sort(vec<T>& v) {
+ sort(v, LessThan_default<T>()); }
+
+
+//=================================================================================================
+}
+
+#endif
diff --git a/libs/minisat/System.cc b/libs/minisat/System.cc
new file mode 100644
index 00000000..febe3b40
--- /dev/null
+++ b/libs/minisat/System.cc
@@ -0,0 +1,171 @@
+#define __STDC_FORMAT_MACROS
+#define __STDC_LIMIT_MACROS
+/***************************************************************************************[System.cc]
+Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson
+Copyright (c) 2007-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+#include <signal.h>
+#include <stdio.h>
+
+#include "System.h"
+
+#if defined(__linux__)
+
+#include <stdlib.h>
+
+using namespace Minisat;
+
+static inline int memReadStat(int field)
+{
+ char name[256];
+ pid_t pid = getpid();
+ int value;
+
+ sprintf(name, "/proc/%d/statm", pid);
+ FILE* in = fopen(name, "rb");
+ if (in == NULL) return 0;
+
+ for (; field >= 0; field--)
+ if (fscanf(in, "%d", &value) != 1)
+ printf("ERROR! Failed to parse memory statistics from \"/proc\".\n"), exit(1);
+ fclose(in);
+ return value;
+}
+
+
+static inline int memReadPeak(void)
+{
+ char name[256];
+ pid_t pid = getpid();
+
+ sprintf(name, "/proc/%d/status", pid);
+ FILE* in = fopen(name, "rb");
+ if (in == NULL) return 0;
+
+ // Find the correct line, beginning with "VmPeak:":
+ int peak_kb = 0;
+ while (!feof(in) && fscanf(in, "VmPeak: %d kB", &peak_kb) != 1)
+ while (!feof(in) && fgetc(in) != '\n')
+ ;
+ fclose(in);
+
+ return peak_kb;
+}
+
+double Minisat::memUsed() { return (double)memReadStat(0) * (double)getpagesize() / (1024*1024); }
+double Minisat::memUsedPeak(bool strictlyPeak) {
+ double peak = memReadPeak() / (double)1024;
+ return peak == 0 && !strictlyPeak ? memUsed() : peak; }
+
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__gnu_hurd__)
+
+double Minisat::memUsed() {
+ struct rusage ru;
+ getrusage(RUSAGE_SELF, &ru);
+ return (double)ru.ru_maxrss / 1024; }
+double Minisat::memUsedPeak(bool) { return memUsed(); }
+
+
+#elif defined(__APPLE__)
+#include <malloc/malloc.h>
+
+double Minisat::memUsed() {
+ malloc_statistics_t t;
+ malloc_zone_statistics(NULL, &t);
+ return (double)t.max_size_in_use / (1024*1024); }
+double Minisat::memUsedPeak(bool) { return memUsed(); }
+
+#else
+double Minisat::memUsed() { return 0; }
+double Minisat::memUsedPeak(bool) { return 0; }
+#endif
+
+
+void Minisat::setX86FPUPrecision()
+{
+#if defined(__linux__) && defined(_FPU_EXTENDED) && defined(_FPU_DOUBLE) && defined(_FPU_GETCW)
+ // Only correct FPU precision on Linux architectures that needs and supports it:
+ fpu_control_t oldcw, newcw;
+ _FPU_GETCW(oldcw); newcw = (oldcw & ~_FPU_EXTENDED) | _FPU_DOUBLE; _FPU_SETCW(newcw);
+ printf("WARNING: for repeatability, setting FPU to use double precision\n");
+#endif
+}
+
+
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+void Minisat::limitMemory(uint64_t max_mem_mb)
+{
+// FIXME: OpenBSD does not support RLIMIT_AS. Not sure how well RLIMIT_DATA works instead.
+#if defined(__OpenBSD__)
+#define RLIMIT_AS RLIMIT_DATA
+#endif
+
+ // Set limit on virtual memory:
+ if (max_mem_mb != 0){
+ rlim_t new_mem_lim = (rlim_t)max_mem_mb * 1024*1024;
+ rlimit rl;
+ getrlimit(RLIMIT_AS, &rl);
+ if (rl.rlim_max == RLIM_INFINITY || new_mem_lim < rl.rlim_max){
+ rl.rlim_cur = new_mem_lim;
+ if (setrlimit(RLIMIT_AS, &rl) == -1)
+ printf("WARNING! Could not set resource limit: Virtual memory.\n");
+ }
+ }
+
+#if defined(__OpenBSD__)
+#undef RLIMIT_AS
+#endif
+}
+#else
+void Minisat::limitMemory(uint64_t /*max_mem_mb*/)
+{
+ printf("WARNING! Memory limit not supported on this architecture.\n");
+}
+#endif
+
+
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+void Minisat::limitTime(uint32_t max_cpu_time)
+{
+ if (max_cpu_time != 0){
+ rlimit rl;
+ getrlimit(RLIMIT_CPU, &rl);
+ if (rl.rlim_max == RLIM_INFINITY || (rlim_t)max_cpu_time < rl.rlim_max){
+ rl.rlim_cur = max_cpu_time;
+ if (setrlimit(RLIMIT_CPU, &rl) == -1)
+ printf("WARNING! Could not set resource limit: CPU-time.\n");
+ }
+ }
+}
+#else
+void Minisat::limitTime(uint32_t /*max_cpu_time*/)
+{
+ printf("WARNING! CPU-time limit not supported on this architecture.\n");
+}
+#endif
+
+
+void Minisat::sigTerm(void handler(int))
+{
+ signal(SIGINT, handler);
+ signal(SIGTERM,handler);
+#ifdef SIGXCPU
+ signal(SIGXCPU,handler);
+#endif
+}
diff --git a/libs/minisat/System.h b/libs/minisat/System.h
new file mode 100644
index 00000000..ee92a6e0
--- /dev/null
+++ b/libs/minisat/System.h
@@ -0,0 +1,72 @@
+/****************************************************************************************[System.h]
+Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson
+Copyright (c) 2007-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+#ifndef Minisat_System_h
+#define Minisat_System_h
+
+#if defined(__linux__)
+#include <fpu_control.h>
+#endif
+
+#include "IntTypes.h"
+
+//-------------------------------------------------------------------------------------------------
+
+namespace Minisat {
+
+static inline double cpuTime(void); // CPU-time in seconds.
+
+extern double memUsed(); // Memory in mega bytes (returns 0 for unsupported architectures).
+extern double memUsedPeak(bool strictlyPeak = false); // Peak-memory in mega bytes (returns 0 for unsupported architectures).
+
+extern void setX86FPUPrecision(); // Make sure double's are represented with the same precision
+ // in memory and registers.
+
+extern void limitMemory(uint64_t max_mem_mb); // Set a limit on total memory usage. The exact
+ // semantics varies depending on architecture.
+
+extern void limitTime(uint32_t max_cpu_time); // Set a limit on maximum CPU time. The exact
+ // semantics varies depending on architecture.
+
+extern void sigTerm(void handler(int)); // Set up handling of available termination signals.
+
+}
+
+//-------------------------------------------------------------------------------------------------
+// Implementation of inline functions:
+
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#include <time.h>
+
+static inline double Minisat::cpuTime(void) { return (double)clock() / CLOCKS_PER_SEC; }
+
+#else
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+static inline double Minisat::cpuTime(void) {
+ struct rusage ru;
+ getrusage(RUSAGE_SELF, &ru);
+ return (double)ru.ru_utime.tv_sec + (double)ru.ru_utime.tv_usec / 1000000; }
+
+#endif
+
+#endif
diff --git a/libs/minisat/Vec.h b/libs/minisat/Vec.h
new file mode 100644
index 00000000..6e398801
--- /dev/null
+++ b/libs/minisat/Vec.h
@@ -0,0 +1,134 @@
+/*******************************************************************************************[Vec.h]
+Copyright (c) 2003-2007, Niklas Een, Niklas Sorensson
+Copyright (c) 2007-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+#ifndef Minisat_Vec_h
+#define Minisat_Vec_h
+
+#include <assert.h>
+#include <limits>
+#include <new>
+
+#include "IntTypes.h"
+#include "XAlloc.h"
+
+namespace Minisat {
+
+//=================================================================================================
+// Automatically resizable arrays
+//
+// NOTE! Don't use this vector on datatypes that cannot be re-located in memory (with realloc)
+
+template<class T, class _Size = int>
+class vec {
+public:
+ typedef _Size Size;
+private:
+ T* data;
+ Size sz;
+ Size cap;
+
+ // Don't allow copying (error prone):
+ vec<T>& operator=(vec<T>& other);
+ vec (vec<T>& other);
+
+ static inline Size max(Size x, Size y){ return (x > y) ? x : y; }
+
+public:
+ // Constructors:
+ vec() : data(NULL), sz(0), cap(0) { }
+ explicit vec(Size size) : data(NULL), sz(0), cap(0) { growTo(size); }
+ vec(Size size, const T& pad) : data(NULL), sz(0), cap(0) { growTo(size, pad); }
+ ~vec() { clear(true); }
+
+ // Pointer to first element:
+ operator T* (void) { return data; }
+
+ // Size operations:
+ Size size (void) const { return sz; }
+ void shrink (Size nelems) { assert(nelems <= sz); for (Size i = 0; i < nelems; i++) sz--, data[sz].~T(); }
+ void shrink_ (Size nelems) { assert(nelems <= sz); sz -= nelems; }
+ int capacity (void) const { return cap; }
+ void capacity (Size min_cap);
+ void growTo (Size size);
+ void growTo (Size size, const T& pad);
+ void clear (bool dealloc = false);
+
+ // Stack interface:
+ void push (void) { if (sz == cap) capacity(sz+1); new (&data[sz]) T(); sz++; }
+ //void push (const T& elem) { if (sz == cap) capacity(sz+1); data[sz++] = elem; }
+ void push (const T& elem) { if (sz == cap) capacity(sz+1); new (&data[sz++]) T(elem); }
+ void push_ (const T& elem) { assert(sz < cap); data[sz++] = elem; }
+ void pop (void) { assert(sz > 0); sz--, data[sz].~T(); }
+ // NOTE: it seems possible that overflow can happen in the 'sz+1' expression of 'push()', but
+ // in fact it can not since it requires that 'cap' is equal to INT_MAX. This in turn can not
+ // happen given the way capacities are calculated (below). Essentially, all capacities are
+ // even, but INT_MAX is odd.
+
+ const T& last (void) const { return data[sz-1]; }
+ T& last (void) { return data[sz-1]; }
+
+ // Vector interface:
+ const T& operator [] (Size index) const { return data[index]; }
+ T& operator [] (Size index) { return data[index]; }
+
+ // Duplicatation (preferred instead):
+ void copyTo(vec<T>& copy) const { copy.clear(); copy.growTo(sz); for (Size i = 0; i < sz; i++) copy[i] = data[i]; }
+ void moveTo(vec<T>& dest) { dest.clear(true); dest.data = data; dest.sz = sz; dest.cap = cap; data = NULL; sz = 0; cap = 0; }
+};
+
+
+template<class T, class _Size>
+void vec<T,_Size>::capacity(Size min_cap) {
+ if (cap >= min_cap) return;
+ Size add = max((min_cap - cap + 1) & ~1, ((cap >> 1) + 2) & ~1); // NOTE: grow by approximately 3/2
+ const Size size_max = std::numeric_limits<Size>::max();
+ if ( ((size_max <= std::numeric_limits<int>::max()) && (add > size_max - cap))
+ || (((data = (T*)::realloc(data, (cap += add) * sizeof(T))) == NULL) && errno == ENOMEM) )
+ throw OutOfMemoryException();
+ }
+
+
+template<class T, class _Size>
+void vec<T,_Size>::growTo(Size size, const T& pad) {
+ if (sz >= size) return;
+ capacity(size);
+ for (Size i = sz; i < size; i++) data[i] = pad;
+ sz = size; }
+
+
+template<class T, class _Size>
+void vec<T,_Size>::growTo(Size size) {
+ if (sz >= size) return;
+ capacity(size);
+ for (Size i = sz; i < size; i++) new (&data[i]) T();
+ sz = size; }
+
+
+template<class T, class _Size>
+void vec<T,_Size>::clear(bool dealloc) {
+ if (data != NULL){
+ for (Size i = 0; i < sz; i++) data[i].~T();
+ sz = 0;
+ if (dealloc) free(data), data = NULL, cap = 0; } }
+
+//=================================================================================================
+}
+
+#endif
diff --git a/libs/minisat/XAlloc.h b/libs/minisat/XAlloc.h
new file mode 100644
index 00000000..1da17602
--- /dev/null
+++ b/libs/minisat/XAlloc.h
@@ -0,0 +1,45 @@
+/****************************************************************************************[XAlloc.h]
+Copyright (c) 2009-2010, Niklas Sorensson
+
+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.
+**************************************************************************************************/
+
+
+#ifndef Minisat_XAlloc_h
+#define Minisat_XAlloc_h
+
+#include <errno.h>
+#include <stdlib.h>
+
+namespace Minisat {
+
+//=================================================================================================
+// Simple layer on top of malloc/realloc to catch out-of-memory situtaions and provide some typing:
+
+class OutOfMemoryException{};
+static inline void* xrealloc(void *ptr, size_t size)
+{
+ void* mem = realloc(ptr, size);
+ if (mem == NULL && errno == ENOMEM){
+ throw OutOfMemoryException();
+ }else
+ return mem;
+}
+
+//=================================================================================================
+}
+
+#endif
diff --git a/libs/sha1/sha1.cpp b/libs/sha1/sha1.cpp
new file mode 100644
index 00000000..51bbd85c
--- /dev/null
+++ b/libs/sha1/sha1.cpp
@@ -0,0 +1,275 @@
+/*
+ sha1.cpp - source code of
+
+ ============
+ SHA-1 in C++
+ ============
+
+ 100% Public Domain.
+
+ Original C Code
+ -- Steve Reid <steve@edmweb.com>
+ Small changes to fit into bglibs
+ -- Bruce Guenter <bruce@untroubled.org>
+ Translation to simpler C++ Code
+ -- Volker Grabsch <vog@notjusthosting.com>
+ Fixing bugs and improving style
+ -- Eugene Hopkinson <slowriot at voxelstorm dot com>
+*/
+
+#include "sha1.h"
+#include <sstream>
+#include <iomanip>
+#include <fstream>
+
+/* Help macros */
+#define SHA1_ROL(value, bits) (((value) << (bits)) | (((value) & 0xffffffff) >> (32 - (bits))))
+#define SHA1_BLK(i) (block[i&15] = SHA1_ROL(block[(i+13)&15] ^ block[(i+8)&15] ^ block[(i+2)&15] ^ block[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define SHA1_R0(v,w,x,y,z,i) z += ((w&(x^y))^y) + block[i] + 0x5a827999 + SHA1_ROL(v,5); w=SHA1_ROL(w,30);
+#define SHA1_R1(v,w,x,y,z,i) z += ((w&(x^y))^y) + SHA1_BLK(i) + 0x5a827999 + SHA1_ROL(v,5); w=SHA1_ROL(w,30);
+#define SHA1_R2(v,w,x,y,z,i) z += (w^x^y) + SHA1_BLK(i) + 0x6ed9eba1 + SHA1_ROL(v,5); w=SHA1_ROL(w,30);
+#define SHA1_R3(v,w,x,y,z,i) z += (((w|x)&y)|(w&x)) + SHA1_BLK(i) + 0x8f1bbcdc + SHA1_ROL(v,5); w=SHA1_ROL(w,30);
+#define SHA1_R4(v,w,x,y,z,i) z += (w^x^y) + SHA1_BLK(i) + 0xca62c1d6 + SHA1_ROL(v,5); w=SHA1_ROL(w,30);
+
+SHA1::SHA1()
+{
+ reset();
+}
+
+
+void SHA1::update(const std::string &s)
+{
+ std::istringstream is(s);
+ update(is);
+}
+
+
+void SHA1::update(std::istream &is)
+{
+ std::string rest_of_buffer;
+ read(is, rest_of_buffer, BLOCK_BYTES - buffer.size());
+ buffer += rest_of_buffer;
+
+ while (is)
+ {
+ uint32_t block[BLOCK_INTS];
+ buffer_to_block(buffer, block);
+ transform(block);
+ read(is, buffer, BLOCK_BYTES);
+ }
+}
+
+
+/*
+ * Add padding and return the message digest.
+ */
+
+std::string SHA1::final()
+{
+ /* Total number of hashed bits */
+ uint64_t total_bits = (transforms*BLOCK_BYTES + buffer.size()) * 8;
+
+ /* Padding */
+ buffer += 0x80;
+ unsigned int orig_size = buffer.size();
+ while (buffer.size() < BLOCK_BYTES)
+ {
+ buffer += (char)0x00;
+ }
+
+ uint32_t block[BLOCK_INTS];
+ buffer_to_block(buffer, block);
+
+ if (orig_size > BLOCK_BYTES - 8)
+ {
+ transform(block);
+ for (unsigned int i = 0; i < BLOCK_INTS - 2; i++)
+ {
+ block[i] = 0;
+ }
+ }
+
+ /* Append total_bits, split this uint64_t into two uint32_t */
+ block[BLOCK_INTS - 1] = total_bits;
+ block[BLOCK_INTS - 2] = (total_bits >> 32);
+ transform(block);
+
+ /* Hex std::string */
+ std::ostringstream result;
+ for (unsigned int i = 0; i < DIGEST_INTS; i++)
+ {
+ result << std::hex << std::setfill('0') << std::setw(8);
+ result << (digest[i] & 0xffffffff);
+ }
+
+ /* Reset for next run */
+ reset();
+
+ return result.str();
+}
+
+
+std::string SHA1::from_file(const std::string &filename)
+{
+ std::ifstream stream(filename.c_str(), std::ios::binary);
+ SHA1 checksum;
+ checksum.update(stream);
+ return checksum.final();
+}
+
+
+void SHA1::reset()
+{
+ /* SHA1 initialization constants */
+ digest[0] = 0x67452301;
+ digest[1] = 0xefcdab89;
+ digest[2] = 0x98badcfe;
+ digest[3] = 0x10325476;
+ digest[4] = 0xc3d2e1f0;
+
+ /* Reset counters */
+ transforms = 0;
+ buffer = "";
+}
+
+
+/*
+ * Hash a single 512-bit block. This is the core of the algorithm.
+ */
+
+void SHA1::transform(uint32_t block[BLOCK_BYTES])
+{
+ /* Copy digest[] to working vars */
+ uint32_t a = digest[0];
+ uint32_t b = digest[1];
+ uint32_t c = digest[2];
+ uint32_t d = digest[3];
+ uint32_t e = digest[4];
+
+
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ SHA1_R0(a,b,c,d,e, 0);
+ SHA1_R0(e,a,b,c,d, 1);
+ SHA1_R0(d,e,a,b,c, 2);
+ SHA1_R0(c,d,e,a,b, 3);
+ SHA1_R0(b,c,d,e,a, 4);
+ SHA1_R0(a,b,c,d,e, 5);
+ SHA1_R0(e,a,b,c,d, 6);
+ SHA1_R0(d,e,a,b,c, 7);
+ SHA1_R0(c,d,e,a,b, 8);
+ SHA1_R0(b,c,d,e,a, 9);
+ SHA1_R0(a,b,c,d,e,10);
+ SHA1_R0(e,a,b,c,d,11);
+ SHA1_R0(d,e,a,b,c,12);
+ SHA1_R0(c,d,e,a,b,13);
+ SHA1_R0(b,c,d,e,a,14);
+ SHA1_R0(a,b,c,d,e,15);
+ SHA1_R1(e,a,b,c,d,16);
+ SHA1_R1(d,e,a,b,c,17);
+ SHA1_R1(c,d,e,a,b,18);
+ SHA1_R1(b,c,d,e,a,19);
+ SHA1_R2(a,b,c,d,e,20);
+ SHA1_R2(e,a,b,c,d,21);
+ SHA1_R2(d,e,a,b,c,22);
+ SHA1_R2(c,d,e,a,b,23);
+ SHA1_R2(b,c,d,e,a,24);
+ SHA1_R2(a,b,c,d,e,25);
+ SHA1_R2(e,a,b,c,d,26);
+ SHA1_R2(d,e,a,b,c,27);
+ SHA1_R2(c,d,e,a,b,28);
+ SHA1_R2(b,c,d,e,a,29);
+ SHA1_R2(a,b,c,d,e,30);
+ SHA1_R2(e,a,b,c,d,31);
+ SHA1_R2(d,e,a,b,c,32);
+ SHA1_R2(c,d,e,a,b,33);
+ SHA1_R2(b,c,d,e,a,34);
+ SHA1_R2(a,b,c,d,e,35);
+ SHA1_R2(e,a,b,c,d,36);
+ SHA1_R2(d,e,a,b,c,37);
+ SHA1_R2(c,d,e,a,b,38);
+ SHA1_R2(b,c,d,e,a,39);
+ SHA1_R3(a,b,c,d,e,40);
+ SHA1_R3(e,a,b,c,d,41);
+ SHA1_R3(d,e,a,b,c,42);
+ SHA1_R3(c,d,e,a,b,43);
+ SHA1_R3(b,c,d,e,a,44);
+ SHA1_R3(a,b,c,d,e,45);
+ SHA1_R3(e,a,b,c,d,46);
+ SHA1_R3(d,e,a,b,c,47);
+ SHA1_R3(c,d,e,a,b,48);
+ SHA1_R3(b,c,d,e,a,49);
+ SHA1_R3(a,b,c,d,e,50);
+ SHA1_R3(e,a,b,c,d,51);
+ SHA1_R3(d,e,a,b,c,52);
+ SHA1_R3(c,d,e,a,b,53);
+ SHA1_R3(b,c,d,e,a,54);
+ SHA1_R3(a,b,c,d,e,55);
+ SHA1_R3(e,a,b,c,d,56);
+ SHA1_R3(d,e,a,b,c,57);
+ SHA1_R3(c,d,e,a,b,58);
+ SHA1_R3(b,c,d,e,a,59);
+ SHA1_R4(a,b,c,d,e,60);
+ SHA1_R4(e,a,b,c,d,61);
+ SHA1_R4(d,e,a,b,c,62);
+ SHA1_R4(c,d,e,a,b,63);
+ SHA1_R4(b,c,d,e,a,64);
+ SHA1_R4(a,b,c,d,e,65);
+ SHA1_R4(e,a,b,c,d,66);
+ SHA1_R4(d,e,a,b,c,67);
+ SHA1_R4(c,d,e,a,b,68);
+ SHA1_R4(b,c,d,e,a,69);
+ SHA1_R4(a,b,c,d,e,70);
+ SHA1_R4(e,a,b,c,d,71);
+ SHA1_R4(d,e,a,b,c,72);
+ SHA1_R4(c,d,e,a,b,73);
+ SHA1_R4(b,c,d,e,a,74);
+ SHA1_R4(a,b,c,d,e,75);
+ SHA1_R4(e,a,b,c,d,76);
+ SHA1_R4(d,e,a,b,c,77);
+ SHA1_R4(c,d,e,a,b,78);
+ SHA1_R4(b,c,d,e,a,79);
+
+ /* Add the working vars back into digest[] */
+ digest[0] += a;
+ digest[1] += b;
+ digest[2] += c;
+ digest[3] += d;
+ digest[4] += e;
+
+ /* Count the number of transformations */
+ transforms++;
+}
+
+
+void SHA1::buffer_to_block(const std::string &buffer, uint32_t block[BLOCK_INTS])
+{
+ /* Convert the std::string (byte buffer) to a uint32_t array (MSB) */
+ for (unsigned int i = 0; i < BLOCK_INTS; i++)
+ {
+ block[i] = (buffer[4*i+3] & 0xff)
+ | (buffer[4*i+2] & 0xff)<<8
+ | (buffer[4*i+1] & 0xff)<<16
+ | (buffer[4*i+0] & 0xff)<<24;
+ }
+}
+
+
+void SHA1::read(std::istream &is, std::string &s, size_t max)
+{
+ char* sbuf = new char[max];
+
+ is.read(sbuf, max);
+ s.assign(sbuf, is.gcount());
+
+ delete[] sbuf;
+}
+
+
+std::string sha1(const std::string &string)
+{
+ SHA1 checksum;
+ checksum.update(string);
+ return checksum.final();
+}
diff --git a/libs/sha1/sha1.h b/libs/sha1/sha1.h
new file mode 100644
index 00000000..9f526376
--- /dev/null
+++ b/libs/sha1/sha1.h
@@ -0,0 +1,57 @@
+/*
+ sha1.h - header of
+
+ ============
+ SHA-1 in C++
+ ============
+
+ 100% Public Domain.
+
+ Original C Code
+ -- Steve Reid <steve@edmweb.com>
+ Small changes to fit into bglibs
+ -- Bruce Guenter <bruce@untroubled.org>
+ Translation to simpler C++ Code
+ -- Volker Grabsch <vog@notjusthosting.com>
+ Fixing bugs and improving style
+ -- Eugene Hopkinson <slowriot at voxelstorm dot com>
+*/
+
+#ifndef SHA1_HPP
+#define SHA1_HPP
+
+
+#include <iostream>
+#include <string>
+#include <stdint.h>
+
+class SHA1
+{
+public:
+ SHA1();
+ void update(const std::string &s);
+ void update(std::istream &is);
+ std::string final();
+ static std::string from_file(const std::string &filename);
+
+private:
+ static const unsigned int DIGEST_INTS = 5; /* number of 32bit integers per SHA1 digest */
+ static const unsigned int BLOCK_INTS = 16; /* number of 32bit integers per SHA1 block */
+ static const unsigned int BLOCK_BYTES = BLOCK_INTS * 4;
+
+ uint32_t digest[DIGEST_INTS];
+ std::string buffer;
+ uint64_t transforms;
+
+ void reset();
+ void transform(uint32_t block[BLOCK_BYTES]);
+
+ static void read(std::istream &is, std::string &s, size_t max);
+ static void buffer_to_block(const std::string &buffer, uint32_t block[BLOCK_INTS]);
+};
+
+std::string sha1(const std::string &string);
+
+
+
+#endif /* SHA1_HPP */
diff --git a/libs/subcircuit/.gitignore b/libs/subcircuit/.gitignore
new file mode 100644
index 00000000..9f1eb4e8
--- /dev/null
+++ b/libs/subcircuit/.gitignore
@@ -0,0 +1,2 @@
+demo
+scshell
diff --git a/libs/subcircuit/Makefile b/libs/subcircuit/Makefile
new file mode 100644
index 00000000..f81085b5
--- /dev/null
+++ b/libs/subcircuit/Makefile
@@ -0,0 +1,53 @@
+
+CONFIG := clang-debug
+# CONFIG := gcc-debug
+# CONFIG := profile
+# CONFIG := release
+
+CC = clang
+CXX = clang
+CXXFLAGS = -MD -Wall -Wextra -ggdb
+LDLIBS = -lstdc++
+
+ifeq ($(CONFIG),clang-debug)
+CXXFLAGS += -std=c++11 -O0
+endif
+
+ifeq ($(CONFIG),gcc-debug)
+CC = gcc
+CXX = gcc
+CXXFLAGS += -std=gnu++0x -O0
+endif
+
+ifeq ($(CONFIG),profile)
+CC = gcc
+CXX = gcc
+CXXFLAGS += -std=gnu++0x -Os -DNDEBUG
+endif
+
+ifeq ($(CONFIG),release)
+CC = gcc
+CXX = gcc
+CXXFLAGS += -std=gnu++0x -march=native -O3 -DNDEBUG
+endif
+
+all: demo scshell
+
+demo: demo.o subcircuit.o
+
+scshell: scshell.o subcircuit.o
+
+test: scshell
+ ./scshell < test_macc22.txt
+ ./scshell < test_mine.txt
+ perl test_perm.pl | ./scshell
+ splrun test_shorts.spl | ./scshell
+ splrun test_large.spl | ./scshell
+
+clean:
+ rm -f demo scshell *.o *.d
+
+.PHONY: all test clean
+
+-include *.d
+
diff --git a/libs/subcircuit/README b/libs/subcircuit/README
new file mode 100644
index 00000000..b1335681
--- /dev/null
+++ b/libs/subcircuit/README
@@ -0,0 +1,466 @@
+
+ **************************************************************************
+ * *
+ * The SubCircuit C++11 library *
+ * *
+ * An implementation of a modified Ullmann Subgraph Isomorphism Algorithm *
+ * for coarse grain logic networks. by Clifford Wolf *
+ * *
+ **************************************************************************
+
+============
+Introduction
+============
+
+This is a library that implements a modified Ullmann Subgraph Isomorphism
+Algorithm with additional features aimed at working with coarse grain logic
+networks. It also contains a simple frequent subcircuit mining algorithm.
+
+A simple command line tool that exposes the features of the library is also
+included.
+
+
+C++11 Warning
+-------------
+
+This project is written in C++11. Use appropriate compiler switches to compile
+it. Tested with clang version 3.0 and option -std=c++11. Also tested with gcc
+version 4.6.3 and option -std=c++0x.
+
+
+========
+Features
+========
+
+The input is two graphs (needle and haystack) that represent coarse grain
+logic networks. The algorithm identifies all subgraphs of haystack that are
+isomorphic to needle.
+
+The following additional features over the regular Ullmann Subgraph Isomorphism
+Algorithm are provided by the library.
+
+ * The graphs are attributed hypergraphs capable of representing netlists:
+
+ - Nodes represent the logic cells:
+ - Nodes have types and only match compatible types
+ - Nodes have ports with variable bit-width
+
+ - Hyperedges represent the signals:
+ - Each hyperedge connects one to many bits on ports on nodes
+
+ - Callback functions for advanced attributes and compatibility rules:
+ Any set of node-node compatibility rules and edge-edge
+ compatibility rules can be implemented by providing
+ the necessary callback functions.
+
+ * The algorithm is very efficient when all or many bits of one port are
+ connected to bits of the same other port. This is usually the case
+ in coarse grain logic networks. But the algorithm does not add any
+ restrictions in this area; it is just optimized for this scenario.
+
+ * The algorithm can be configured to allow larger ports in needle cells to
+ match smaller ports in haystack cells in certain situations. This way it
+ is possible to e.g. have a 32-bit adder cell in the needle match a
+ 16-bit adder cell in the haystack.
+
+ * The algorithm can be configured to perform port-swapping on certain
+ ports on certain cell types to match commutative operations properly.
+
+ This is, however, not implemented very efficiently when a larger number
+ of permutations is possible on a cell type. Therefore it is recommended
+ to only use swap groups with only a few members and a few such groups
+ on one cell type type.
+
+ Also note, that the algorithm can not resolve complex dependencies
+ between the port swappings of different cells. Therefore it is
+ recommended to only use port swapping on input pins of commutative
+ operations, where such complex dependencies can not emerge.
+
+ * The algorithm can be configured to distinguish between internal signals
+ of the needle and externally visible signals. The needle will only
+ match a subgraph of the haystack if that subgraph does not expose the
+ internal signal to nodes in the haystack outside the matching subgraph.
+
+ * The algorithm can recognize a subcircuit even if some or all of its
+ inputs and/or outputs are shorted together.
+
+ * Explicit fast support for constant signals without extra nodes for
+ constant drivers.
+
+ * Support for finding only non-overlapping matches.
+
+ * A simple miner for frequent subcircuts that operates on the same circuit
+ description format.
+
+ * The public API of the library is using std::string identifiers for
+ nodes, node types and ports. Internally the costly part of the
+ algorithm is only using integer values, thus speeding up the
+ algorithm without exposing complex internal encodings to the caller.
+
+
+=================
+API Documentation
+=================
+
+This section gives a brief overview of the API. For a working example, have a
+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
+set up the circuit.
+
+ SubCircuit::Graph myGraph;
+
+For each node in the circuit call the createNode() method. Specify the
+identifier for the node and also the type of function implemented by the node.
+Then call createPort() for each port of this node.
+
+E.g. the following code adds a node "myAdder" of type "add" with three 32 bit
+wide ports "A", "B" and "Y". Note that SubCircuit does not care which port is
+an input and which port is an output. The last (and optional) argument to
+createPort() specifies the minimum number of bits required for this port in the
+haystack (this field is only used in the needle graph). So in this example the
+node would e.g. also match any adder with a bit width smaller 32.
+
+ myGraph.createNode("myAdder", "add");
+ myGraph.createPort("myAdder", "A", 32, 1);
+ myGraph.createPort("myAdder", "B", 32, 1);
+ myGraph.createPort("myAdder", "Y", 32, 1);
+
+The createConnection() method can be used to connect the nodes. It internally
+creates a hypergraph. So the following code does not only connect cell1.Y with
+cell2.A and cell3.A but also implicitly cell2.A with cell3.A.
+
+ myGraph.createConnection("cell1", "Y", "cell2", "A");
+ myGraph.createConnection("cell1", "Y", "cell3", "A");
+
+Redundent calls to createConnection() are ignored. As long as the method is
+called after the relevant nodes and ports are created, the order in which the
+createConnection() calls are performed is irrelevant.
+
+The createConnection() method can also be used to connect single bit signals.
+In this case the start bit for both ports must be provided as well as an
+optional width (which defaults to 1). E.g. the following calls can be used to
+connect the 32 bit port cell4.Y to the 32 bit port cell5.A with a one bit left
+rotate shift,
+
+ myGraph.createConnection("cell4", "Y", 0, "cell5", "A", 1, 31);
+ myGraph.createConnection("cell4", "Y", 31, "cell5", "A", 0);
+
+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 1:
+
+ myGraph.createConnection("cell6", "A", 0, '1');
+
+It is also possible to set an entire port to a integer value, using the
+encodings '0' and '1' for the binary digits:
+
+ myGraph.createConnection("cell6", "A", 42);
+
+The method markExtern() can be used to mark a signal as externally visible. In
+a needle graph this means, this signal may match a signal in the haystack that
+is used outside the matching subgraph. In a haystack graph this means, this
+signal is used outside the haystack graph. I.e. an internal signal of the
+needle won't match an external signal of the haystack regardless where the
+signal is used in the haystack.
+
+In some application one may disable this extern/intern checks. This can easily
+be achieved by marking all signals in the needle as extern. This can be done
+using the Graph::markAllExtern() method.
+
+
+Setting up and running solvers
+------------------------------
+
+To actually run the subgraph isomorphism algorithm, an instance of
+SubCircuit::Solver must be created.
+
+ SubCircuit::Solver mySolver;
+
+The addGraph() method can be used to register graphs with the solver:
+
+ mySolver.addGraph("graph1", myGraph);
+ mySolver.addGraph("graph2", myOtherGraph);
+
+Usually nodes in the needle and the haystack must have the same type identifier
+to match each other. Additionally pairs of compatible needle and haystack node
+pairs can be registered using the addCompatibleTypes() method:
+
+ mySolver.addCompatibleTypes("alu", "add");
+ mySolver.addCompatibleTypes("alu", "sub");
+ mySolver.addCompatibleTypes("alu", "and");
+ mySolver.addCompatibleTypes("alu", "or");
+ mySolver.addCompatibleTypes("alu", "xor");
+
+Note that nodes in needle and haystack must also use the same naming convention
+for their ports in order to be considered compatible by the algorithm.
+
+Similarly the method addCompatibleConstants() can be used the specify which
+constant values in the needle should match which constant value in the haystack.
+Equal values always do match.
+
+ mySolver.addCompatibleConstants('x', '0');
+ mySolver.addCompatibleConstants('x', '1');
+
+Some cells implement commutative operations that don't care if their input
+operands are swapped. For this cell types it is possible to register groups
+of swappable ports. Let's consider a cell "macc23" that implements the
+function Y = (A * B) + (C * D * E):
+
+ mySolver.addSwappablePorts("macc23", "A", "B");
+ mySolver.addSwappablePorts("macc23", "C", "D", "E");
+
+Sometimes the rules for port swapping are a more complicated and the swapping
+of one port is related to the swapping of another port. Let's consider a cell
+"macc22" that implements the function Y = (A * B) + (C * D):
+
+ mySolver.addSwappablePorts("macc22", "A", "B");
+ mySolver.addSwappablePorts("macc22", "C", "D");
+
+ std::map<std::string, std::string> portMapping;
+ portMapping["A"] = "C";
+ portMapping["B"] = "D";
+ portMapping["C"] = "A";
+ portMapping["D"] = "B";
+ mySolver.addSwappablePortsPermutation("macc22", portMapping);
+
+I.e. the method mySolver.addSwappablePortsPermutation() can be used to register
+additional permutations for a node type of which one or none is applied on top
+of the permutations yielded by the permutations generated by the swap groups.
+
+Note that two solutions that differ only in the applied port swapping are not
+reported as separate solutions. Instead only one of them is selected (in most
+cases the one with less port swapping as it is usually identified first).
+
+Once everything has been set up, the solve() method can be used to actually
+search for isomorphic subgraphs. The first argument to solve() is an
+std::vector<SubCircuit::Solver::Result> objects to which all found solutions
+are appended. The second argument is the identifier under which the needle
+graph has been registered and the third argument is the identifier under which
+the haystack graph has been registered:
+
+ std::vector<SubCircuit::Solver::Result> results;
+ mySolver.solve(results, "graph1", "graph2");
+
+The SubCircuit::Solver::Result object is a simple data structure that contains
+the mappings between needle and haystack nodes, port mappings after the port
+swapping and some additional metadata. See "subcircuit.h" and "demo.cc" for
+details.
+
+The solve() method has a third optional boolean argument. If it is set to
+false, solve will not return any solutions that contain haystack nodes that
+have been part of a previously found solution. This way it is e.g. easy
+to implement a greedy macro cell matching algorithm:
+
+ std::vector<SubCircuit::Solver::Result> results;
+ mySolver.solve(results, "macroCell1", "circuit", false);
+ mySolver.solve(results, "macroCell2", "circuit", false);
+ mySolver.solve(results, "macroCell3", "circuit", false);
+
+After this code has been executed, the results vector contains all
+non-overlapping matches of the three macrocells. The method
+clearOverlapHistory() can be used to reset the internal state used
+for this feature. The default value for the third argument to solve()
+is true (allow overlapping). The optional boolean fourth argument to the
+Graph::createNode() method can be used to mark a node as shareable even
+in non-overlapping solver mode.
+
+The solve() method also has a fourth optional integer argument. If it is set to
+a positive integer, this integer specifies the maximum number of solutions to
+be appended to the results vector, i.e. to terminate the algorithm early when
+the set number of matches is found. When this fourth argument is negative or
+omitted all matches are found and appended.
+
+An alternative version of the solve() method supports an additional argument
+after they haystack graph identifier that specifies initial mappings for
+the algorithm. In the following example only the haystack nodes cell_1 and
+cell_2 are considered as mappings for the needle node cell_A:
+
+ std::map<std::string, std::set<std::string>> initialMappings;
+ initialMappings["cell_A"].insert("cell_1");
+ initialMappings["cell_A"].insert("cell_2");
+
+ std::vector<SubCircuit::Solver::Result> results;
+ mySolver.solve(results, "graph1", "graph2", initialMappings);
+
+The clearConfig() method can be used to clear all data registered using
+addCompatibleTypes(), addCompatibleConstants(), addSwappablePorts() and
+addSwappablePortsPermutation() but retaining the graphs and the overlap state.
+
+
+Using user callback function
+----------------------------
+
+For more complex tasks it is possible to derive a class from SubCircuit::Solver
+that overloads one or more of the following virtual methods. The userData
+arguments to the following methods are void pointers that can be passed as
+third argument to Graph::createNode() and are simly passed thru to the user
+callback functions together with the node id whenever a node is referenced.
+
+bool userCompareNodes(needleGraphId, needleNodeId, needleUserData, haystackGraphId, haystackNodeId, haystackUserData):
+
+ Perform additional checks on a pair of nodes (one from the needle, one
+ from the haystack) to determine if the nodes are compatible. The default
+ implementation always returns true.
+
+
+bool userCompareEdge(needleGraphId, needleFromNodeId, needleFromUserData, needleToNodeId, needleToUserData,
+ haystackGraphId, haystackFromNodeId, haystackFromUserData, haystackToNodeId, haystackToUserData):
+
+ 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
+ that edge from the haystack. The default implementation always
+ returns true.
+
+bool userCheckSolution(result):
+
+ Perform additional checks on a solution before appending it to the
+ results vector. When this function returns false, the solution is
+ ignored. The default implementation always returns true.
+
+
+Mining for frequent SubCircuits
+-------------------------------
+
+The solver also contains a miner for frequent subcircuits. The following code
+fragment will find all frequent subcircuits with at least minNodes nodes and
+at most maxNodes nodes that occurs at least minMatches times:
+
+ std::vector<SubCircuit::Solver::MineResult> results;
+ mySolver.mine(results, minNodes, maxNodes, minMatches);
+
+The mine() method has an optional fifth parameter that limits the number of
+matches counted in one graph. This can be useful when mining for circuits that
+are found in at least a number of graphs. E.g. the following call would find
+all subcircuits with 5 nodes that are found in at least 7 of the registered
+graphs:
+
+ mySolver.mine(results, 5, 5, 7, 1);
+
+Note that this miner is not very efficient and therefore its use is not
+recommended for large circuits. Also note that the miner is working under the
+assumption that subgraph isomorphism is bidirectional. This is not the case in
+circuits with gates with shorted pins. This can result in undetected frequent
+subcircuits in some corner cases.
+
+
+Debugging
+---------
+
+For debugging purposes the SubCircuit::Solver class implements a setVerbose()
+method. When called once, all further calls to the solve() method cause the
+algorithm to dump out a lot of debug information to stdout.
+
+In conjunction with setVerbose() one can also overload the userAnnotateEdge()
+method in order to add additional information about the edges to the debug
+output.
+
+
+===================
+Shell Documentation
+===================
+
+This package also contains a small command-line tool called "scshell" that can
+be used for experimentation with the algorithm. This program reads a series of
+commands from stdin and reports its findings to stdout on exit.
+
+ $ ./scshell < test_macc22.txt
+
+ ...
+
+ Match #3: (macc22 in macc4x2)
+ add_1 -> add_2 A:B B:A Y:Y
+ mul_1 -> mul_4 A:A B:B Y:Y
+ mul_2 -> mul_3 A:A B:B Y:Y
+
+The following commands can be used in scshell to specify graphs:
+
+ graph <graph_name>
+ ...
+ endgraph
+
+ Used to specify a graph with the given name. Only the commands
+ "node", "connect" and "extern" may be used within the graph ...
+ endgraph block.
+
+ node <node_name> [<port_name> [<bits> [<min_bits>]]]+
+
+ Used to create a node and ports. This command is a direct frontend
+ to the Graph::createNode() and Graph::createPort() methods.
+
+ connect <from_node> <from_port> <to_node> <to_port>
+ connect <from_node> <from_port> <from_bit> <to_node> <to_port> <to_bit>
+ connect <from_node> <from_port> <from_bit> <to_node> <to_port> <to_bit> <width>
+
+ Used to connect the nodes in the graph via Graph::createConnection().
+
+ constant <node> <port> [<bit>] <value>
+
+ Call Graph::createConstant().
+
+ extern <node> [<port> [<bit>]]+
+
+ Mark signals as extern via Graph::markExtern().
+
+ allextern
+
+ Mark all signals as extern via Graph::markAllExtern().
+
+The following commands can be used in scshell outside a graph ... endgraph block:
+
+ compatible <needle_type> <haystack_type>
+
+ Call Solver::addCompatibleTypes().
+
+ constcompat <needle_value> <haystack_value>
+
+ Call Solver::addCompatibleConstants().
+
+ swapgroup <needle_type> <port>+
+
+ Call Solver::addSwappablePorts().
+
+ swapperm <needle_type> <ports>+ : <ports>+
+
+ Call Solver::addSwappablePortsPermutation(). Both port lists must
+ have the same length and the second one must be a permutation of the
+ first one.
+
+ initmap <needle_node> <haystack_node>+
+
+ Add an entry to the initial mappings for the next solve command.
+ This mappings are automatically reset after the solve command.
+
+ solve <needle_graph> <haystack_graph> [<allow_overlap> [<max_solutions>]]
+
+ Call Solver::solve(). The <allow_overlap> must be "1" or "true"
+ for true and "0" or "false" for false.
+
+ mine <min_nodes> <max_nodes> <min_matches> [<limit_matches_per_graph>]
+
+ Call Solver::mine().
+
+ expect <number>
+
+ Print all results so far since the last call to expect. Expect
+ <number> results and exit with error code 1 if a different number
+ of results have been found.
+
+ clearoverlap
+
+ Call Solver::clearOverlapHistory().
+
+ clearconfig
+
+ Call Solver::clearConfig().
+
+ verbose
+
+ Call Solver::setVerbose().
+
diff --git a/libs/subcircuit/demo.cc b/libs/subcircuit/demo.cc
new file mode 100644
index 00000000..149dc6aa
--- /dev/null
+++ b/libs/subcircuit/demo.cc
@@ -0,0 +1,134 @@
+#include "subcircuit.h"
+#include <stdio.h>
+
+#define VERBOSE
+
+int main()
+{
+ SubCircuit::Graph needle, haystack;
+
+ // create needle graph
+
+ needle.createNode("mul_1", "product");
+ needle.createPort("mul_1", "A", 4);
+ needle.createPort("mul_1", "B", 4);
+ needle.createPort("mul_1", "Y", 4);
+ needle.markExtern("mul_1", "A");
+ needle.markExtern("mul_1", "B");
+
+ needle.createNode("mul_2", "product");
+ needle.createPort("mul_2", "A", 4);
+ needle.createPort("mul_2", "B", 4);
+ needle.createPort("mul_2", "Y", 4);
+ needle.markExtern("mul_2", "A");
+ needle.markExtern("mul_2", "B");
+
+ needle.createNode("add_1", "sum");
+ needle.createPort("add_1", "A", 4);
+ needle.createPort("add_1", "B", 4);
+ needle.createPort("add_1", "Y", 4);
+ needle.markExtern("add_1", "Y");
+
+ needle.createConnection("mul_1", "Y", "add_1", "A");
+ needle.createConnection("mul_2", "Y", "add_1", "B");
+
+#ifdef VERBOSE
+ printf("\n");
+ needle.print();
+#endif
+
+ // create haystack graph
+
+#if 0
+ for (int i = 0; i < 4; i++) {
+ char id[100];
+ snprintf(id, 100, "mul_%d", i);
+ haystack.createNode(id, "mul");
+ haystack.createPort(id, "A", 4);
+ haystack.createPort(id, "B", 4);
+ haystack.createPort(id, "Y", 4);
+ haystack.markExtern(id, "A");
+ haystack.markExtern(id, "B");
+ }
+
+ for (int i = 0; i < 3; i++) {
+ char id[100];
+ snprintf(id, 100, "add_%d", i);
+ haystack.createNode(id, "add");
+ haystack.createPort(id, "A", 4);
+ haystack.createPort(id, "B", 4);
+ haystack.createPort(id, "Y", 4);
+ }
+
+ haystack.createConnection("mul_0", "Y", "add_0", "A");
+ haystack.createConnection("mul_1", "Y", "add_0", "B");
+
+ haystack.createConnection("mul_2", "Y", "add_1", "A");
+ haystack.createConnection("mul_3", "Y", "add_1", "B");
+
+ haystack.createConnection("add_0", "Y", "add_2", "A");
+ haystack.createConnection("add_1", "Y", "add_2", "B");
+ haystack.markExtern("add_2", "Y");
+#else
+ std::vector<std::string> cellIds;
+ srand48(12345);
+
+ for (int i = 0; i < 45; i++) {
+ char id[100];
+ snprintf(id, 100, "cell_%02d", i);
+ haystack.createNode(id, i < 30 ? "mul" : "add");
+ haystack.createPort(id, "A", 4);
+ haystack.createPort(id, "B", 4);
+ haystack.createPort(id, "Y", 4);
+ cellIds.push_back(id);
+ }
+
+ for (int i = 0; i < int(cellIds.size()); i++) {
+ if (lrand48() % (i < 20 ? 3 : 2) != 0)
+ continue;
+ const std::string &id = cellIds[i];
+ const std::string &id_left = cellIds[lrand48() % cellIds.size()];
+ const std::string &id_right = cellIds[lrand48() % cellIds.size()];
+ haystack.createConnection(id_left, "Y", id, "A");
+ haystack.createConnection(id_right, "Y", id, "B");
+ }
+#endif
+
+#ifdef VERBOSE
+ printf("\n");
+ haystack.print();
+#endif
+
+ // search needle in haystack
+
+ SubCircuit::Solver solver;
+ std::vector<SubCircuit::Solver::Result> results;
+
+#ifdef VERBOSE
+ solver.setVerbose();
+#endif
+
+ solver.addCompatibleTypes("product", "mul");
+ solver.addCompatibleTypes("sum", "add");
+
+ solver.addSwappablePorts("product", "A", "B");
+ solver.addSwappablePorts("sum", "A", "B");
+
+ solver.addGraph("needle", needle);
+ solver.addGraph("haystack", haystack);
+ solver.solve(results, "needle", "haystack");
+
+ for (int i = 0; i < int(results.size()); i++) {
+ printf("\nMatch #%d: (%s in %s)\n", i, results[i].needleGraphId.c_str(), results[i].haystackGraphId.c_str());
+ for (const auto &it : results[i].mappings) {
+ printf(" %s -> %s", it.first.c_str(), it.second.haystackNodeId.c_str());
+ for (const auto &it2 : it.second.portMapping)
+ printf(" %s:%s", it2.first.c_str(), it2.second.c_str());
+ printf("\n");
+ }
+ }
+
+ printf("\n");
+ return 0;
+}
+
diff --git a/libs/subcircuit/scshell.cc b/libs/subcircuit/scshell.cc
new file mode 100644
index 00000000..c4b37a4d
--- /dev/null
+++ b/libs/subcircuit/scshell.cc
@@ -0,0 +1,261 @@
+#include "subcircuit.h"
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+std::vector<std::string> readLine()
+{
+ char buffer[4096];
+ std::vector<std::string> tokenList;
+
+ while (tokenList.size() == 0 && fgets(buffer, sizeof(buffer), stdin) != NULL) {
+ for (char *p = buffer; char *tok = strtok(p, " \t\r\n"); p = NULL) {
+ if (p != NULL && tok[0] == '#')
+ break;
+ tokenList.push_back(tok);
+ }
+ }
+
+ return tokenList;
+}
+
+int main()
+{
+ std::string graphId;
+ SubCircuit::Graph *graph = NULL;
+ SubCircuit::Solver solver;
+ std::map<std::string, std::set<std::string>> initialMappings;
+ std::vector<SubCircuit::Solver::Result> results;
+ std::vector<SubCircuit::Solver::MineResult> mineResults;
+ std::vector<std::string> cmdBuffer;
+ bool lastCommandExpect = false;
+
+ while (1)
+ {
+ cmdBuffer = readLine();
+ if (cmdBuffer.empty())
+ break;
+
+ printf(graph == NULL || cmdBuffer[0] == "endgraph" ? ">" : "> ");
+ for (const auto &tok : cmdBuffer)
+ printf(" %s", tok.c_str());
+ printf("\n");
+
+ lastCommandExpect = false;
+
+ if (graph != NULL)
+ {
+ if (cmdBuffer[0] == "node" && cmdBuffer.size() >= 3) {
+ graph->createNode(cmdBuffer[1], cmdBuffer[2]);
+ for (int i = 3; i < int(cmdBuffer.size()); i++) {
+ std::string portId = cmdBuffer[i];
+ int width = 1, minWidth = -1;
+ if (i+1 < int(cmdBuffer.size()) && '0' <= cmdBuffer[i+1][0] && cmdBuffer[i+1][0] <= '9')
+ width = atoi(cmdBuffer[++i].c_str());
+ if (i+1 < int(cmdBuffer.size()) && '0' <= cmdBuffer[i+1][0] && cmdBuffer[i+1][0] <= '9')
+ minWidth = atoi(cmdBuffer[++i].c_str());
+ graph->createPort(cmdBuffer[1], portId, width, minWidth);
+ }
+ continue;
+ }
+
+ if (cmdBuffer[0] == "connect" && cmdBuffer.size() == 5) {
+ graph->createConnection(cmdBuffer[1], cmdBuffer[2], cmdBuffer[3], cmdBuffer[4]);
+ continue;
+ }
+
+ if (cmdBuffer[0] == "connect" && cmdBuffer.size() == 7) {
+ graph->createConnection(cmdBuffer[1], cmdBuffer[2], atoi(cmdBuffer[3].c_str()), cmdBuffer[4], cmdBuffer[5], atoi(cmdBuffer[6].c_str()));
+ continue;
+ }
+
+ if (cmdBuffer[0] == "connect" && cmdBuffer.size() == 8) {
+ graph->createConnection(cmdBuffer[1], cmdBuffer[2], atoi(cmdBuffer[3].c_str()), cmdBuffer[4], cmdBuffer[5], atoi(cmdBuffer[6].c_str()), atoi(cmdBuffer[7].c_str()));
+ continue;
+ }
+
+ if (cmdBuffer[0] == "constant" && cmdBuffer.size() == 5) {
+ int constValue = cmdBuffer[4].size() > 1 && cmdBuffer[4][0] == '#' ? atoi(cmdBuffer[4].c_str()+1) : cmdBuffer[4][0];
+ graph->createConstant(cmdBuffer[1], cmdBuffer[2], atoi(cmdBuffer[3].c_str()), constValue);
+ continue;
+ }
+
+ if (cmdBuffer[0] == "constant" && cmdBuffer.size() == 4) {
+ graph->createConstant(cmdBuffer[1], cmdBuffer[2], atoi(cmdBuffer[3].c_str()));
+ continue;
+ }
+
+ if (cmdBuffer[0] == "extern" && cmdBuffer.size() >= 3) {
+ for (int i = 2; i < int(cmdBuffer.size()); i++) {
+ std::string portId = cmdBuffer[i];
+ int bit = -1;
+ if (i+1 < int(cmdBuffer.size()) && '0' <= cmdBuffer[i+1][0] && cmdBuffer[i+1][0] <= '9')
+ bit = atoi(cmdBuffer[++i].c_str());
+ graph->markExtern(cmdBuffer[1], portId, bit);
+ }
+ continue;
+ }
+
+ if (cmdBuffer[0] == "allextern" && cmdBuffer.size() == 1) {
+ graph->markAllExtern();
+ continue;
+ }
+
+ if (cmdBuffer[0] == "endgraph" && cmdBuffer.size() == 1) {
+ solver.addGraph(graphId, *graph);
+ delete graph;
+ graph = NULL;
+ continue;
+ }
+ }
+ else
+ {
+ if (cmdBuffer[0] == "graph" && cmdBuffer.size() == 2) {
+ graph = new SubCircuit::Graph;
+ graphId = cmdBuffer[1];
+ continue;
+ }
+
+ if (cmdBuffer[0] == "compatible" && cmdBuffer.size() == 3) {
+ solver.addCompatibleTypes(cmdBuffer[1], cmdBuffer[2]);
+ continue;
+ }
+
+ if (cmdBuffer[0] == "constcompat" && cmdBuffer.size() == 3) {
+ int needleConstValue = cmdBuffer[1].size() > 1 && cmdBuffer[1][0] == '#' ? atoi(cmdBuffer[1].c_str()+1) : cmdBuffer[1][0];
+ int haystackConstValue = cmdBuffer[2].size() > 1 && cmdBuffer[2][0] == '#' ? atoi(cmdBuffer[2].c_str()+1) : cmdBuffer[2][0];
+ solver.addCompatibleConstants(needleConstValue, haystackConstValue);
+ continue;
+ }
+
+ if (cmdBuffer[0] == "swapgroup" && cmdBuffer.size() >= 4) {
+ std::set<std::string> ports;
+ for (int i = 2; i < int(cmdBuffer.size()); i++)
+ ports.insert(cmdBuffer[i]);
+ solver.addSwappablePorts(cmdBuffer[1], ports);
+ continue;
+ }
+
+ if (cmdBuffer[0] == "swapperm" && cmdBuffer.size() >= 4 && cmdBuffer.size() % 2 == 1 && cmdBuffer[cmdBuffer.size()/2 + 1] == ":") {
+ std::map<std::string, std::string> portMapping;
+ int n = (cmdBuffer.size()-3) / 2;
+ for (int i = 0; i < n; i++)
+ portMapping[cmdBuffer[i+2]] = cmdBuffer[i+3+n];
+ solver.addSwappablePortsPermutation(cmdBuffer[1], portMapping);
+ continue;
+ }
+
+ if (cmdBuffer[0] == "initmap" && cmdBuffer.size() >= 4) {
+ for (int i = 2; i < int(cmdBuffer.size()); i++)
+ initialMappings[cmdBuffer[1]].insert(cmdBuffer[i]);
+ continue;
+ }
+
+ if (cmdBuffer[0] == "solve" && 3 <= cmdBuffer.size() && cmdBuffer.size() <= 5) {
+ bool allowOverlap = true;
+ int maxSolutions = -1;
+ if (cmdBuffer.size() >= 4)
+ allowOverlap = cmdBuffer[3] == "true" || atoi(cmdBuffer[3].c_str()) ? true : false;
+ if (cmdBuffer.size() >= 5)
+ maxSolutions = atoi(cmdBuffer[4].c_str());
+ solver.solve(results, cmdBuffer[1], cmdBuffer[2], initialMappings, allowOverlap, maxSolutions);
+ initialMappings.clear();
+ continue;
+ }
+
+ if (cmdBuffer[0] == "mine" && 4 <= cmdBuffer.size() && cmdBuffer.size() <= 5) {
+ solver.mine(mineResults, atoi(cmdBuffer[1].c_str()), atoi(cmdBuffer[2].c_str()),
+ atoi(cmdBuffer[3].c_str()), cmdBuffer.size() == 5 ? atoi(cmdBuffer[4].c_str()) : -1);
+ continue;
+ }
+
+ if (cmdBuffer[0] == "clearoverlap" && cmdBuffer.size() == 1) {
+ solver.clearOverlapHistory();
+ continue;
+ }
+
+ if (cmdBuffer[0] == "clearconfig" && cmdBuffer.size() == 1) {
+ solver.clearConfig();
+ continue;
+ }
+
+ if (cmdBuffer[0] == "verbose" && cmdBuffer.size() == 1) {
+ solver.setVerbose();
+ continue;
+ }
+
+ if (cmdBuffer[0] == "expect" && cmdBuffer.size() == 2) {
+ int expected = atoi(cmdBuffer[1].c_str());
+ printf("\n-- Expected %d, Got %d --\n", expected, int(results.size()) + int(mineResults.size()));
+ for (int i = 0; i < int(results.size()); i++) {
+ printf("\nMatch #%d: (%s in %s)\n", i, results[i].needleGraphId.c_str(), results[i].haystackGraphId.c_str());
+ for (const auto &it : results[i].mappings) {
+ printf(" %s -> %s", it.first.c_str(), it.second.haystackNodeId.c_str());
+ for (const auto &it2 : it.second.portMapping)
+ printf(" %s:%s", it2.first.c_str(), it2.second.c_str());
+ printf("\n");
+ }
+ }
+ for (auto &result : mineResults) {
+ printf("\nFrequent SubCircuit with %d nodes and %d matches:\n", int(result.nodes.size()), result.totalMatchesAfterLimits);
+ printf(" primary match in %s:", result.graphId.c_str());
+ for (auto &node : result.nodes)
+ printf(" %s", node.nodeId.c_str());
+ printf("\n");
+ for (auto &it : result.matchesPerGraph)
+ printf(" matches in %s: %d\n", it.first.c_str(), it.second);
+ }
+ printf("\n");
+ if (expected != int(results.size()) + int(mineResults.size())) {
+ printf("^^ expected %d, Got %d ^^\n\n", expected, int(results.size()) + int(mineResults.size()));
+ printf(" +----------------+\n");
+ printf(" | \\|/ ____ \\|/ |\n");
+ printf(" | \"@'/ ,. \\`@\" |\n");
+ printf(" | /_| \\__/ |_\\ |\n");
+ printf(" | \\__U_/ |\n");
+ printf(" | | | |\n");
+ printf(" +----------------+\n\n");
+ return 1;
+ }
+ results.clear();
+ mineResults.clear();
+ lastCommandExpect = true;
+ continue;
+ }
+ }
+
+ printf("Invalid input command!\n");
+ return 1;
+ }
+
+ if (graph)
+ delete graph;
+
+ if (!lastCommandExpect) {
+ printf("\n-- Got %d --\n", int(results.size()) + int(mineResults.size()));
+ for (int i = 0; i < int(results.size()); i++) {
+ printf("\nMatch #%d: (%s in %s)\n", i, results[i].needleGraphId.c_str(), results[i].haystackGraphId.c_str());
+ for (const auto &it : results[i].mappings) {
+ printf(" %s -> %s", it.first.c_str(), it.second.haystackNodeId.c_str());
+ for (const auto &it2 : it.second.portMapping)
+ printf(" %s:%s", it2.first.c_str(), it2.second.c_str());
+ printf("\n");
+ }
+ }
+ for (auto &result : mineResults) {
+ printf("\nFrequent SubCircuit with %d nodes and %d matches:\n", int(result.nodes.size()), result.totalMatchesAfterLimits);
+ printf(" primary match in %s:", result.graphId.c_str());
+ for (auto &node : result.nodes)
+ printf(" %s", node.nodeId.c_str());
+ printf("\n");
+ for (auto &it : result.matchesPerGraph)
+ printf(" matches in %s: %d\n", it.first.c_str(), it.second);
+ }
+ } else
+ printf("PASSED.\n");
+
+ printf("\n");
+
+ return 0;
+}
+
diff --git a/libs/subcircuit/subcircuit.cc b/libs/subcircuit/subcircuit.cc
new file mode 100644
index 00000000..7c723683
--- /dev/null
+++ b/libs/subcircuit/subcircuit.cc
@@ -0,0 +1,1694 @@
+/*
+ * SubCircuit -- An implementation of the Ullmann Subgraph Isomorphism
+ * algorithm for coarse grain logic networks
+ *
+ * Copyright (C) 2013 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 "subcircuit.h"
+
+#include <algorithm>
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#ifdef _YOSYS_
+# include "kernel/yosys.h"
+# define my_printf YOSYS_NAMESPACE_PREFIX log
+#else
+# define my_printf printf
+#endif
+
+using namespace SubCircuit;
+
+#ifndef _YOSYS_
+static std::string my_stringf(const char *fmt, ...)
+{
+ std::string string;
+ char *str = NULL;
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (vasprintf(&str, fmt, ap) < 0)
+ str = NULL;
+ va_end(ap);
+
+ if (str != NULL) {
+ string = str;
+ free(str);
+ }
+
+ return string;
+}
+#else
+# define my_stringf YOSYS_NAMESPACE_PREFIX stringf
+#endif
+
+SubCircuit::Graph::Graph(const Graph &other, const std::vector<std::string> &otherNodes)
+{
+ allExtern = other.allExtern;
+
+ std::map<int, int> other2this;
+ for (int i = 0; i < int(otherNodes.size()); i++) {
+ assert(other.nodeMap.count(otherNodes[i]) > 0);
+ other2this[other.nodeMap.at(otherNodes[i])] = i;
+ nodeMap[otherNodes[i]] = i;
+ }
+
+ std::map<int, int> edges2this;
+ for (auto &i1 : other2this)
+ for (auto &i2 : other.nodes[i1.first].ports)
+ for (auto &i3 : i2.bits)
+ if (edges2this.count(i3.edgeIdx) == 0) {
+ int next_idx = edges2this.size();
+ edges2this[i3.edgeIdx] = next_idx;
+ }
+
+ edges.resize(edges2this.size());
+ for (auto &it : edges2this) {
+ for (auto &bit : other.edges[it.first].portBits)
+ if (other2this.count(bit.nodeIdx) > 0)
+ edges[it.second].portBits.insert(BitRef(other2this[bit.nodeIdx], bit.portIdx, bit.bitIdx));
+ edges[it.second].constValue = other.edges[it.first].constValue;
+ edges[it.second].isExtern = other.edges[it.first].isExtern;
+ }
+
+ nodes.resize(other2this.size());
+ for (auto &it : other2this) {
+ nodes[it.second] = other.nodes[it.first];
+ for (auto &i2 : nodes[it.second].ports)
+ for (auto &i3 : i2.bits)
+ i3.edgeIdx = edges2this.at(i3.edgeIdx);
+ }
+}
+
+bool SubCircuit::Graph::BitRef::operator < (const BitRef &other) const
+{
+ if (nodeIdx != other.nodeIdx)
+ return nodeIdx < other.nodeIdx;
+ if (portIdx != other.portIdx)
+ return portIdx < other.portIdx;
+ return bitIdx < other.bitIdx;
+}
+
+void SubCircuit::Graph::createNode(std::string nodeId, std::string typeId, void *userData, bool shared)
+{
+ assert(nodeMap.count(nodeId) == 0);
+ nodeMap[nodeId] = nodes.size();
+ nodes.push_back(Node());
+
+ Node &newNode = nodes.back();
+ newNode.nodeId = nodeId;
+ newNode.typeId = typeId;
+ newNode.userData = userData;
+ newNode.shared = shared;
+}
+
+void SubCircuit::Graph::createPort(std::string nodeId, std::string portId, int width, int minWidth)
+{
+ assert(nodeMap.count(nodeId) != 0);
+ int nodeIdx = nodeMap[nodeId];
+ Node &node = nodes[nodeIdx];
+
+ assert(node.portMap.count(portId) == 0);
+
+ int portIdx = node.ports.size();
+ node.portMap[portId] = portIdx;
+ node.ports.push_back(Port());
+ Port &port = node.ports.back();
+
+ port.portId = portId;
+ port.minWidth = minWidth < 0 ? width : minWidth;
+ port.bits.insert(port.bits.end(), width, PortBit());
+
+ for (int i = 0; i < width; i++) {
+ port.bits[i].edgeIdx = edges.size();
+ edges.push_back(Edge());
+ edges.back().portBits.insert(BitRef(nodeIdx, portIdx, i));
+ }
+}
+
+void SubCircuit::Graph::createConnection(std::string fromNodeId, std::string fromPortId, int fromBit, std::string toNodeId, std::string toPortId, int toBit, int width)
+{
+ assert(nodeMap.count(fromNodeId) != 0);
+ assert(nodeMap.count(toNodeId) != 0);
+
+ int fromNodeIdx = nodeMap[fromNodeId];
+ Node &fromNode = nodes[fromNodeIdx];
+
+ int toNodeIdx = nodeMap[toNodeId];
+ Node &toNode = nodes[toNodeIdx];
+
+ assert(fromNode.portMap.count(fromPortId) != 0);
+ assert(toNode.portMap.count(toPortId) != 0);
+
+ int fromPortIdx = fromNode.portMap[fromPortId];
+ Port &fromPort = fromNode.ports[fromPortIdx];
+
+ int toPortIdx = toNode.portMap[toPortId];
+ Port &toPort = toNode.ports[toPortIdx];
+
+ if (width < 0) {
+ assert(fromBit == 0 && toBit == 0);
+ assert(fromPort.bits.size() == toPort.bits.size());
+ width = fromPort.bits.size();
+ }
+
+ assert(fromBit >= 0 && toBit >= 0);
+ for (int i = 0; i < width; i++)
+ {
+ assert(fromBit + i < int(fromPort.bits.size()));
+ assert(toBit + i < int(toPort.bits.size()));
+
+ int fromEdgeIdx = fromPort.bits[fromBit + i].edgeIdx;
+ int toEdgeIdx = toPort.bits[toBit + i].edgeIdx;
+
+ if (fromEdgeIdx == toEdgeIdx)
+ continue;
+
+ // merge toEdge into fromEdge
+ if (edges[toEdgeIdx].isExtern)
+ edges[fromEdgeIdx].isExtern = true;
+ if (edges[toEdgeIdx].constValue) {
+ assert(edges[fromEdgeIdx].constValue == 0);
+ edges[fromEdgeIdx].constValue = edges[toEdgeIdx].constValue;
+ }
+ for (const auto &ref : edges[toEdgeIdx].portBits) {
+ edges[fromEdgeIdx].portBits.insert(ref);
+ nodes[ref.nodeIdx].ports[ref.portIdx].bits[ref.bitIdx].edgeIdx = fromEdgeIdx;
+ }
+
+ // remove toEdge (move last edge over toEdge if needed)
+ if (toEdgeIdx+1 != int(edges.size())) {
+ edges[toEdgeIdx] = edges.back();
+ for (const auto &ref : edges[toEdgeIdx].portBits)
+ nodes[ref.nodeIdx].ports[ref.portIdx].bits[ref.bitIdx].edgeIdx = toEdgeIdx;
+ }
+ edges.pop_back();
+ }
+}
+
+void SubCircuit::Graph::createConnection(std::string fromNodeId, std::string fromPortId, std::string toNodeId, std::string toPortId)
+{
+ createConnection(fromNodeId, fromPortId, 0, toNodeId, toPortId, 0, -1);
+}
+
+void SubCircuit::Graph::createConstant(std::string toNodeId, std::string toPortId, int toBit, int constValue)
+{
+ assert(nodeMap.count(toNodeId) != 0);
+ int toNodeIdx = nodeMap[toNodeId];
+ Node &toNode = nodes[toNodeIdx];
+
+ assert(toNode.portMap.count(toPortId) != 0);
+ int toPortIdx = toNode.portMap[toPortId];
+ Port &toPort = toNode.ports[toPortIdx];
+
+ assert(toBit >= 0 && toBit < int(toPort.bits.size()));
+ int toEdgeIdx = toPort.bits[toBit].edgeIdx;
+
+ assert(edges[toEdgeIdx].constValue == 0);
+ edges[toEdgeIdx].constValue = constValue;
+}
+
+void SubCircuit::Graph::createConstant(std::string toNodeId, std::string toPortId, int constValue)
+{
+ assert(nodeMap.count(toNodeId) != 0);
+ int toNodeIdx = nodeMap[toNodeId];
+ Node &toNode = nodes[toNodeIdx];
+
+ assert(toNode.portMap.count(toPortId) != 0);
+ int toPortIdx = toNode.portMap[toPortId];
+ Port &toPort = toNode.ports[toPortIdx];
+
+ for (int i = 0; i < int(toPort.bits.size()); i++) {
+ int toEdgeIdx = toPort.bits[i].edgeIdx;
+ assert(edges[toEdgeIdx].constValue == 0);
+ edges[toEdgeIdx].constValue = constValue % 2 ? '1' : '0';
+ constValue = constValue >> 1;
+ }
+}
+
+void SubCircuit::Graph::markExtern(std::string nodeId, std::string portId, int bit)
+{
+ assert(nodeMap.count(nodeId) != 0);
+ Node &node = nodes[nodeMap[nodeId]];
+
+ assert(node.portMap.count(portId) != 0);
+ Port &port = node.ports[node.portMap[portId]];
+
+ if (bit < 0) {
+ for (const auto portBit : port.bits)
+ edges[portBit.edgeIdx].isExtern = true;
+ } else {
+ assert(bit < int(port.bits.size()));
+ edges[port.bits[bit].edgeIdx].isExtern = true;
+ }
+}
+
+void SubCircuit::Graph::markAllExtern()
+{
+ allExtern = true;
+}
+
+void SubCircuit::Graph::print()
+{
+ for (int i = 0; i < int(nodes.size()); i++) {
+ const Node &node = nodes[i];
+ my_printf("NODE %d: %s (%s)\n", i, node.nodeId.c_str(), node.typeId.c_str());
+ for (int j = 0; j < int(node.ports.size()); j++) {
+ const Port &port = node.ports[j];
+ my_printf(" PORT %d: %s (%d/%d)\n", j, port.portId.c_str(), port.minWidth, int(port.bits.size()));
+ for (int k = 0; k < int(port.bits.size()); k++) {
+ int edgeIdx = port.bits[k].edgeIdx;
+ my_printf(" BIT %d (%d):", k, edgeIdx);
+ for (const auto &ref : edges[edgeIdx].portBits)
+ my_printf(" %d.%d.%d", ref.nodeIdx, ref.portIdx, ref.bitIdx);
+ if (edges[edgeIdx].isExtern)
+ my_printf(" [extern]");
+ my_printf("\n");
+ }
+ }
+ }
+}
+
+class SubCircuit::SolverWorker
+{
+ // basic internal data structures
+
+ typedef std::vector<std::map<int, int>> adjMatrix_t;
+
+ struct GraphData {
+ std::string graphId;
+ Graph graph;
+ adjMatrix_t adjMatrix;
+ std::vector<bool> usedNodes;
+ };
+
+ static void printAdjMatrix(const adjMatrix_t &matrix)
+ {
+ my_printf("%7s", "");
+ for (int i = 0; i < int(matrix.size()); i++)
+ my_printf("%4d:", i);
+ my_printf("\n");
+ for (int i = 0; i < int(matrix.size()); i++) {
+ my_printf("%5d:", i);
+ for (int j = 0; j < int(matrix.size()); j++)
+ if (matrix.at(i).count(j) == 0)
+ my_printf("%5s", "-");
+ else
+ my_printf("%5d", matrix.at(i).at(j));
+ my_printf("\n");
+ }
+ }
+
+ // helper functions for handling permutations
+
+ static const int maxPermutationsLimit = 1000000;
+
+ static int numberOfPermutations(const std::vector<std::string> &list)
+ {
+ int numPermutations = 1;
+ for (int i = 0; i < int(list.size()); i++) {
+ assert(numPermutations < maxPermutationsLimit);
+ numPermutations *= i+1;
+ }
+ return numPermutations;
+ }
+
+ static void permutateVectorToMap(std::map<std::string, std::string> &map, const std::vector<std::string> &list, int idx)
+ {
+ // convert idx to a list.size() digits factoradic number
+
+ std::vector<int> factoradicDigits;
+ for (int i = 0; i < int(list.size()); i++) {
+ factoradicDigits.push_back(idx % (i+1));
+ idx = idx / (i+1);
+ }
+
+ // construct permutation
+
+ std::vector<std::string> pool = list;
+ std::vector<std::string> permutation;
+
+ while (!factoradicDigits.empty()) {
+ int i = factoradicDigits.back();
+ factoradicDigits.pop_back();
+ permutation.push_back(pool[i]);
+ pool.erase(pool.begin() + i);
+ }
+
+ // update map
+
+ for (int i = 0; i < int(list.size()); i++)
+ map[list[i]] = permutation[i];
+ }
+
+ static int numberOfPermutationsArray(const std::vector<std::vector<std::string>> &list)
+ {
+ int numPermutations = 1;
+ for (const auto &it : list) {
+ int thisPermutations = numberOfPermutations(it);
+ assert(float(numPermutations) * float(thisPermutations) < maxPermutationsLimit);
+ numPermutations *= thisPermutations;
+ }
+ return numPermutations;
+ }
+
+ static void permutateVectorToMapArray(std::map<std::string, std::string> &map, const std::vector<std::vector<std::string>> &list, int idx)
+ {
+ for (const auto &it : list) {
+ int thisPermutations = numberOfPermutations(it);
+ int thisIdx = idx % thisPermutations;
+ permutateVectorToMap(map, it, thisIdx);
+ idx /= thisPermutations;
+ }
+ }
+
+ static void applyPermutation(std::map<std::string, std::string> &map, const std::map<std::string, std::string> &permutation)
+ {
+ std::vector<std::pair<std::string, std::string>> changeLog;
+ for (const auto &it : permutation)
+ if (map.count(it.second))
+ changeLog.push_back(std::pair<std::string, std::string>(it.first, map.at(it.second)));
+ else
+ changeLog.push_back(std::pair<std::string, std::string>(it.first, it.second));
+ for (const auto &it : changeLog)
+ map[it.first] = it.second;
+ }
+
+ // classes for internal digraph representation
+
+ struct DiBit
+ {
+ std::string fromPort, toPort;
+ int fromBit, toBit;
+
+ DiBit() : fromPort(), toPort(), fromBit(-1), toBit(-1) { }
+ DiBit(std::string fromPort, int fromBit, std::string toPort, int toBit) : fromPort(fromPort), toPort(toPort), fromBit(fromBit), toBit(toBit) { }
+
+ bool operator < (const DiBit &other) const
+ {
+ if (fromPort != other.fromPort)
+ return fromPort < other.fromPort;
+ if (toPort != other.toPort)
+ return toPort < other.toPort;
+ if (fromBit != other.fromBit)
+ return fromBit < other.fromBit;
+ return toBit < other.toBit;
+ }
+
+ std::string toString() const
+ {
+ return my_stringf("%s[%d]:%s[%d]", fromPort.c_str(), fromBit, toPort.c_str(), toBit);
+ }
+ };
+
+ struct DiNode
+ {
+ std::string typeId;
+ std::map<std::string, int> portSizes;
+
+ DiNode()
+ {
+ }
+
+ DiNode(const Graph &graph, int nodeIdx)
+ {
+ const Graph::Node &node = graph.nodes.at(nodeIdx);
+ typeId = node.typeId;
+ for (const auto &port : node.ports)
+ portSizes[port.portId] = port.bits.size();
+ }
+
+ bool operator < (const DiNode &other) const
+ {
+ if (typeId != other.typeId)
+ return typeId < other.typeId;
+ return portSizes < other.portSizes;
+ }
+
+ std::string toString() const
+ {
+ std::string str;
+ bool firstPort = true;
+ for (const auto &it : portSizes) {
+ str += my_stringf("%s%s[%d]", firstPort ? "" : ",", it.first.c_str(), it.second);
+ firstPort = false;
+ }
+ return typeId + "(" + str + ")";
+ }
+ };
+
+ struct DiEdge
+ {
+ DiNode fromNode, toNode;
+ std::set<DiBit> bits;
+ std::string userAnnotation;
+
+ bool operator < (const DiEdge &other) const
+ {
+ if (fromNode < other.fromNode || other.fromNode < fromNode)
+ return fromNode < other.fromNode;
+ if (toNode < other.toNode || other.toNode < toNode)
+ return toNode < other.toNode;
+ if (bits < other.bits || other.bits < bits)
+ return bits < other.bits;
+ return userAnnotation < other.userAnnotation;
+ }
+
+ bool compare(const DiEdge &other, const std::map<std::string, std::string> &mapFromPorts, const std::map<std::string, std::string> &mapToPorts) const
+ {
+ // Rules for matching edges:
+ //
+ // For all bits in the needle edge:
+ // - ignore if needle ports don't exist in haystack edge
+ // - otherwise: matching bit in haystack edge must exist
+ //
+ // There is no need to check in the other direction, as checking
+ // of the isExtern properties is already performed in node matching.
+ //
+ // Note: "this" is needle, "other" is haystack
+
+ for (auto bit : bits)
+ {
+ if (mapFromPorts.count(bit.fromPort) > 0)
+ bit.fromPort = mapFromPorts.at(bit.fromPort);
+ if (mapToPorts.count(bit.toPort) > 0)
+ bit.toPort = mapToPorts.at(bit.toPort);
+
+ if (other.fromNode.portSizes.count(bit.fromPort) == 0)
+ continue;
+ if (other.toNode.portSizes.count(bit.toPort) == 0)
+ continue;
+
+ if (bit.fromBit >= other.fromNode.portSizes.at(bit.fromPort))
+ continue;
+ if (bit.toBit >= other.toNode.portSizes.at(bit.toPort))
+ continue;
+
+ if (other.bits.count(bit) == 0)
+ return false;
+ }
+
+ return true;
+ }
+
+ bool compareWithFromAndToPermutations(const DiEdge &other, const std::map<std::string, std::string> &mapFromPorts, const std::map<std::string, std::string> &mapToPorts,
+ const std::map<std::string, std::set<std::map<std::string, std::string>>> &swapPermutations) const
+ {
+ if (swapPermutations.count(fromNode.typeId) > 0)
+ for (const auto &permutation : swapPermutations.at(fromNode.typeId)) {
+ std::map<std::string, std::string> thisMapFromPorts = mapFromPorts;
+ applyPermutation(thisMapFromPorts, permutation);
+ if (compareWithToPermutations(other, thisMapFromPorts, mapToPorts, swapPermutations))
+ return true;
+ }
+ return compareWithToPermutations(other, mapFromPorts, mapToPorts, swapPermutations);
+ }
+
+ bool compareWithToPermutations(const DiEdge &other, const std::map<std::string, std::string> &mapFromPorts, const std::map<std::string, std::string> &mapToPorts,
+ const std::map<std::string, std::set<std::map<std::string, std::string>>> &swapPermutations) const
+ {
+ if (swapPermutations.count(toNode.typeId) > 0)
+ for (const auto &permutation : swapPermutations.at(toNode.typeId)) {
+ std::map<std::string, std::string> thisMapToPorts = mapToPorts;
+ applyPermutation(thisMapToPorts, permutation);
+ if (compare(other, mapFromPorts, thisMapToPorts))
+ return true;
+ }
+ return compare(other, mapFromPorts, mapToPorts);
+ }
+
+ bool compare(const DiEdge &other, const std::map<std::string, std::set<std::set<std::string>>> &swapPorts,
+ const std::map<std::string, std::set<std::map<std::string, std::string>>> &swapPermutations) const
+ {
+ // brute force method for port swapping: try all variations
+
+ std::vector<std::vector<std::string>> swapFromPorts;
+ std::vector<std::vector<std::string>> swapToPorts;
+
+ // only use groups that are relevant for this edge
+
+ if (swapPorts.count(fromNode.typeId) > 0)
+ for (const auto &ports : swapPorts.at(fromNode.typeId)) {
+ for (const auto &bit : bits)
+ if (ports.count(bit.fromPort))
+ goto foundFromPortMatch;
+ if (0) {
+ foundFromPortMatch:
+ std::vector<std::string> portsVector;
+ for (const auto &port : ports)
+ portsVector.push_back(port);
+ swapFromPorts.push_back(portsVector);
+ }
+ }
+
+ if (swapPorts.count(toNode.typeId) > 0)
+ for (const auto &ports : swapPorts.at(toNode.typeId)) {
+ for (const auto &bit : bits)
+ if (ports.count(bit.toPort))
+ goto foundToPortMatch;
+ if (0) {
+ foundToPortMatch:
+ std::vector<std::string> portsVector;
+ for (const auto &port : ports)
+ portsVector.push_back(port);
+ swapToPorts.push_back(portsVector);
+ }
+ }
+
+ // try all permutations
+
+ std::map<std::string, std::string> mapFromPorts, mapToPorts;
+ int fromPortsPermutations = numberOfPermutationsArray(swapFromPorts);
+ int toPortsPermutations = numberOfPermutationsArray(swapToPorts);
+
+ for (int i = 0; i < fromPortsPermutations; i++)
+ {
+ permutateVectorToMapArray(mapFromPorts, swapFromPorts, i);
+
+ for (int j = 0; j < toPortsPermutations; j++) {
+ permutateVectorToMapArray(mapToPorts, swapToPorts, j);
+ if (compareWithFromAndToPermutations(other, mapFromPorts, mapToPorts, swapPermutations))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool compare(const DiEdge &other, const std::map<std::string, std::string> &mapFromPorts, const std::map<std::string, std::set<std::set<std::string>>> &swapPorts,
+ const std::map<std::string, std::set<std::map<std::string, std::string>>> &swapPermutations) const
+ {
+ // strip-down version of the last function: only try permutations for mapToPorts, mapFromPorts is already provided by the caller
+
+ std::vector<std::vector<std::string>> swapToPorts;
+
+ if (swapPorts.count(toNode.typeId) > 0)
+ for (const auto &ports : swapPorts.at(toNode.typeId)) {
+ for (const auto &bit : bits)
+ if (ports.count(bit.toPort))
+ goto foundToPortMatch;
+ if (0) {
+ foundToPortMatch:
+ std::vector<std::string> portsVector;
+ for (const auto &port : ports)
+ portsVector.push_back(port);
+ swapToPorts.push_back(portsVector);
+ }
+ }
+
+ std::map<std::string, std::string> mapToPorts;
+ int toPortsPermutations = numberOfPermutationsArray(swapToPorts);
+
+ for (int j = 0; j < toPortsPermutations; j++) {
+ permutateVectorToMapArray(mapToPorts, swapToPorts, j);
+ if (compareWithToPermutations(other, mapFromPorts, mapToPorts, swapPermutations))
+ return true;
+ }
+
+ return false;
+ }
+
+ std::string toString() const
+ {
+ std::string buffer = fromNode.toString() + " " + toNode.toString();
+ for (const auto &bit : bits)
+ buffer += " " + bit.toString();
+ if (!userAnnotation.empty())
+ buffer += " " + userAnnotation;
+ return buffer;
+ }
+
+ static void findEdgesInGraph(const Graph &graph, std::map<std::pair<int, int>, DiEdge> &edges)
+ {
+ edges.clear();
+ for (const auto &edge : graph.edges) {
+ if (edge.constValue != 0)
+ continue;
+ for (const auto &fromBit : edge.portBits)
+ for (const auto &toBit : edge.portBits)
+ if (&fromBit != &toBit) {
+ DiEdge &de = edges[std::pair<int, int>(fromBit.nodeIdx, toBit.nodeIdx)];
+ de.fromNode = DiNode(graph, fromBit.nodeIdx);
+ de.toNode = DiNode(graph, toBit.nodeIdx);
+ std::string fromPortId = graph.nodes[fromBit.nodeIdx].ports[fromBit.portIdx].portId;
+ std::string toPortId = graph.nodes[toBit.nodeIdx].ports[toBit.portIdx].portId;
+ de.bits.insert(DiBit(fromPortId, fromBit.bitIdx, toPortId, toBit.bitIdx));
+ }
+ }
+ }
+ };
+
+ struct DiCache
+ {
+ std::map<DiEdge, int> edgeTypesMap;
+ std::vector<DiEdge> edgeTypes;
+ std::map<std::pair<int, int>, bool> compareCache;
+
+ void add(const Graph &graph, adjMatrix_t &adjMatrix, const std::string &graphId, Solver *userSolver)
+ {
+ std::map<std::pair<int, int>, DiEdge> edges;
+ DiEdge::findEdgesInGraph(graph, edges);
+
+ adjMatrix.clear();
+ adjMatrix.resize(graph.nodes.size());
+
+ for (auto &it : edges) {
+ const Graph::Node &fromNode = graph.nodes[it.first.first];
+ const Graph::Node &toNode = graph.nodes[it.first.second];
+ it.second.userAnnotation = userSolver->userAnnotateEdge(graphId, fromNode.nodeId, fromNode.userData, toNode.nodeId, toNode.userData);
+ }
+
+ for (const auto &it : edges) {
+ if (edgeTypesMap.count(it.second) == 0) {
+ edgeTypesMap[it.second] = edgeTypes.size();
+ edgeTypes.push_back(it.second);
+ }
+ adjMatrix[it.first.first][it.first.second] = edgeTypesMap[it.second];
+ }
+ }
+
+ bool compare(int needleEdge, int haystackEdge, const std::map<std::string, std::set<std::set<std::string>>> &swapPorts,
+ const std::map<std::string, std::set<std::map<std::string, std::string>>> &swapPermutations)
+ {
+ std::pair<int, int> key(needleEdge, haystackEdge);
+ if (!compareCache.count(key))
+ compareCache[key] = edgeTypes.at(needleEdge).compare(edgeTypes.at(haystackEdge), swapPorts, swapPermutations);
+ return compareCache[key];
+ }
+
+ bool compare(int needleEdge, int haystackEdge, const std::map<std::string, std::string> &mapFromPorts, const std::map<std::string, std::set<std::set<std::string>>> &swapPorts,
+ const std::map<std::string, std::set<std::map<std::string, std::string>>> &swapPermutations) const
+ {
+ return edgeTypes.at(needleEdge).compare(edgeTypes.at(haystackEdge), mapFromPorts, swapPorts, swapPermutations);
+ }
+
+ bool compare(int needleEdge, int haystackEdge, const std::map<std::string, std::string> &mapFromPorts, const std::map<std::string, std::string> &mapToPorts) const
+ {
+ return edgeTypes.at(needleEdge).compare(edgeTypes.at(haystackEdge), mapFromPorts, mapToPorts);
+ }
+
+ void printEdgeTypes() const
+ {
+ for (int i = 0; i < int(edgeTypes.size()); i++)
+ my_printf("%5d: %s\n", i, edgeTypes[i].toString().c_str());
+ }
+ };
+
+ // solver state variables
+
+ Solver *userSolver;
+ std::map<std::string, GraphData> graphData;
+ std::map<std::string, std::set<std::string>> compatibleTypes;
+ std::map<int, std::set<int>> compatibleConstants;
+ std::map<std::string, std::set<std::set<std::string>>> swapPorts;
+ std::map<std::string, std::set<std::map<std::string, std::string>>> swapPermutations;
+ DiCache diCache;
+ bool verbose;
+
+ // main solver functions
+
+ bool matchNodePorts(const Graph &needle, int needleNodeIdx, const Graph &haystack, int haystackNodeIdx, const std::map<std::string, std::string> &swaps) const
+ {
+ const Graph::Node &nn = needle.nodes[needleNodeIdx];
+ const Graph::Node &hn = haystack.nodes[haystackNodeIdx];
+ assert(nn.ports.size() == hn.ports.size());
+
+ for (int i = 0; i < int(nn.ports.size()); i++)
+ {
+ std::string hnPortId = nn.ports[i].portId;
+ if (swaps.count(hnPortId) > 0)
+ hnPortId = swaps.at(hnPortId);
+
+ if (hn.portMap.count(hnPortId) == 0)
+ return false;
+
+ const Graph::Port &np = nn.ports[i];
+ const Graph::Port &hp = hn.ports[hn.portMap.at(hnPortId)];
+
+ if (int(hp.bits.size()) < np.minWidth || hp.bits.size() > np.bits.size())
+ return false;
+
+ for (int j = 0; j < int(hp.bits.size()); j++)
+ {
+ const Graph::Edge &ne = needle.edges[np.bits[j].edgeIdx];
+ const Graph::Edge &he = haystack.edges[hp.bits[j].edgeIdx];
+
+ if (ne.constValue || he.constValue) {
+ if (ne.constValue != he.constValue)
+ if (compatibleConstants.count(ne.constValue) == 0 || compatibleConstants.at(ne.constValue).count(he.constValue) == 0)
+ return false;
+ continue;
+ }
+
+ if (ne.isExtern || needle.allExtern) {
+ if (he.portBits.size() < ne.portBits.size())
+ return false;
+ } else {
+ if (he.portBits.size() != ne.portBits.size())
+ return false;
+ if (he.isExtern || haystack.allExtern)
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ bool matchNodes(const GraphData &needle, int needleNodeIdx, const GraphData &haystack, int haystackNodeIdx) const
+ {
+ // Rules for matching nodes:
+ //
+ // 1. their typeId must be identical or compatible
+ // (this is checked before calling this function)
+ //
+ // 2. they must have the same ports and the haystack port
+ // widths must match the needle port width range
+ //
+ // 3. All edges from the needle must match the haystack:
+ // a) if the needle edge is extern:
+ // - the haystack edge must have at least as many components as the needle edge
+ // b) if the needle edge is not extern:
+ // - the haystack edge must have the same number of components as the needle edge
+ // - the haystack edge must not be extern
+
+ const Graph::Node &nn = needle.graph.nodes[needleNodeIdx];
+ const Graph::Node &hn = haystack.graph.nodes[haystackNodeIdx];
+
+ assert(nn.typeId == hn.typeId || (compatibleTypes.count(nn.typeId) > 0 && compatibleTypes.at(nn.typeId).count(hn.typeId) > 0));
+
+ if (nn.ports.size() != hn.ports.size())
+ return false;
+
+ std::map<std::string, std::string> currentCandidate;
+
+ for (const auto &port : needle.graph.nodes[needleNodeIdx].ports)
+ currentCandidate[port.portId] = port.portId;
+
+ if (swapPorts.count(needle.graph.nodes[needleNodeIdx].typeId) == 0)
+ {
+ if (matchNodePorts(needle.graph, needleNodeIdx, haystack.graph, haystackNodeIdx, currentCandidate) &&
+ userSolver->userCompareNodes(needle.graphId, nn.nodeId, nn.userData, haystack.graphId, hn.nodeId, hn.userData, currentCandidate))
+ return true;
+
+ if (swapPermutations.count(needle.graph.nodes[needleNodeIdx].typeId) > 0)
+ for (const auto &permutation : swapPermutations.at(needle.graph.nodes[needleNodeIdx].typeId)) {
+ std::map<std::string, std::string> currentSubCandidate = currentCandidate;
+ applyPermutation(currentSubCandidate, permutation);
+ if (matchNodePorts(needle.graph, needleNodeIdx, haystack.graph, haystackNodeIdx, currentCandidate) &&
+ userSolver->userCompareNodes(needle.graphId, nn.nodeId, nn.userData, haystack.graphId, hn.nodeId, hn.userData, currentCandidate))
+ return true;
+ }
+ }
+ else
+ {
+ std::vector<std::vector<std::string>> thisSwapPorts;
+ for (const auto &ports : swapPorts.at(needle.graph.nodes[needleNodeIdx].typeId)) {
+ std::vector<std::string> portsVector;
+ for (const auto &port : ports)
+ portsVector.push_back(port);
+ thisSwapPorts.push_back(portsVector);
+ }
+
+ int thisPermutations = numberOfPermutationsArray(thisSwapPorts);
+ for (int i = 0; i < thisPermutations; i++)
+ {
+ permutateVectorToMapArray(currentCandidate, thisSwapPorts, i);
+
+ if (matchNodePorts(needle.graph, needleNodeIdx, haystack.graph, haystackNodeIdx, currentCandidate) &&
+ userSolver->userCompareNodes(needle.graphId, nn.nodeId, nn.userData, haystack.graphId, hn.nodeId, hn.userData, currentCandidate))
+ return true;
+
+ if (swapPermutations.count(needle.graph.nodes[needleNodeIdx].typeId) > 0)
+ for (const auto &permutation : swapPermutations.at(needle.graph.nodes[needleNodeIdx].typeId)) {
+ std::map<std::string, std::string> currentSubCandidate = currentCandidate;
+ applyPermutation(currentSubCandidate, permutation);
+ if (matchNodePorts(needle.graph, needleNodeIdx, haystack.graph, haystackNodeIdx, currentCandidate) &&
+ userSolver->userCompareNodes(needle.graphId, nn.nodeId, nn.userData, haystack.graphId, hn.nodeId, hn.userData, currentCandidate))
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ void generateEnumerationMatrix(std::vector<std::set<int>> &enumerationMatrix, const GraphData &needle, const GraphData &haystack, const std::map<std::string, std::set<std::string>> &initialMappings) const
+ {
+ std::map<std::string, std::set<int>> haystackNodesByTypeId;
+ for (int i = 0; i < int(haystack.graph.nodes.size()); i++)
+ haystackNodesByTypeId[haystack.graph.nodes[i].typeId].insert(i);
+
+ enumerationMatrix.clear();
+ enumerationMatrix.resize(needle.graph.nodes.size());
+ for (int i = 0; i < int(needle.graph.nodes.size()); i++)
+ {
+ const Graph::Node &nn = needle.graph.nodes[i];
+
+ for (int j : haystackNodesByTypeId[nn.typeId]) {
+ const Graph::Node &hn = haystack.graph.nodes[j];
+ if (initialMappings.count(nn.nodeId) > 0 && initialMappings.at(nn.nodeId).count(hn.nodeId) == 0)
+ continue;
+ if (!matchNodes(needle, i, haystack, j))
+ continue;
+ enumerationMatrix[i].insert(j);
+ }
+
+ if (compatibleTypes.count(nn.typeId) > 0)
+ for (const std::string &compatibleTypeId : compatibleTypes.at(nn.typeId))
+ for (int j : haystackNodesByTypeId[compatibleTypeId]) {
+ const Graph::Node &hn = haystack.graph.nodes[j];
+ if (initialMappings.count(nn.nodeId) > 0 && initialMappings.at(nn.nodeId).count(hn.nodeId) == 0)
+ continue;
+ if (!matchNodes(needle, i, haystack, j))
+ continue;
+ enumerationMatrix[i].insert(j);
+ }
+ }
+ }
+
+ bool checkEnumerationMatrix(std::vector<std::set<int>> &enumerationMatrix, int i, int j, const GraphData &needle, const GraphData &haystack)
+ {
+ for (const auto &it_needle : needle.adjMatrix.at(i))
+ {
+ int needleNeighbour = it_needle.first;
+ int needleEdgeType = it_needle.second;
+
+ for (int haystackNeighbour : enumerationMatrix[needleNeighbour])
+ if (haystack.adjMatrix.at(j).count(haystackNeighbour) > 0) {
+ int haystackEdgeType = haystack.adjMatrix.at(j).at(haystackNeighbour);
+ if (diCache.compare(needleEdgeType, haystackEdgeType, swapPorts, swapPermutations)) {
+ const Graph::Node &needleFromNode = needle.graph.nodes[i];
+ const Graph::Node &needleToNode = needle.graph.nodes[needleNeighbour];
+ const Graph::Node &haystackFromNode = haystack.graph.nodes[j];
+ const Graph::Node &haystackToNode = haystack.graph.nodes[haystackNeighbour];
+ if (userSolver->userCompareEdge(needle.graphId, needleFromNode.nodeId, needleFromNode.userData, needleToNode.nodeId, needleToNode.userData,
+ haystack.graphId, haystackFromNode.nodeId, haystackFromNode.userData, haystackToNode.nodeId, haystackToNode.userData))
+ goto found_match;
+ }
+ }
+
+ return false;
+ found_match:;
+ }
+
+ return true;
+ }
+
+ bool pruneEnumerationMatrix(std::vector<std::set<int>> &enumerationMatrix, const GraphData &needle, const GraphData &haystack, int &nextRow, bool allowOverlap)
+ {
+ bool didSomething = true;
+ while (didSomething)
+ {
+ nextRow = -1;
+ didSomething = false;
+ for (int i = 0; i < int(enumerationMatrix.size()); i++) {
+ std::set<int> newRow;
+ for (int j : enumerationMatrix[i]) {
+ if (!checkEnumerationMatrix(enumerationMatrix, i, j, needle, haystack))
+ didSomething = true;
+ else if (!allowOverlap && haystack.usedNodes[j])
+ didSomething = true;
+ else
+ newRow.insert(j);
+ }
+ if (newRow.size() == 0)
+ return false;
+ if (newRow.size() >= 2 && (nextRow < 0 || needle.adjMatrix.at(nextRow).size() < needle.adjMatrix.at(i).size()))
+ nextRow = i;
+ enumerationMatrix[i].swap(newRow);
+ }
+ }
+ return true;
+ }
+
+ void printEnumerationMatrix(const std::vector<std::set<int>> &enumerationMatrix, int maxHaystackNodeIdx = -1) const
+ {
+ if (maxHaystackNodeIdx < 0) {
+ for (const auto &it : enumerationMatrix)
+ for (int idx : it)
+ maxHaystackNodeIdx = std::max(maxHaystackNodeIdx, idx);
+ }
+
+ my_printf(" ");
+ for (int j = 0; j < maxHaystackNodeIdx; j += 5)
+ my_printf("%-6d", j);
+ my_printf("\n");
+
+ for (int i = 0; i < int(enumerationMatrix.size()); i++)
+ {
+ my_printf("%5d:", i);
+ for (int j = 0; j < maxHaystackNodeIdx; j++) {
+ if (j % 5 == 0)
+ my_printf(" ");
+ my_printf("%c", enumerationMatrix[i].count(j) > 0 ? '*' : '.');
+ }
+ my_printf("\n");
+ }
+ }
+
+ bool checkPortmapCandidate(const std::vector<std::set<int>> &enumerationMatrix, const GraphData &needle, const GraphData &haystack, int idx, const std::map<std::string, std::string> &currentCandidate)
+ {
+ assert(enumerationMatrix[idx].size() == 1);
+ int idxHaystack = *enumerationMatrix[idx].begin();
+
+ const Graph::Node &nn = needle.graph.nodes[idx];
+ const Graph::Node &hn = haystack.graph.nodes[idxHaystack];
+
+ if (!matchNodePorts(needle.graph, idx, haystack.graph, idxHaystack, currentCandidate) ||
+ !userSolver->userCompareNodes(needle.graphId, nn.nodeId, nn.userData, haystack.graphId, hn.nodeId, hn.userData, currentCandidate))
+ return false;
+
+ for (const auto &it_needle : needle.adjMatrix.at(idx))
+ {
+ int needleNeighbour = it_needle.first;
+ int needleEdgeType = it_needle.second;
+
+ assert(enumerationMatrix[needleNeighbour].size() == 1);
+ int haystackNeighbour = *enumerationMatrix[needleNeighbour].begin();
+
+ assert(haystack.adjMatrix.at(idxHaystack).count(haystackNeighbour) > 0);
+ int haystackEdgeType = haystack.adjMatrix.at(idxHaystack).at(haystackNeighbour);
+ if (!diCache.compare(needleEdgeType, haystackEdgeType, currentCandidate, swapPorts, swapPermutations))
+ return false;
+ }
+
+ return true;
+ }
+
+ void generatePortmapCandidates(std::set<std::map<std::string, std::string>> &portmapCandidates, const std::vector<std::set<int>> &enumerationMatrix,
+ const GraphData &needle, const GraphData &haystack, int idx)
+ {
+ std::map<std::string, std::string> currentCandidate;
+
+ for (const auto &port : needle.graph.nodes[idx].ports)
+ currentCandidate[port.portId] = port.portId;
+
+ if (swapPorts.count(needle.graph.nodes[idx].typeId) == 0)
+ {
+ if (checkPortmapCandidate(enumerationMatrix, needle, haystack, idx, currentCandidate))
+ portmapCandidates.insert(currentCandidate);
+
+ if (swapPermutations.count(needle.graph.nodes[idx].typeId) > 0)
+ for (const auto &permutation : swapPermutations.at(needle.graph.nodes[idx].typeId)) {
+ std::map<std::string, std::string> currentSubCandidate = currentCandidate;
+ applyPermutation(currentSubCandidate, permutation);
+ if (checkPortmapCandidate(enumerationMatrix, needle, haystack, idx, currentSubCandidate))
+ portmapCandidates.insert(currentSubCandidate);
+ }
+ }
+ else
+ {
+ std::vector<std::vector<std::string>> thisSwapPorts;
+ for (const auto &ports : swapPorts.at(needle.graph.nodes[idx].typeId)) {
+ std::vector<std::string> portsVector;
+ for (const auto &port : ports)
+ portsVector.push_back(port);
+ thisSwapPorts.push_back(portsVector);
+ }
+
+ int thisPermutations = numberOfPermutationsArray(thisSwapPorts);
+ for (int i = 0; i < thisPermutations; i++)
+ {
+ permutateVectorToMapArray(currentCandidate, thisSwapPorts, i);
+
+ if (checkPortmapCandidate(enumerationMatrix, needle, haystack, idx, currentCandidate))
+ portmapCandidates.insert(currentCandidate);
+
+ if (swapPermutations.count(needle.graph.nodes[idx].typeId) > 0)
+ for (const auto &permutation : swapPermutations.at(needle.graph.nodes[idx].typeId)) {
+ std::map<std::string, std::string> currentSubCandidate = currentCandidate;
+ applyPermutation(currentSubCandidate, permutation);
+ if (checkPortmapCandidate(enumerationMatrix, needle, haystack, idx, currentSubCandidate))
+ portmapCandidates.insert(currentSubCandidate);
+ }
+ }
+ }
+ }
+
+ bool prunePortmapCandidates(std::vector<std::set<std::map<std::string, std::string>>> &portmapCandidates, std::vector<std::set<int>> enumerationMatrix, const GraphData &needle, const GraphData &haystack)
+ {
+ bool didSomething = false;
+
+ // strategy #1: prune impossible port mappings
+
+ for (int i = 0; i < int(needle.graph.nodes.size()); i++)
+ {
+ assert(enumerationMatrix[i].size() == 1);
+ int j = *enumerationMatrix[i].begin();
+
+ std::set<std::map<std::string, std::string>> thisCandidates;
+ portmapCandidates[i].swap(thisCandidates);
+
+ for (const auto &testCandidate : thisCandidates)
+ {
+ for (const auto &it_needle : needle.adjMatrix.at(i))
+ {
+ int needleNeighbour = it_needle.first;
+ int needleEdgeType = it_needle.second;
+
+ assert(enumerationMatrix[needleNeighbour].size() == 1);
+ int haystackNeighbour = *enumerationMatrix[needleNeighbour].begin();
+
+ assert(haystack.adjMatrix.at(j).count(haystackNeighbour) > 0);
+ int haystackEdgeType = haystack.adjMatrix.at(j).at(haystackNeighbour);
+
+ std::set<std::map<std::string, std::string>> &candidates =
+ i == needleNeighbour ? thisCandidates : portmapCandidates[needleNeighbour];
+
+ for (const auto &otherCandidate : candidates) {
+ if (diCache.compare(needleEdgeType, haystackEdgeType, testCandidate, otherCandidate))
+ goto found_match;
+ }
+
+ didSomething = true;
+ goto purgeCandidate;
+ found_match:;
+ }
+
+ portmapCandidates[i].insert(testCandidate);
+ purgeCandidate:;
+ }
+
+ if (portmapCandidates[i].size() == 0)
+ return false;
+ }
+
+ if (didSomething)
+ return true;
+
+ // strategy #2: prune a single random port mapping
+
+ for (int i = 0; i < int(needle.graph.nodes.size()); i++)
+ if (portmapCandidates[i].size() > 1) {
+ // remove last mapping. this keeps ports unswapped in don't-care situations
+ portmapCandidates[i].erase(--portmapCandidates[i].end());
+ return true;
+ }
+
+ return false;
+ }
+
+ void ullmannRecursion(std::vector<Solver::Result> &results, std::vector<std::set<int>> &enumerationMatrix, int iter, const GraphData &needle, GraphData &haystack, bool allowOverlap, int limitResults)
+ {
+ int i = -1;
+ if (!pruneEnumerationMatrix(enumerationMatrix, needle, haystack, i, allowOverlap))
+ return;
+
+ if (i < 0)
+ {
+ Solver::Result result;
+ result.needleGraphId = needle.graphId;
+ result.haystackGraphId = haystack.graphId;
+
+ std::vector<std::set<std::map<std::string, std::string>>> portmapCandidates;
+ portmapCandidates.resize(enumerationMatrix.size());
+
+ for (int j = 0; j < int(enumerationMatrix.size()); j++) {
+ Solver::ResultNodeMapping mapping;
+ mapping.needleNodeId = needle.graph.nodes[j].nodeId;
+ mapping.needleUserData = needle.graph.nodes[j].userData;
+ mapping.haystackNodeId = haystack.graph.nodes[*enumerationMatrix[j].begin()].nodeId;
+ mapping.haystackUserData = haystack.graph.nodes[*enumerationMatrix[j].begin()].userData;
+ generatePortmapCandidates(portmapCandidates[j], enumerationMatrix, needle, haystack, j);
+ result.mappings[needle.graph.nodes[j].nodeId] = mapping;
+ }
+
+ while (prunePortmapCandidates(portmapCandidates, enumerationMatrix, needle, haystack)) { }
+
+ if (verbose) {
+ my_printf("\nPortmapper results:\n");
+ for (int j = 0; j < int(enumerationMatrix.size()); j++) {
+ my_printf("%5d: %s\n", j, needle.graph.nodes[j].nodeId.c_str());
+ int variantCounter = 0;
+ for (auto &i2 : portmapCandidates.at(j)) {
+ my_printf("%*s variant %2d:", 6, "", variantCounter++);
+ int mapCounter = 0;
+ for (auto &i3 : i2)
+ my_printf("%s %s -> %s", mapCounter++ ? "," : "", i3.first.c_str(), i3.second.c_str());
+ my_printf("\n");
+ }
+ }
+ }
+
+ for (int j = 0; j < int(enumerationMatrix.size()); j++) {
+ if (portmapCandidates[j].size() == 0) {
+ if (verbose) {
+ my_printf("\nSolution (rejected by portmapper):\n");
+ printEnumerationMatrix(enumerationMatrix, haystack.graph.nodes.size());
+ }
+ return;
+ }
+ result.mappings[needle.graph.nodes[j].nodeId].portMapping = *portmapCandidates[j].begin();
+ }
+
+ if (!userSolver->userCheckSolution(result)) {
+ if (verbose) {
+ my_printf("\nSolution (rejected by userCheckSolution):\n");
+ printEnumerationMatrix(enumerationMatrix, haystack.graph.nodes.size());
+ }
+ return;
+ }
+
+ for (int j = 0; j < int(enumerationMatrix.size()); j++)
+ if (!haystack.graph.nodes[*enumerationMatrix[j].begin()].shared)
+ haystack.usedNodes[*enumerationMatrix[j].begin()] = true;
+
+ if (verbose) {
+ my_printf("\nSolution:\n");
+ printEnumerationMatrix(enumerationMatrix, haystack.graph.nodes.size());
+ }
+
+ results.push_back(result);
+ return;
+ }
+
+ if (verbose) {
+ my_printf("\n");
+ my_printf("Enumeration Matrix at recursion level %d (%d):\n", iter, i);
+ printEnumerationMatrix(enumerationMatrix, haystack.graph.nodes.size());
+ }
+
+ std::set<int> activeRow;
+ enumerationMatrix[i].swap(activeRow);
+
+ for (int j : activeRow)
+ {
+ // found enough?
+ if (limitResults >= 0 && int(results.size()) >= limitResults)
+ return;
+
+ // already used by other solution -> try next
+ if (!allowOverlap && haystack.usedNodes[j])
+ continue;
+
+ // create enumeration matrix for child in recursion tree
+ std::vector<std::set<int>> nextEnumerationMatrix = enumerationMatrix;
+ for (int k = 0; k < int(nextEnumerationMatrix.size()); k++)
+ nextEnumerationMatrix[k].erase(j);
+ nextEnumerationMatrix[i].insert(j);
+
+ // recursion
+ ullmannRecursion(results, nextEnumerationMatrix, iter+1, needle, haystack, allowOverlap, limitResults);
+
+ // we just have found something -> unroll to top recursion level
+ if (!allowOverlap && haystack.usedNodes[j] && iter > 0)
+ return;
+ }
+ }
+
+ // additional data structes and functions for mining
+
+ struct NodeSet {
+ std::string graphId;
+ std::set<int> nodes;
+ NodeSet(std::string graphId, int node1, int node2) {
+ this->graphId = graphId;
+ nodes.insert(node1);
+ nodes.insert(node2);
+ }
+ NodeSet(std::string graphId, const std::vector<int> &nodes) {
+ this->graphId = graphId;
+ for (int node : nodes)
+ this->nodes.insert(node);
+ }
+ void extend(const NodeSet &other) {
+ assert(this->graphId == other.graphId);
+ for (int node : other.nodes)
+ nodes.insert(node);
+ }
+ int extendCandidate(const NodeSet &other) const {
+ if (graphId != other.graphId)
+ return 0;
+ int newNodes = 0;
+ bool intersect = false;
+ for (int node : other.nodes)
+ if (nodes.count(node) > 0)
+ intersect = true;
+ else
+ newNodes++;
+ return intersect ? newNodes : 0;
+ }
+ bool operator <(const NodeSet &other) const {
+ if (graphId != other.graphId)
+ return graphId < other.graphId;
+ return nodes < other.nodes;
+ }
+ std::string to_string() const {
+ std::string str = graphId + "(";
+ bool first = true;
+ for (int node : nodes) {
+ str += my_stringf("%s%d", first ? "" : " ", node);
+ first = false;
+ }
+ return str + ")";
+ }
+ };
+
+ void solveForMining(std::vector<Solver::Result> &results, const GraphData &needle)
+ {
+ bool backupVerbose = verbose;
+ verbose = false;
+
+ for (auto &it : graphData)
+ {
+ GraphData &haystack = it.second;
+
+ std::vector<std::set<int>> enumerationMatrix;
+ std::map<std::string, std::set<std::string>> initialMappings;
+ generateEnumerationMatrix(enumerationMatrix, needle, haystack, initialMappings);
+
+ haystack.usedNodes.resize(haystack.graph.nodes.size());
+ ullmannRecursion(results, enumerationMatrix, 0, needle, haystack, true, -1);
+ }
+
+ verbose = backupVerbose;
+ }
+
+ int testForMining(std::vector<Solver::MineResult> &results, std::set<NodeSet> &usedSets, std::set<NodeSet> &nextPool, NodeSet &testSet,
+ const std::string &graphId, const Graph &graph, int minNodes, int minMatches, int limitMatchesPerGraph)
+ {
+ // my_printf("test: %s\n", testSet.to_string().c_str());
+
+ GraphData needle;
+ std::vector<std::string> needle_nodes;
+ for (int nodeIdx : testSet.nodes)
+ needle_nodes.push_back(graph.nodes[nodeIdx].nodeId);
+ needle.graph = Graph(graph, needle_nodes);
+ needle.graph.markAllExtern();
+ diCache.add(needle.graph, needle.adjMatrix, graphId, userSolver);
+
+ std::vector<Solver::Result> ullmannResults;
+ solveForMining(ullmannResults, needle);
+
+ int matches = 0;
+ std::map<std::string, int> matchesPerGraph;
+ std::set<NodeSet> thisNodeSetSet;
+
+ for (auto &it : ullmannResults)
+ {
+ std::vector<int> resultNodes;
+ for (auto &i2 : it.mappings)
+ resultNodes.push_back(graphData[it.haystackGraphId].graph.nodeMap[i2.second.haystackNodeId]);
+ NodeSet resultSet(it.haystackGraphId, resultNodes);
+
+ // my_printf("match: %s%s\n", resultSet.to_string().c_str(), usedSets.count(resultSet) > 0 ? " (dup)" : "");
+
+#if 0
+ if (usedSets.count(resultSet) > 0) {
+ // Because of shorted pins isomorphisim is not always bidirectional!
+ //
+ // This means that the following assert is not true in all cases and subgraph A might
+ // show up in the matches for subgraph B but not vice versa... This also means that the
+ // order in which subgraphs are processed has an impact on the results set.
+ //
+ assert(thisNodeSetSet.count(resultSet) > 0);
+ continue;
+ }
+#else
+ if (thisNodeSetSet.count(resultSet) > 0)
+ continue;
+#endif
+
+ usedSets.insert(resultSet);
+ thisNodeSetSet.insert(resultSet);
+
+ matchesPerGraph[it.haystackGraphId]++;
+ if (limitMatchesPerGraph < 0 || matchesPerGraph[it.haystackGraphId] < limitMatchesPerGraph)
+ matches++;
+ }
+
+ if (matches < minMatches)
+ return matches;
+
+ if (minNodes <= int(testSet.nodes.size()))
+ {
+ Solver::MineResult result;
+ result.graphId = graphId;
+ result.totalMatchesAfterLimits = matches;
+ result.matchesPerGraph = matchesPerGraph;
+ for (int nodeIdx : testSet.nodes) {
+ Solver::MineResultNode resultNode;
+ resultNode.nodeId = graph.nodes[nodeIdx].nodeId;
+ resultNode.userData = graph.nodes[nodeIdx].userData;
+ result.nodes.push_back(resultNode);
+ }
+ results.push_back(result);
+ }
+
+ nextPool.insert(thisNodeSetSet.begin(), thisNodeSetSet.end());
+ return matches;
+ }
+
+ void findNodePairs(std::vector<Solver::MineResult> &results, std::set<NodeSet> &nodePairs, int minNodes, int minMatches, int limitMatchesPerGraph)
+ {
+ int groupCounter = 0;
+ std::set<NodeSet> usedPairs;
+ nodePairs.clear();
+
+ if (verbose)
+ my_printf("\nMining for frequent node pairs:\n");
+
+ for (auto &graph_it : graphData)
+ for (int node1 = 0; node1 < int(graph_it.second.graph.nodes.size()); node1++)
+ for (auto &adj_it : graph_it.second.adjMatrix.at(node1))
+ {
+ const std::string &graphId = graph_it.first;
+ const auto &graph = graph_it.second.graph;
+ int node2 = adj_it.first;
+
+ if (node1 == node2)
+ continue;
+
+ NodeSet pair(graphId, node1, node2);
+
+ if (usedPairs.count(pair) > 0)
+ continue;
+
+ int matches = testForMining(results, usedPairs, nodePairs, pair, graphId, graph, minNodes, minMatches, limitMatchesPerGraph);
+
+ if (verbose)
+ my_printf("Pair %s[%s,%s] -> %d%s\n", graphId.c_str(), graph.nodes[node1].nodeId.c_str(),
+ graph.nodes[node2].nodeId.c_str(), matches, matches < minMatches ? " *purge*" : "");
+
+ if (minMatches <= matches)
+ groupCounter++;
+ }
+
+ if (verbose)
+ my_printf("Found a total of %d subgraphs in %d groups.\n", int(nodePairs.size()), groupCounter);
+ }
+
+ void findNextPool(std::vector<Solver::MineResult> &results, std::set<NodeSet> &pool,
+ int oldSetSize, int increment, int minNodes, int minMatches, int limitMatchesPerGraph)
+ {
+ int groupCounter = 0;
+ std::map<std::string, std::vector<const NodeSet*>> poolPerGraph;
+ std::set<NodeSet> nextPool;
+
+ for (auto &it : pool)
+ poolPerGraph[it.graphId].push_back(&it);
+
+ if (verbose)
+ my_printf("\nMining for frequent subcircuits of size %d using increment %d:\n", oldSetSize+increment, increment);
+
+ int count = 0;
+ for (auto &it : poolPerGraph)
+ {
+ std::map<int, std::set<int>> node2sets;
+ std::set<NodeSet> usedSets;
+
+ for (int idx = 0; idx < int(it.second.size()); idx++) {
+ for (int node : it.second[idx]->nodes)
+ node2sets[node].insert(idx);
+ }
+
+ for (int idx1 = 0; idx1 < int(it.second.size()); idx1++, count++)
+ {
+ std::set<int> idx2set;
+
+ for (int node : it.second[idx1]->nodes)
+ for (int idx2 : node2sets[node])
+ if (idx2 > idx1)
+ idx2set.insert(idx2);
+
+ for (int idx2 : idx2set)
+ {
+ if (it.second[idx1]->extendCandidate(*it.second[idx2]) != increment)
+ continue;
+
+ NodeSet mergedSet = *it.second[idx1];
+ mergedSet.extend(*it.second[idx2]);
+
+ if (usedSets.count(mergedSet) > 0)
+ continue;
+
+ const std::string &graphId = it.first;
+ const auto &graph = graphData[it.first].graph;
+
+ if (verbose) {
+ my_printf("<%d%%/%d> Found %s[", int(100*count/pool.size()), oldSetSize+increment, graphId.c_str());
+ bool first = true;
+ for (int nodeIdx : mergedSet.nodes) {
+ my_printf("%s%s", first ? "" : ",", graph.nodes[nodeIdx].nodeId.c_str());
+ first = false;
+ }
+ my_printf("] ->");
+ }
+
+ int matches = testForMining(results, usedSets, nextPool, mergedSet, graphId, graph, minNodes, minMatches, limitMatchesPerGraph);
+
+ if (verbose)
+ my_printf(" %d%s\n", matches, matches < minMatches ? " *purge*" : "");
+
+ if (minMatches <= matches)
+ groupCounter++;
+ }
+ }
+ }
+
+ pool.swap(nextPool);
+
+ if (verbose)
+ my_printf("Found a total of %d subgraphs in %d groups.\n", int(pool.size()), groupCounter);
+ }
+
+ // interface to the public solver class
+
+protected:
+ SolverWorker(Solver *userSolver) : userSolver(userSolver), verbose(false)
+ {
+ }
+
+ void setVerbose()
+ {
+ verbose = true;
+ }
+
+ void addGraph(std::string graphId, const Graph &graph)
+ {
+ assert(graphData.count(graphId) == 0);
+
+ GraphData &gd = graphData[graphId];
+ gd.graphId = graphId;
+ gd.graph = graph;
+ diCache.add(gd.graph, gd.adjMatrix, graphId, userSolver);
+ }
+
+ void addCompatibleTypes(std::string needleTypeId, std::string haystackTypeId)
+ {
+ compatibleTypes[needleTypeId].insert(haystackTypeId);
+ }
+
+ void addCompatibleConstants(int needleConstant, int haystackConstant)
+ {
+ compatibleConstants[needleConstant].insert(haystackConstant);
+ }
+
+ void addSwappablePorts(std::string needleTypeId, const std::set<std::string> &ports)
+ {
+ swapPorts[needleTypeId].insert(ports);
+ diCache.compareCache.clear();
+ }
+
+ void addSwappablePortsPermutation(std::string needleTypeId, const std::map<std::string, std::string> &portMapping)
+ {
+ swapPermutations[needleTypeId].insert(portMapping);
+ diCache.compareCache.clear();
+ }
+
+ void solve(std::vector<Solver::Result> &results, std::string needleGraphId, std::string haystackGraphId,
+ const std::map<std::string, std::set<std::string>> &initialMappings, bool allowOverlap, int maxSolutions)
+ {
+ assert(graphData.count(needleGraphId) > 0);
+ assert(graphData.count(haystackGraphId) > 0);
+
+ const GraphData &needle = graphData[needleGraphId];
+ GraphData &haystack = graphData[haystackGraphId];
+
+ std::vector<std::set<int>> enumerationMatrix;
+ generateEnumerationMatrix(enumerationMatrix, needle, haystack, initialMappings);
+
+ if (verbose)
+ {
+ my_printf("\n");
+ my_printf("Needle nodes:\n");
+ for (int i = 0; i < int(needle.graph.nodes.size()); i++)
+ my_printf("%5d: %s (%s)\n", i, needle.graph.nodes[i].nodeId.c_str(), needle.graph.nodes[i].typeId.c_str());
+
+ my_printf("\n");
+ my_printf("Haystack nodes:\n");
+ for (int i = 0; i < int(haystack.graph.nodes.size()); i++)
+ my_printf("%5d: %s (%s)\n", i, haystack.graph.nodes[i].nodeId.c_str(), haystack.graph.nodes[i].typeId.c_str());
+
+ my_printf("\n");
+ my_printf("Needle Adjecency Matrix:\n");
+ printAdjMatrix(needle.adjMatrix);
+
+ my_printf("\n");
+ my_printf("Haystack Adjecency Matrix:\n");
+ printAdjMatrix(haystack.adjMatrix);
+
+ my_printf("\n");
+ my_printf("Edge Types:\n");
+ diCache.printEdgeTypes();
+
+ my_printf("\n");
+ my_printf("Enumeration Matrix (haystack nodes at column indices):\n");
+ printEnumerationMatrix(enumerationMatrix, haystack.graph.nodes.size());
+ }
+
+ haystack.usedNodes.resize(haystack.graph.nodes.size());
+ ullmannRecursion(results, enumerationMatrix, 0, needle, haystack, allowOverlap, maxSolutions > 0 ? results.size() + maxSolutions : -1);
+ }
+
+ void mine(std::vector<Solver::MineResult> &results, int minNodes, int maxNodes, int minMatches, int limitMatchesPerGraph)
+ {
+ int nodeSetSize = 2;
+ std::set<NodeSet> pool;
+ findNodePairs(results, pool, minNodes, minMatches, limitMatchesPerGraph);
+
+ while ((maxNodes < 0 || nodeSetSize < maxNodes) && pool.size() > 0)
+ {
+ int increment = nodeSetSize - 1;
+ if (nodeSetSize + increment >= minNodes)
+ increment = minNodes - nodeSetSize;
+ if (nodeSetSize >= minNodes)
+ increment = 1;
+
+ findNextPool(results, pool, nodeSetSize, increment, minNodes, minMatches, limitMatchesPerGraph);
+ nodeSetSize += increment;
+ }
+ }
+
+ void clearOverlapHistory()
+ {
+ for (auto &it : graphData)
+ it.second.usedNodes.clear();
+ }
+
+ void clearConfig()
+ {
+ compatibleTypes.clear();
+ compatibleConstants.clear();
+ swapPorts.clear();
+ swapPermutations.clear();
+ diCache.compareCache.clear();
+ }
+
+ friend class Solver;
+};
+
+bool Solver::userCompareNodes(const std::string&, const std::string&, void*, const std::string&, const std::string&, void*, const std::map<std::string, std::string>&)
+{
+ return true;
+}
+
+std::string Solver::userAnnotateEdge(const std::string&, const std::string&, void*, const std::string&, void*)
+{
+ return std::string();
+}
+
+bool Solver::userCompareEdge(const std::string&, const std::string&, void*, const std::string&, void*, const std::string&, const std::string&, void*, const std::string&, void*)
+{
+ return true;
+}
+
+bool Solver::userCheckSolution(const Result&)
+{
+ return true;
+}
+
+SubCircuit::Solver::Solver()
+{
+ worker = new SolverWorker(this);
+}
+
+SubCircuit::Solver::~Solver()
+{
+ delete worker;
+}
+
+void SubCircuit::Solver::setVerbose()
+{
+ worker->setVerbose();
+}
+
+void SubCircuit::Solver::addGraph(std::string graphId, const Graph &graph)
+{
+ worker->addGraph(graphId, graph);
+}
+
+void SubCircuit::Solver::addCompatibleTypes(std::string needleTypeId, std::string haystackTypeId)
+{
+ worker->addCompatibleTypes(needleTypeId, haystackTypeId);
+}
+
+void SubCircuit::Solver::addCompatibleConstants(int needleConstant, int haystackConstant)
+{
+ worker->addCompatibleConstants(needleConstant, haystackConstant);
+}
+
+void SubCircuit::Solver::addSwappablePorts(std::string needleTypeId, std::string portId1, std::string portId2, std::string portId3, std::string portId4)
+{
+ std::set<std::string> ports;
+ ports.insert(portId1);
+ ports.insert(portId2);
+ ports.insert(portId3);
+ ports.insert(portId4);
+ ports.erase(std::string());
+ addSwappablePorts(needleTypeId, ports);
+}
+
+void SubCircuit::Solver::addSwappablePorts(std::string needleTypeId, std::set<std::string> ports)
+{
+ worker->addSwappablePorts(needleTypeId, ports);
+}
+
+void SubCircuit::Solver::addSwappablePortsPermutation(std::string needleTypeId, std::map<std::string, std::string> portMapping)
+{
+ worker->addSwappablePortsPermutation(needleTypeId, portMapping);
+}
+
+void SubCircuit::Solver::solve(std::vector<Result> &results, std::string needleGraphId, std::string haystackGraphId, bool allowOverlap, int maxSolutions)
+{
+ std::map<std::string, std::set<std::string>> emptyInitialMapping;
+ worker->solve(results, needleGraphId, haystackGraphId, emptyInitialMapping, allowOverlap, maxSolutions);
+}
+
+void SubCircuit::Solver::solve(std::vector<Result> &results, std::string needleGraphId, std::string haystackGraphId,
+ const std::map<std::string, std::set<std::string>> &initialMappings, bool allowOverlap, int maxSolutions)
+{
+ worker->solve(results, needleGraphId, haystackGraphId, initialMappings, allowOverlap, maxSolutions);
+}
+
+void SubCircuit::Solver::mine(std::vector<MineResult> &results, int minNodes, int maxNodes, int minMatches, int limitMatchesPerGraph)
+{
+ worker->mine(results, minNodes, maxNodes, minMatches, limitMatchesPerGraph);
+}
+
+void SubCircuit::Solver::clearOverlapHistory()
+{
+ worker->clearOverlapHistory();
+}
+
+void SubCircuit::Solver::clearConfig()
+{
+ worker->clearConfig();
+}
+
diff --git a/libs/subcircuit/subcircuit.h b/libs/subcircuit/subcircuit.h
new file mode 100644
index 00000000..5291c642
--- /dev/null
+++ b/libs/subcircuit/subcircuit.h
@@ -0,0 +1,155 @@
+/*
+ * SubCircuit -- An implementation of the Ullmann Subgraph Isomorphism
+ * algorithm for coarse grain logic networks
+ *
+ * Copyright (C) 2013 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.
+ *
+ */
+
+#ifndef SUBCIRCUIT_H
+#define SUBCIRCUIT_H
+
+#include <string>
+#include <vector>
+#include <set>
+#include <map>
+
+namespace SubCircuit
+{
+ class SolverWorker;
+
+ class Graph
+ {
+ protected:
+ struct BitRef {
+ int nodeIdx, portIdx, bitIdx;
+ BitRef(int nodeIdx = -1, int portIdx = -1, int bitIdx = -1) : nodeIdx(nodeIdx), portIdx(portIdx), bitIdx(bitIdx) { };
+ bool operator < (const BitRef &other) const;
+ };
+
+ struct Edge {
+ std::set<BitRef> portBits;
+ int constValue;
+ bool isExtern;
+ Edge() : constValue(0), isExtern(false) { };
+ };
+
+ struct PortBit {
+ int edgeIdx;
+ PortBit() : edgeIdx(-1) { };
+ };
+
+ struct Port {
+ std::string portId;
+ int minWidth;
+ std::vector<PortBit> bits;
+ Port() : minWidth(-1) { };
+ };
+
+ struct Node {
+ std::string nodeId, typeId;
+ std::map<std::string, int> portMap;
+ std::vector<Port> ports;
+ void *userData;
+ bool shared;
+ Node() : userData(NULL), shared(false) { };
+ };
+
+ bool allExtern;
+ std::map<std::string, int> nodeMap;
+ std::vector<Node> nodes;
+ std::vector<Edge> edges;
+
+ public:
+ Graph() : allExtern(false) { };
+ Graph(const Graph &other, const std::vector<std::string> &otherNodes);
+
+ void createNode(std::string nodeId, std::string typeId, void *userData = NULL, bool shared = false);
+ void createPort(std::string nodeId, std::string portId, int width = 1, int minWidth = -1);
+ void createConnection(std::string fromNodeId, std::string fromPortId, int fromBit, std::string toNodeId, std::string toPortId, int toBit, int width = 1);
+ void createConnection(std::string fromNodeId, std::string fromPortId, std::string toNodeId, std::string toPortId);
+ void createConstant(std::string toNodeId, std::string toPortId, int toBit, int constValue);
+ void createConstant(std::string toNodeId, std::string toPortId, int constValue);
+ void markExtern(std::string nodeId, std::string portId, int bit = -1);
+ void markAllExtern();
+ void print();
+
+ friend class SolverWorker;
+ };
+
+ class Solver
+ {
+ public:
+ struct ResultNodeMapping {
+ std::string needleNodeId, haystackNodeId;
+ void *needleUserData, *haystackUserData;
+ std::map<std::string, std::string> portMapping;
+ };
+ struct Result {
+ std::string needleGraphId, haystackGraphId;
+ std::map<std::string, ResultNodeMapping> mappings;
+ };
+
+ struct MineResultNode {
+ std::string nodeId;
+ void *userData;
+ };
+ struct MineResult {
+ std::string graphId;
+ int totalMatchesAfterLimits;
+ std::map<std::string, int> matchesPerGraph;
+ std::vector<MineResultNode> nodes;
+ };
+
+ private:
+ SolverWorker *worker;
+
+ protected:
+ virtual bool userCompareNodes(const std::string &needleGraphId, const std::string &needleNodeId, void *needleUserData,
+ const std::string &haystackGraphId, const std::string &haystackNodeId, void *haystackUserData, const std::map<std::string, std::string> &portMapping);
+
+ virtual std::string userAnnotateEdge(const std::string &graphId, const std::string &fromNodeId, void *fromUserData, const std::string &toNodeId, void *toUserData);
+
+ virtual bool userCompareEdge(const std::string &needleGraphId, const std::string &needleFromNodeId, void *needleFromUserData, const std::string &needleToNodeId, void *needleToUserData,
+ const std::string &haystackGraphId, const std::string &haystackFromNodeId, void *haystackFromUserData, const std::string &haystackToNodeId, void *haystackToUserData);
+
+ virtual bool userCheckSolution(const Result &result);
+
+ friend class SolverWorker;
+
+ public:
+ Solver();
+ ~Solver();
+
+ void setVerbose();
+ void addGraph(std::string graphId, const Graph &graph);
+ void addCompatibleTypes(std::string needleTypeId, std::string haystackTypeId);
+ void addCompatibleConstants(int needleConstant, int haystackConstant);
+ void addSwappablePorts(std::string needleTypeId, std::string portId1, std::string portId2, std::string portId3 = std::string(), std::string portId4 = std::string());
+ void addSwappablePorts(std::string needleTypeId, std::set<std::string> ports);
+ void addSwappablePortsPermutation(std::string needleTypeId, std::map<std::string, std::string> portMapping);
+
+ void solve(std::vector<Result> &results, std::string needleGraphId, std::string haystackGraphId, bool allowOverlap = true, int maxSolutions = -1);
+ void solve(std::vector<Result> &results, std::string needleGraphId, std::string haystackGraphId,
+ const std::map<std::string, std::set<std::string>> &initialMapping, bool allowOverlap = true, int maxSolutions = -1);
+
+ void mine(std::vector<MineResult> &results, int minNodes, int maxNodes, int minMatches, int limitMatchesPerGraph = -1);
+
+ void clearOverlapHistory();
+ void clearConfig();
+ };
+}
+
+#endif /* SUBCIRCUIT_H */
diff --git a/libs/subcircuit/test_large.spl b/libs/subcircuit/test_large.spl
new file mode 100644
index 00000000..e33e2698
--- /dev/null
+++ b/libs/subcircuit/test_large.spl
@@ -0,0 +1,221 @@
+#!/usr/bin/env splrun
+
+var idx = 0;
+var count_nand = 0;
+var count_nor = 0;
+
+function makeNAND(net, id)
+{
+ count_nand++;
+
+ net["${id}_VDD"] = "${id}_pa S";
+ net["${id}_VSS"] = "${id}_nb S";
+
+ net["${id}_A"] = "${id}_pa G";
+ net["${id}_B"] = "${id}_pb G";
+ net["${id}_Y"] = "${id}_pb D";
+
+ return <:>
+ : node ${id}_pa pmos S 1 D 1 G 1
+ : node ${id}_pb pmos S 1 D 1 G 1
+ : node ${id}_na nmos S 1 D 1 G 1
+ : node ${id}_nb nmos S 1 D 1 G 1
+ : connect ${id}_pa S ${id}_pb S
+ : connect ${id}_pa D ${id}_pb D
+ : connect ${id}_pa D ${id}_na D
+ : connect ${id}_na S ${id}_nb D
+ : connect ${id}_pa G ${id}_na G
+ : connect ${id}_pb G ${id}_nb G
+ </>;
+}
+
+function makeNOR(net, id)
+{
+ count_nor++;
+
+ net["${id}_VDD"] = "${id}_pa S";
+ net["${id}_VSS"] = "${id}_nb S";
+
+ net["${id}_A"] = "${id}_pa G";
+ net["${id}_B"] = "${id}_pb G";
+ net["${id}_Y"] = "${id}_pb D";
+
+ return <:>
+ : node ${id}_pa pmos S 1 D 1 G 1
+ : node ${id}_pb pmos S 1 D 1 G 1
+ : node ${id}_na nmos S 1 D 1 G 1
+ : node ${id}_nb nmos S 1 D 1 G 1
+ : connect ${id}_pa D ${id}_pb S
+ : connect ${id}_pb D ${id}_na D
+ : connect ${id}_pb D ${id}_nb D
+ : connect ${id}_na S ${id}_nb S
+ : connect ${id}_pa G ${id}_na G
+ : connect ${id}_pb G ${id}_nb G
+ </>;
+}
+
+function makeGraph(seed, gates, primaryInputs, primaryOutputs)
+{
+ srand(seed);
+
+ var code = "";
+ var net, vdd, vss, outputs;
+ var unusedOutpus;
+ for (var i = 0; i < gates; i++)
+ {
+ var id = fmt("G%d", idx++);
+ if (rand(2) == 0)
+ code ~= makeNAND(net, id);
+ else
+ code ~= makeNOR(net, id);
+
+ if (i == 0) {
+ vdd = net["${id}_VDD"];
+ vss = net["${id}_VSS"];
+ } else {
+ code ~= <:>
+ : connect $vdd ${net["${id}_VDD"]}
+ : connect $vss ${net["${id}_VSS"]}
+ </>;
+ }
+
+ var inIdx1 = rand((elementsof outputs) + 1);
+ if (inIdx1 < elementsof outputs) {
+ code ~= " connect ${outputs[inIdx1]} ${net["${id}_A"]}\n";
+ delete unusedOutpus[outputs[inIdx1]];
+ } else
+ push primaryInputs, net["${id}_A"];
+
+ var inIdx2 = rand((elementsof outputs) + 1);
+ if (inIdx2 < elementsof outputs) {
+ code ~= " connect ${outputs[inIdx2]} ${net["${id}_B"]}\n";
+ delete unusedOutpus[outputs[inIdx2]];
+ } else
+ push primaryInputs, net["${id}_B"];
+
+ unusedOutpus[net["${id}_Y"]] = 1;
+ push outputs, net["${id}_Y"];
+ }
+
+ foreach netDecl (unusedOutpus)
+ push primaryOutputs, netDecl;
+
+ return code;
+}
+
+function makeConnections(fromNets, toNets)
+{
+ var code = "";
+ foreach[] toNet (toNets) {
+ var fromNet = fromNets[rand(elementsof fromNets)];
+ code != " connect $fromNet $toNet\n";
+ }
+ return code;
+}
+
+var numNodes;
+
+write(<:>
+ : graph nand
+ <?spl var net = []; ?>
+ ${makeNAND(net, "nand")}
+ : extern ${net["nand_VDD"]}
+ : extern ${net["nand_VSS"]}
+ : extern ${net["nand_A"]}
+ : extern ${net["nand_B"]}
+ : extern ${net["nand_Y"]}
+ : endgraph
+ :
+ : graph nor
+ ${makeNOR(net, "nor")}
+ : extern ${net["nor_VDD"]}
+ : extern ${net["nor_VSS"]}
+ : extern ${net["nor_A"]}
+ : extern ${net["nor_B"]}
+ : extern ${net["nor_Y"]}
+ : endgraph
+ :
+ : graph needle_1
+ <?spl var ports; ?>
+ ${makeGraph(1, 100, ports, ports)}
+ <?spl numNodes["needle_1"] = idx*4; ?>
+ <spl:foreach var="[]net" list="ports">
+ : extern $net
+ </spl:foreach>
+ : endgraph
+ :
+ : graph needle_2
+ <?spl var ports; ?>
+ ${makeGraph(2, 200, ports, ports)}
+ <?spl numNodes["needle_2"] = idx*4; ?>
+ <spl:foreach var="[]net" list="ports">
+ : extern $net
+ </spl:foreach>
+ : endgraph
+ :
+ : graph needle_3
+ <?spl var ports; ?>
+ ${makeGraph(3, 300, ports, ports)}
+ <?spl numNodes["needle_3"] = idx*4; ?>
+ <spl:foreach var="[]net" list="ports">
+ : extern $net
+ </spl:foreach>
+ : endgraph
+ :
+ : graph haystack
+
+ <?spl count_nand=0; count_nor=0; ?>
+
+ <?spl var inPorts1, outPorts1; ?>
+ ${makeGraph(1, 100, inPorts1, outPorts1)}
+
+ <?spl var inPorts2, outPorts2; ?>
+ ${makeGraph(2, 200, inPorts2, outPorts2)}
+
+ <?spl var inPorts3, outPorts3; ?>
+ ${makeGraph(3, 300, inPorts3, outPorts3)}
+
+ <?spl var inPorts4, outPorts4; ?>
+ ${makeGraph(2, 200, inPorts4, outPorts4)}
+
+ <?spl var inPorts5, outPorts5; ?>
+ ${makeGraph(1, 100, inPorts5, outPorts5)}
+ <?spl numNodes["haystack"] = idx*4; ?>
+
+ ${makeConnections(outPorts1, inPorts2)}
+ ${makeConnections(outPorts2, inPorts3)}
+ ${makeConnections(outPorts3, inPorts4)}
+ ${makeConnections(outPorts4, inPorts5)}
+
+ : endgraph
+ :
+ : solve nand haystack false
+ : expect $count_nand
+ : clearoverlap
+ :
+ : solve nor haystack false
+ : expect $count_nor
+ : clearoverlap
+ :
+ : solve needle_1 haystack false
+ : expect 2
+ :
+ : solve needle_2 haystack false
+ : expect 2
+ :
+ : solve needle_3 haystack false
+ : expect 1
+</>);
+
+numNodes["haystack"] -= numNodes["needle_3"];
+numNodes["needle_3"] -= numNodes["needle_2"];
+numNodes["needle_2"] -= numNodes["needle_1"];
+
+write(<:>
+ :
+ : # Needle_1: ${numNodes["needle_1"]} transistors
+ : # Needle_2: ${numNodes["needle_2"]} transistors
+ : # Needle_3: ${numNodes["needle_3"]} transistors
+ : # Haystack: ${numNodes["haystack"]} transistors
+</>);
+
diff --git a/libs/subcircuit/test_macc22.txt b/libs/subcircuit/test_macc22.txt
new file mode 100644
index 00000000..71938c1c
--- /dev/null
+++ b/libs/subcircuit/test_macc22.txt
@@ -0,0 +1,48 @@
+
+# verbose
+
+graph macc22
+ node mul_1 mul A 32 B 32 Y 32
+ node mul_2 mul A 32 B 32 Y 32
+ node add_1 add A 32 B 32 Y 32
+ connect mul_1 Y add_1 A
+ connect mul_2 Y add_1 B
+ extern mul_1 A B
+ extern mul_2 A B
+ extern add_1 Y
+endgraph
+
+graph macc4x2
+ node mul_1 mul A 32 B 32 Y 32
+ node mul_2 mul A 32 B 32 Y 32
+ node mul_3 mul A 32 B 32 Y 32
+ node mul_4 mul A 32 B 32 Y 32
+ node add_1 add A 32 B 32 Y 32
+ node add_2 add A 32 B 32 Y 32
+ node add_3 add A 32 B 32 Y 32
+ connect mul_1 Y add_1 A
+ connect mul_2 Y add_1 B
+ connect mul_3 Y add_2 A
+ connect mul_4 Y add_2 B
+ connect add_1 Y add_3 A
+ connect add_2 Y add_3 B
+ extern mul_1 A B
+ extern mul_2 A B
+ extern mul_3 A B
+ extern mul_4 A B
+ extern add_3 Y
+endgraph
+
+solve macc22 macc4x2
+expect 2
+
+swapgroup mul A B
+
+solve macc22 macc4x2
+expect 2
+
+swapperm add A B : B A
+
+solve macc22 macc4x2
+expect 4
+
diff --git a/libs/subcircuit/test_mine.txt b/libs/subcircuit/test_mine.txt
new file mode 100644
index 00000000..7ba272ea
--- /dev/null
+++ b/libs/subcircuit/test_mine.txt
@@ -0,0 +1,41 @@
+
+# verbose
+
+graph macc22
+ node mul_1 mul A 32 B 32 Y 32
+ node mul_2 mul A 32 B 32 Y 32
+ node add_1 add A 32 B 32 Y 32
+ connect mul_1 Y add_1 A
+ connect mul_2 Y add_1 B
+ extern mul_1 A B
+ extern mul_2 A B
+ extern add_1 Y
+endgraph
+
+graph macc4x2
+ node mul_1 mul A 32 B 32 Y 32
+ node mul_2 mul A 32 B 32 Y 32
+ node mul_3 mul A 32 B 32 Y 32
+ node mul_4 mul A 32 B 32 Y 32
+ node add_1 add A 32 B 32 Y 32
+ node add_2 add A 32 B 32 Y 32
+ node add_3 add A 32 B 32 Y 32
+ connect mul_1 Y add_1 A
+ connect mul_2 Y add_1 B
+ connect mul_3 Y add_2 A
+ connect mul_4 Y add_2 B
+ connect add_1 Y add_3 A
+ connect add_2 Y add_3 B
+ extern mul_1 A B
+ extern mul_2 A B
+ extern mul_3 A B
+ extern mul_4 A B
+ extern add_3 Y
+endgraph
+
+swapgroup mul A B
+swapgroup add A B
+
+mine 2 10 2
+expect 6
+
diff --git a/libs/subcircuit/test_perm.pl b/libs/subcircuit/test_perm.pl
new file mode 100644
index 00000000..b4e05e35
--- /dev/null
+++ b/libs/subcircuit/test_perm.pl
@@ -0,0 +1,108 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+# let "macc" implement a function like Y = (A*B) + (C*D)
+#
+# the following permutations of the input pins exist:
+#
+# g01 | A B C D | match
+# g02 | A B D C | match
+# g03 | A C B D | not
+# g04 | A C D B | not
+# g05 | A D B C | not
+# g06 | A D C B | not
+# g07 | B A C D | match
+# g08 | B A D C | match
+# g09 | B C A D | not
+# g10 | B C D A | not
+# g11 | B D A C | not
+# g12 | B D C A | not
+# g13 | C A B D | not
+# g14 | C A D B | not
+# g15 | C B A D | not
+# g16 | C B D A | not
+# g17 | C D A B | match
+# g18 | C D B A | match
+# g19 | D A B C | not
+# g20 | D A C B | not
+# g21 | D B A C | not
+# g22 | D B C A | not
+# g23 | D C A B | match
+# g24 | D C B A | match
+
+my @matches = qw/g01 g02 g07 g08 g17 g18 g23 g24/;
+my @non_matches = qw/g03 g04 g05 g06 g09 g10 g11 g12 g13 g14 g15 g16 g19 g20 g21 g22/;
+
+print "\n";
+
+for my $i (0..3) {
+for my $j (0..2) {
+for my $k (0..1) {
+ my @t = qw/A B C D/;
+ print "# ";
+ print splice(@t,$i,1),splice(@t,$j,1),splice(@t,$k,1),$t[0];
+ print "\n";
+}}}
+
+print "\n";
+
+my $iter = 1;
+for my $i (0..3) {
+for my $j (0..2) {
+for my $k (0..1) {
+ my @t = qw/A B C D/;
+ printf "graph g%02d\n", $iter++;
+ printf " node input input A 32 1 B 32 1 C 32 1 D 32 1\n";
+ printf " node macc macc A 32 1 B 32 1 C 32 1 D 32 1\n";
+ printf " connect input A macc %s\n", splice(@t,$i,1);
+ printf " connect input B macc %s\n", splice(@t,$j,1);
+ printf " connect input C macc %s\n", splice(@t,$k,1);
+ printf " connect input D macc %s\n", splice(@t,0,1);
+ printf "endgraph\n";
+ printf "\n";
+}}}
+
+$iter = 1;
+printf "graph gXL\n";
+for my $i (0..3) {
+for my $j (0..2) {
+for my $k (0..1) {
+ my $id = sprintf "_%02d", $iter++;
+ my @t = qw/A B C D/;
+ printf " node input$id input A 16 B 16 C 16 D 16\n";
+ printf " node macc$id macc A 16 B 16 C 16 D 16\n";
+ printf " connect input$id A macc$id %s\n", splice(@t,$i,1);
+ printf " connect input$id B macc$id %s\n", splice(@t,$j,1);
+ printf " connect input$id C macc$id %s\n", splice(@t,$k,1);
+ printf " connect input$id D macc$id %s\n", splice(@t,0,1);
+}}}
+printf "endgraph\n";
+printf "\n";
+
+
+printf "swapgroup macc A B\n";
+printf "swapgroup macc C D\n";
+printf "swapperm macc A B C D : C D A B\n";
+
+for my $i (@matches) {
+for my $j (@non_matches) {
+ printf "solve %s %s\n", $i, $j;
+}}
+printf "expect 0\n\n";
+
+for my $i (@matches) {
+for my $j (@matches) {
+ printf "solve %s %s\n", $i, $j;
+}}
+printf "expect %d\n\n", @matches*@matches;
+
+printf "solve g01 gXL false\n";
+printf "expect 8\n";
+
+printf "solve g03 gXL false\n";
+printf "expect 8\n";
+
+printf "solve g04 gXL false\n";
+printf "expect 8\n";
+
diff --git a/libs/subcircuit/test_shorts.spl b/libs/subcircuit/test_shorts.spl
new file mode 100644
index 00000000..f1cb4cfd
--- /dev/null
+++ b/libs/subcircuit/test_shorts.spl
@@ -0,0 +1,121 @@
+#!/usr/bin/env splrun
+//
+// Test procedure for matching Gates with shorted inputs, as suggested in
+// "SubCircuit Extraction with SubGraph Isomorphism. Zong Ling, Ph. D. IBM
+// Almaden Research Center -- EDA Shape Processing zling@us.ibm.com.":
+//
+// Four NAND gates and a NOR gate. One NAND gate (G1) has no shorted inputs,
+// one (G2) has an input shorted to VSS, one (G3) has an input shorted to VDD,
+// and one (G4) has both inputs shorted together. Th last gate (G5) is a NOR
+// gate.
+
+var net;
+
+function makeNAND(id)
+{
+ net["${id}_VDD"] = "${id}_pa S";
+ net["${id}_VSS"] = "${id}_nb S";
+
+ net["${id}_A"] = "${id}_pa G";
+ net["${id}_B"] = "${id}_pb G";
+ net["${id}_Y"] = "${id}_pb D";
+
+ return <:>
+ : node ${id}_pa pmos S 1 D 1 G 1
+ : node ${id}_pb pmos S 1 D 1 G 1
+ : node ${id}_na nmos S 1 D 1 G 1
+ : node ${id}_nb nmos S 1 D 1 G 1
+ : connect ${id}_pa S ${id}_pb S
+ : connect ${id}_pa D ${id}_pb D
+ : connect ${id}_pa D ${id}_na D
+ : connect ${id}_na S ${id}_nb D
+ : connect ${id}_pa G ${id}_na G
+ : connect ${id}_pb G ${id}_nb G
+ </>;
+}
+
+function makeNOR(id)
+{
+ net["${id}_VDD"] = "${id}_pa S";
+ net["${id}_VSS"] = "${id}_nb S";
+
+ net["${id}_A"] = "${id}_pa G";
+ net["${id}_B"] = "${id}_pb G";
+ net["${id}_Y"] = "${id}_pb D";
+
+ return <:>
+ : node ${id}_pa pmos S 1 D 1 G 1
+ : node ${id}_pb pmos S 1 D 1 G 1
+ : node ${id}_na nmos S 1 D 1 G 1
+ : node ${id}_nb nmos S 1 D 1 G 1
+ : connect ${id}_pa D ${id}_pb S
+ : connect ${id}_pb D ${id}_na D
+ : connect ${id}_pb D ${id}_nb D
+ : connect ${id}_na S ${id}_nb S
+ : connect ${id}_pa G ${id}_na G
+ : connect ${id}_pb G ${id}_nb G
+ </>;
+}
+
+write(<:>
+ : graph nand
+ : ${ makeNAND("G0") }
+ : extern ${net["G0_VDD"]}
+ : extern ${net["G0_VSS"]}
+ : extern ${net["G0_A"]}
+ : extern ${net["G0_B"]}
+ : extern ${net["G0_Y"]}
+ : endgraph
+ :
+ : graph nor
+ : ${ makeNOR("G0") }
+ : extern ${net["G0_VDD"]}
+ : extern ${net["G0_VSS"]}
+ : extern ${net["G0_A"]}
+ : extern ${net["G0_B"]}
+ : extern ${net["G0_Y"]}
+ : endgraph
+ :
+ : graph haystack
+ : ${ makeNAND("G1") }
+ : ${ makeNAND("G2") }
+ : ${ makeNAND("G3") }
+ : ${ makeNAND("G4") }
+ ${ makeNOR("G5") }
+ :
+ : node vdd vsupply V 1
+ : connect vdd V ${net["G1_VDD"]}
+ : connect vdd V ${net["G2_VDD"]}
+ : connect vdd V ${net["G3_VDD"]}
+ : connect vdd V ${net["G4_VDD"]}
+ : connect vdd V ${net["G5_VDD"]}
+ :
+ : node vss vsupply V 1
+ : connect vss V ${net["G1_VSS"]}
+ : connect vss V ${net["G2_VSS"]}
+ : connect vss V ${net["G3_VSS"]}
+ : connect vss V ${net["G4_VSS"]}
+ : connect vss V ${net["G5_VSS"]}
+ :
+ : connect ${net["G2_A"]} ${net["G1_A"]}
+ : connect ${net["G2_B"]} ${net["G2_VSS"]}
+ :
+ : connect ${net["G3_A"]} ${net["G1_VDD"]}
+ : connect ${net["G3_B"]} ${net["G2_Y"]}
+ :
+ : connect ${net["G4_A"]} ${net["G1_Y"]}
+ : connect ${net["G4_B"]} ${net["G1_Y"]}
+ :
+ : connect ${net["G5_A"]} ${net["G3_Y"]}
+ : connect ${net["G5_B"]} ${net["G4_Y"]}
+ : endgraph
+ :
+ : solve nand haystack false
+ : clearoverlap
+ : expect 4
+ :
+ : solve nor haystack false
+ : clearoverlap
+ : expect 1
+</>);
+
diff --git a/manual/.gitignore b/manual/.gitignore
new file mode 100644
index 00000000..110f65b1
--- /dev/null
+++ b/manual/.gitignore
@@ -0,0 +1,12 @@
+*.aux
+*.bbl
+*.blg
+*.idx
+*.log
+*.out
+*.pdf
+*.toc
+*.snm
+*.nav
+*.vrb
+*.ok
diff --git a/manual/APPNOTE_010_Verilog_to_BLIF.tex b/manual/APPNOTE_010_Verilog_to_BLIF.tex
new file mode 100644
index 00000000..0ecdf619
--- /dev/null
+++ b/manual/APPNOTE_010_Verilog_to_BLIF.tex
@@ -0,0 +1,466 @@
+
+% IEEEtran howto:
+% http://ftp.univie.ac.at/packages/tex/macros/latex/contrib/IEEEtran/IEEEtran_HOWTO.pdf
+\documentclass[9pt,technote,a4paper]{IEEEtran}
+
+\usepackage[T1]{fontenc} % required for luximono!
+\usepackage[scaled=0.8]{luximono} % typewriter font with bold face
+
+% To install the luximono font files:
+% getnonfreefonts-sys --all or
+% getnonfreefonts-sys luximono
+%
+% when there are trouble you might need to:
+% - Create /etc/texmf/updmap.d/99local-luximono.cfg
+% containing the single line: Map ul9.map
+% - Run update-updmap followed by mktexlsr and updmap-sys
+%
+% This commands must be executed as root with a root environment
+% (i.e. run "sudo su" and then execute the commands in the root
+% shell, don't just prefix the commands with "sudo").
+
+\usepackage[unicode,bookmarks=false]{hyperref}
+\usepackage[english]{babel}
+\usepackage[utf8]{inputenc}
+\usepackage{amssymb}
+\usepackage{amsmath}
+\usepackage{amsfonts}
+\usepackage{units}
+\usepackage{nicefrac}
+\usepackage{eurosym}
+\usepackage{graphicx}
+\usepackage{verbatim}
+\usepackage{algpseudocode}
+\usepackage{scalefnt}
+\usepackage{xspace}
+\usepackage{color}
+\usepackage{colortbl}
+\usepackage{multirow}
+\usepackage{hhline}
+\usepackage{listings}
+\usepackage{float}
+
+\usepackage{tikz}
+\usetikzlibrary{calc}
+\usetikzlibrary{arrows}
+\usetikzlibrary{scopes}
+\usetikzlibrary{through}
+\usetikzlibrary{shapes.geometric}
+
+\lstset{basicstyle=\ttfamily,frame=trBL,xleftmargin=2em,xrightmargin=1em,numbers=left}
+
+\begin{document}
+
+\title{Yosys Application Note 010: \\ Converting Verilog to BLIF}
+\author{Clifford Wolf \\ November 2013}
+\maketitle
+
+\begin{abstract}
+Verilog-2005 is a powerful Hardware Description Language (HDL) that can be used
+to easily create complex designs from small HDL code. It is the preferred
+method of design entry for many designers\footnote{The other half prefers VHDL,
+a very different but -- of course -- equally powerful language.}.
+
+The Berkeley Logic Interchange Format (BLIF) \cite{blif} is a simple file format for
+exchanging sequential logic between programs. It is easy to generate and
+easy to parse and is therefore the preferred method of design entry for
+many authors of logic synthesis tools.
+
+Yosys \cite{yosys} is a feature-rich
+Open-Source Verilog synthesis tool that can be used to bridge the gap between
+the two file formats. It implements most of Verilog-2005 and thus can be used
+to import modern behavioral Verilog designs into BLIF-based design flows
+without dependencies on proprietary synthesis tools.
+
+The scope of Yosys goes of course far beyond Verilog logic synthesis. But
+it is a useful and important feature and this Application Note will focus
+on this aspect of Yosys.
+\end{abstract}
+
+\section{Installation}
+
+Yosys written in C++ (using features from C++11) and is tested on modern Linux.
+It should compile fine on most UNIX systems with a C++11 compiler. The README
+file contains useful information on building Yosys and its prerequisites.
+
+Yosys is a large and feature-rich program with a couple of dependencies. It is,
+however, possible to deactivate some of the dependencies in the Makefile,
+resulting in features in Yosys becoming unavailable. When problems with building
+Yosys are encountered, a user who is only interested in the features of Yosys
+that are discussed in this Application Note may deactivate {\tt TCL}, {\tt Qt}
+and {\tt MiniSAT} support in the {\tt Makefile} and may opt against building
+{\tt yosys-abc}.
+
+\bigskip
+
+This Application Note is based on GIT Rev. {\tt e216e0e} from 2013-11-23 of
+Yosys \cite{yosys}. The Verilog sources used for the examples are taken from
+yosys-bigsim \cite{bigsim}, a collection of real-world designs used for
+regression testing Yosys.
+
+\section{Getting Started}
+
+We start our tour with the Navr\'e processor from yosys-bigsim. The Navr\'e
+processor \cite{navre} is an Open Source AVR clone. It is a single module ({\tt
+softusb\_navre}) in a single design file ({\tt softusb\_navre.v}). It also is
+using only features that map nicely to the BLIF format, for example it only
+uses synchronous resets.
+
+Converting {\tt softusb\_navre.v} to {\tt softusb\_navre.blif} could not be
+easier:
+
+\begin{figure}[H]
+\begin{lstlisting}[language=sh]
+yosys -o softusb_navre.blif -S softusb_navre.v
+\end{lstlisting}
+ \renewcommand{\figurename}{Listing}
+\caption{Calling Yosys without script file}
+\end{figure}
+
+Behind the scenes Yosys is controlled by synthesis scripts that execute
+commands that operate on Yosys' internal state. For example, the {\tt -o
+softusb\_navre.blif} option just adds the command {\tt write\_blif
+softusb\_navre.blif} to the end of the script. Likewise a file on the
+command line -- {\tt softusb\_navre.v} in this case -- adds the command
+{\tt read\_verilog softusb\_navre.v} to the beginning of the
+synthesis script. In both cases the file type is detected from the
+file extension.
+
+Finally the option {\tt -S} instantiates a built-in default synthesis script.
+Instead of using {\tt -S} one could also specify the synthesis commands
+for the script on the command line using the {\tt -p} option, either using
+individual options for each command or by passing one big command string
+with a semicolon-separated list of commands. But in most cases it is more
+convenient to use an actual script file.
+
+\section{Using a Synthesis Script}
+
+With a script file we have better control over Yosys. The following script
+file replicates what the command from the last section did:
+
+\begin{figure}[H]
+\begin{lstlisting}[language=sh]
+read_verilog softusb_navre.v
+hierarchy
+proc; opt; memory; opt; techmap; opt
+write_blif softusb_navre.blif
+\end{lstlisting}
+ \renewcommand{\figurename}{Listing}
+\caption{\tt softusb\_navre.ys}
+\end{figure}
+
+The first and last line obviously read the Verilog file and write the BLIF
+file.
+
+\medskip
+
+The 2nd line checks the design hierarchy and instantiates parametrized
+versions of the modules in the design, if necessary. In the case of this
+simple design this is a no-op. However, as a general rule a synthesis script
+should always contain this command as first command after reading the input
+files.
+
+\medskip
+
+The 3rd line does most of the actual work:
+
+\begin{itemize}
+\item The command {\tt opt} is the Yosys' built-in optimizer. It can perform
+some simple optimizations such as const-folding and removing unconnected parts
+of the design. It is common practice to call opt after each major step in the
+synthesis procedure. In cases where too much optimization is not appreciated
+(for example when analyzing a design), it is recommended to call {\tt clean}
+instead of {\tt opt}.
+\item The command {\tt proc} converts {\it processes} (Yosys' internal
+representation of Verilog {\tt always}- and {\tt initial}-blocks) to circuits
+of multiplexers and storage elements (various types of flip-flops).
+\item The command {\tt memory} converts Yosys' internal representations of
+arrays and array accesses to multi-port block memories, and then maps this
+block memories to address decoders and flip-flops, unless the option {\tt -nomap}
+is used, in which case the multi-port block memories stay in the design
+and can then be mapped to architecture-specific memory primitives using
+other commands.
+\item The command {\tt techmap} turns a high-level circuit with coarse grain
+cells such as wide adders and multipliers to a fine-grain circuit of simple
+logic primitives and single-bit storage elements. The command does that by
+substituting the complex cells by circuits of simpler cells. It is possible
+to provide a custom set of rules for this process in the form of a Verilog
+source file, as we will see in the next section.
+\end{itemize}
+
+Now Yosys can be run with the filename of the synthesis script as argument:
+
+\begin{figure}[H]
+\begin{lstlisting}[language=sh]
+yosys softusb_navre.ys
+\end{lstlisting}
+ \renewcommand{\figurename}{Listing}
+\caption{Calling Yosys with script file}
+\end{figure}
+
+\medskip
+
+Now that we are using a synthesis script we can easily modify how Yosys
+synthesizes the design. The first thing we should customize is the
+call to the {\tt hierarchy} command:
+
+Whenever it is known that there are no implicit blackboxes in the design, i.e.
+modules that are referenced but are not defined, the {\tt hierarchy} command
+should be called with the {\tt -check} option. This will then cause synthesis
+to fail when implicit blackboxes are found in the design.
+
+The 2nd thing we can improve regarding the {\tt hierarchy} command is that we
+can tell it the name of the top level module of the design hierarchy. It will
+then automatically remove all modules that are not referenced from this top
+level module.
+
+\medskip
+
+For many designs it is also desired to optimize the encodings for the finite
+state machines (FSMs) in the design. The {\tt fsm} command finds FSMs, extracts
+them, performs some basic optimizations and then generate a circuit from
+the extracted and optimized description. It would also be possible to tell
+the {\tt fsm} command to leave the FSMs in their extracted form, so they can be
+further processed using custom commands. But in this case we don't want that.
+
+\medskip
+
+So now we have the final synthesis script for generating a BLIF file
+for the Navr\'e CPU:
+
+\begin{figure}[H]
+\begin{lstlisting}[language=sh]
+read_verilog softusb_navre.v
+hierarchy -check -top softusb_navre
+proc; opt; memory; opt; fsm; opt; techmap; opt
+write_blif softusb_navre.blif
+\end{lstlisting}
+ \renewcommand{\figurename}{Listing}
+\caption{{\tt softusb\_navre.ys} (improved)}
+\end{figure}
+
+\section{Advanced Example: The Amber23 ARMv2a CPU}
+
+Our 2nd example is the Amber23 \cite{amber}
+ARMv2a CPU. Once again we base our example on the Verilog code that is included
+in yosys-bigsim \cite{bigsim}.
+
+\begin{figure}[b!]
+\begin{lstlisting}[language=sh]
+read_verilog a23_alu.v
+read_verilog a23_barrel_shift_fpga.v
+read_verilog a23_barrel_shift.v
+read_verilog a23_cache.v
+read_verilog a23_coprocessor.v
+read_verilog a23_core.v
+read_verilog a23_decode.v
+read_verilog a23_execute.v
+read_verilog a23_fetch.v
+read_verilog a23_multiply.v
+read_verilog a23_ram_register_bank.v
+read_verilog a23_register_bank.v
+read_verilog a23_wishbone.v
+read_verilog generic_sram_byte_en.v
+read_verilog generic_sram_line_en.v
+hierarchy -check -top a23_core
+add -global_input globrst 1
+proc -global_arst globrst
+techmap -map adff2dff.v
+opt; memory; opt; fsm; opt; techmap
+write_blif amber23.blif
+\end{lstlisting}
+ \renewcommand{\figurename}{Listing}
+\caption{\tt amber23.ys}
+\label{aber23.ys}
+\end{figure}
+
+The problem with this core is that it contains no dedicated reset logic.
+Instead the coding techniques shown in Listing~\ref{glob_arst} are used to
+define reset values for the global asynchronous reset in an FPGA
+implementation. This design can not be expressed in BLIF as it is. Instead we
+need to use a synthesis script that transforms this form to synchronous resets that
+can be expressed in BLIF.
+
+(Note that there is no problem if this coding techniques are used to model
+ROM, where the register is initialized using this syntax but is never updated
+otherwise.)
+
+\medskip
+
+Listing~\ref{aber23.ys} shows the synthesis script for the Amber23 core. In
+line 17 the {\tt add} command is used to add a 1-bit wide global input signal
+with the name {\tt globrst}. That means that an input with that name is added
+to each module in the design hierarchy and then all module instantiations are
+altered so that this new signal is connected throughout the whole design
+hierarchy.
+
+\begin{figure}[t!]
+\begin{lstlisting}[language=Verilog]
+reg [7:0] a = 13, b;
+initial b = 37;
+\end{lstlisting}
+ \renewcommand{\figurename}{Listing}
+\caption{Implicit coding of global asynchronous resets}
+\label{glob_arst}
+\end{figure}
+
+\begin{figure}[b!]
+\begin{lstlisting}[language=Verilog]
+(* techmap_celltype = "$adff" *)
+module adff2dff (CLK, ARST, D, Q);
+
+parameter WIDTH = 1;
+parameter CLK_POLARITY = 1;
+parameter ARST_POLARITY = 1;
+parameter ARST_VALUE = 0;
+
+input CLK, ARST;
+input [WIDTH-1:0] D;
+output reg [WIDTH-1:0] Q;
+
+wire [1023:0] _TECHMAP_DO_ = "proc";
+
+wire _TECHMAP_FAIL_ =
+ !CLK_POLARITY || !ARST_POLARITY;
+
+always @(posedge CLK)
+ if (ARST)
+ Q <= ARST_VALUE;
+ else
+ Q <= D;
+
+endmodule
+\end{lstlisting}
+\renewcommand{\figurename}{Listing}
+\caption{\tt adff2dff.v}
+\label{adff2dff.v}
+\end{figure}
+
+In line 18 the {\tt proc} command is called. But in this script the signal name
+{\tt globrst} is passed to the command as a global reset signal for resetting
+the registers to their assigned initial values.
+
+Finally in line 19 the {\tt techmap} command is used to replace all instances
+of flip-flops with asynchronous resets with flip-flops with synchronous resets.
+The map file used for this is shown in Listing~\ref{adff2dff.v}. Note how the
+{\tt techmap\_celltype} attribute is used in line 1 to tell the techmap command
+which cells to replace in the design, how the {\tt \_TECHMAP\_FAIL\_} wire in
+lines 15 and 16 (which evaluates to a constant value) determines if the
+parameter set is compatible with this replacement circuit, and how the {\tt
+\_TECHMAP\_DO\_} wire in line 13 provides a mini synthesis-script to be used to
+process this cell.
+
+\begin{figure*}
+\begin{lstlisting}[language=C]
+#include <stdint.h>
+#include <stdbool.h>
+
+#define BITMAP_SIZE 64
+#define OUTPORT 0x10000000
+
+static uint32_t bitmap[BITMAP_SIZE/32];
+
+static void bitmap_set(uint32_t idx) { bitmap[idx/32] |= 1 << (idx % 32); }
+static bool bitmap_get(uint32_t idx) { return (bitmap[idx/32] & (1 << (idx % 32))) != 0; }
+static void output(uint32_t val) { *((volatile uint32_t*)OUTPORT) = val; }
+
+int main() {
+ uint32_t i, j, k;
+ output(2);
+ for (i = 0; i < BITMAP_SIZE; i++) {
+ if (bitmap_get(i)) continue;
+ output(3+2*i);
+ for (j = 2*(3+2*i);; j += 3+2*i) {
+ if (j%2 == 0) continue;
+ k = (j-3)/2;
+ if (k >= BITMAP_SIZE) break;
+ bitmap_set(k);
+ }
+ }
+ output(0);
+ return 0;
+}
+\end{lstlisting}
+\renewcommand{\figurename}{Listing}
+\caption{Test program for the Amber23 CPU (Sieve of Eratosthenes). Compiled using
+GCC 4.6.3 for ARM with {\tt -Os -marm -march=armv2a -mno-thumb-interwork
+-ffreestanding}, linked with {\tt -{}-fix-v4bx} set and booted with a custom
+setup routine written in ARM assembler.}
+\label{sieve}
+\end{figure*}
+
+\section{Verification of the Amber23 CPU}
+
+The BLIF file for the Amber23 core, generated using Listings~\ref{aber23.ys}
+and \ref{adff2dff.v} and the version of the Amber23 RTL source that is bundled
+with yosys-bigsim, was verified using the test-bench from yosys-bigsim.
+It successfully executed the program shown in Listing~\ref{sieve} in the
+test-bench.
+
+For simulation the BLIF file was converted back to Verilog using ABC
+\cite{ABC}. So this test includes the successful transformation of the BLIF
+file into ABC's internal format as well.
+
+The only thing left to write about the simulation itself is that it probably
+was one of the most energy inefficient and time consuming ways of successfully
+calculating the first 31 primes the author has ever conducted.
+
+\section{Limitations}
+
+At the time of this writing Yosys does not support multi-dimensional memories,
+does not support writing to individual bits of array elements, does not
+support initialization of arrays with {\tt \$readmemb} and {\tt \$readmemh},
+and has only limited support for tristate logic, to name just a few
+limitations.
+
+That being said, Yosys can synthesize an overwhelming majority of real-world
+Verilog RTL code. The remaining cases can usually be modified to be compatible
+with Yosys quite easily.
+
+The various designs in yosys-bigsim are a good place to look for examples
+of what is within the capabilities of Yosys.
+
+\section{Conclusion}
+
+Yosys is a feature-rich Verilog-2005 synthesis tool. It has many uses, but
+one is to provide an easy gateway from high-level Verilog code to low-level
+logic circuits.
+
+The command line option {\tt -S} can be used to quickly synthesize Verilog
+code to BLIF files without a hassle.
+
+With custom synthesis scripts it becomes possible to easily perform high-level
+optimizations, such as re-encoding FSMs. In some extreme cases, such as the
+Amber23 ARMv2 CPU, the more advanced Yosys features can be used to change a
+design to fit a certain need without actually touching the RTL code.
+
+\begin{thebibliography}{9}
+
+\bibitem{yosys}
+Clifford Wolf. The Yosys Open SYnthesis Suite. \\
+\url{http://www.clifford.at/yosys/}
+
+\bibitem{bigsim}
+yosys-bigsim, a collection of real-world Verilog designs for regression testing purposes. \\
+\url{https://github.com/cliffordwolf/yosys-bigsim}
+
+\bibitem{navre}
+Sebastien Bourdeauducq. Navr\'e AVR clone (8-bit RISC). \\
+\url{http://opencores.org/project,navre}
+
+\bibitem{amber}
+Conor Santifort. Amber ARM-compatible core. \\
+\url{http://opencores.org/project,amber}
+
+\bibitem{ABC}
+Berkeley Logic Synthesis and Verification Group. ABC: A System for Sequential Synthesis and Verification. \\
+\url{http://www.eecs.berkeley.edu/~alanmi/abc/}
+
+\bibitem{blif}
+Berkeley Logic Interchange Format (BLIF) \\
+\url{http://vlsi.colorado.edu/~vis/blif.ps}
+
+\end{thebibliography}
+
+
+\end{document}
diff --git a/manual/APPNOTE_011_Design_Investigation.tex b/manual/APPNOTE_011_Design_Investigation.tex
new file mode 100644
index 00000000..9780c783
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation.tex
@@ -0,0 +1,1070 @@
+
+% IEEEtran howto:
+% http://ftp.univie.ac.at/packages/tex/macros/latex/contrib/IEEEtran/IEEEtran_HOWTO.pdf
+\documentclass[9pt,technote,a4paper]{IEEEtran}
+
+\usepackage[T1]{fontenc} % required for luximono!
+\usepackage[scaled=0.8]{luximono} % typewriter font with bold face
+
+% To install the luximono font files:
+% getnonfreefonts-sys --all or
+% getnonfreefonts-sys luximono
+%
+% when there are trouble you might need to:
+% - Create /etc/texmf/updmap.d/99local-luximono.cfg
+% containing the single line: Map ul9.map
+% - Run update-updmap followed by mktexlsr and updmap-sys
+%
+% This commands must be executed as root with a root environment
+% (i.e. run "sudo su" and then execute the commands in the root
+% shell, don't just prefix the commands with "sudo").
+
+\usepackage[unicode,bookmarks=false]{hyperref}
+\usepackage[english]{babel}
+\usepackage[utf8]{inputenc}
+\usepackage{amssymb}
+\usepackage{amsmath}
+\usepackage{amsfonts}
+\usepackage{units}
+\usepackage{nicefrac}
+\usepackage{eurosym}
+\usepackage{graphicx}
+\usepackage{verbatim}
+\usepackage{algpseudocode}
+\usepackage{scalefnt}
+\usepackage{xspace}
+\usepackage{color}
+\usepackage{colortbl}
+\usepackage{multirow}
+\usepackage{hhline}
+\usepackage{listings}
+\usepackage{float}
+
+\usepackage{tikz}
+\usetikzlibrary{calc}
+\usetikzlibrary{arrows}
+\usetikzlibrary{scopes}
+\usetikzlibrary{through}
+\usetikzlibrary{shapes.geometric}
+
+\def\FIXME{{\color{red}\bf FIXME}}
+
+\lstset{basicstyle=\ttfamily,frame=trBL,xleftmargin=0.7cm,xrightmargin=0.2cm,numbers=left}
+
+\begin{document}
+
+\title{Yosys Application Note 011: \\ Interactive Design Investigation}
+\author{Clifford Wolf \\ Original Version December 2013}
+\maketitle
+
+\begin{abstract}
+Yosys \cite{yosys} can be a great environment for building custom synthesis
+flows. It can also be an excellent tool for teaching and learning Verilog based
+RTL synthesis. In both applications it is of great importance to be able to
+analyze the designs it produces easily.
+
+This Yosys application note covers the generation of circuit diagrams with the
+Yosys {\tt show} command, the selection of interesting parts of the circuit
+using the {\tt select} command, and briefly discusses advanced investigation
+commands for evaluating circuits and solving SAT problems.
+\end{abstract}
+
+\section{Installation and Prerequisites}
+
+This Application Note is based on the Yosys \cite{yosys} GIT Rev. {\tt 2b90ba1} from
+2013-12-08. The {\tt README} file covers how to install Yosys. The
+{\tt show} command requires a working installation of GraphViz \cite{graphviz}
+and \cite{xdot} for generating the actual circuit diagrams.
+
+\section{Overview}
+
+This application note is structured as follows:
+
+Sec.~\ref{intro_show} introduces the {\tt show} command and explains the
+symbols used in the circuit diagrams generated by it.
+
+Sec.~\ref{navigate} introduces additional commands used to navigate in the
+design, select portions of the design, and print additional information on
+the elements in the design that are not contained in the circuit diagrams.
+
+Sec.~\ref{poke} introduces commands to evaluate the design and solve SAT
+problems within the design.
+
+Sec.~\ref{conclusion} concludes the document and summarizes the key points.
+
+\section{Introduction to the {\tt show} command}
+\label{intro_show}
+
+\begin{figure}[b]
+\begin{lstlisting}
+$ cat example.ys
+read_verilog example.v
+show -pause
+proc
+show -pause
+opt
+show -pause
+
+$ cat example.v
+module example(input clk, a, b, c,
+ output reg [1:0] y);
+ always @(posedge clk)
+ if (c)
+ y <= c ? a + b : 2'd0;
+endmodule
+\end{lstlisting}
+\caption{Yosys script with {\tt show} commands and example design}
+\label{example_src}
+\end{figure}
+
+\begin{figure}[b!]
+\includegraphics[width=\linewidth]{APPNOTE_011_Design_Investigation/example_00.pdf}
+\includegraphics[width=\linewidth]{APPNOTE_011_Design_Investigation/example_01.pdf}
+\includegraphics[width=\linewidth]{APPNOTE_011_Design_Investigation/example_02.pdf}
+\caption{Output of the three {\tt show} commands from Fig.~\ref{example_src}}
+\label{example_out}
+\end{figure}
+
+The {\tt show} command generates a circuit diagram for the design in its
+current state. Various options can be used to change the appearance of the
+circuit diagram, set the name and format for the output file, and so forth.
+When called without any special options, it saves the circuit diagram in
+a temporary file and launches {\tt xdot} to display the diagram.
+Subsequent calls to {\tt show} re-use the {\tt xdot} instance
+(if still running).
+
+\subsection{A simple circuit}
+
+Fig.~\ref{example_src} shows a simple synthesis script and a Verilog file that
+demonstrate the usage of {\tt show} in a simple setting. Note that {\tt show}
+is called with the {\tt -pause} option, that halts execution of the Yosys
+script until the user presses the Enter key. The {\tt show -pause} command
+also allows the user to enter an interactive shell to further investigate the
+circuit before continuing synthesis.
+
+So this script, when executed, will show the design after each of the three
+synthesis commands. The generated circuit diagrams are shown in Fig.~\ref{example_out}.
+
+The first diagram (from top to bottom) shows the design directly after being
+read by the Verilog front-end. Input and output ports are displayed as
+octagonal shapes. Cells are displayed as rectangles with inputs on the left
+and outputs on the right side. The cell labels are two lines long: The first line
+contains a unique identifier for the cell and the second line contains the cell
+type. Internal cell types are prefixed with a dollar sign. The Yosys manual
+contains a chapter on the internal cell library used in Yosys.
+
+Constants are shown as ellipses with the constant value as label. The syntax
+{\tt <bit\_width>'<bits>} is used for for constants that are not 32-bit wide
+and/or contain bits that are not 0 or 1 (i.e. {\tt x} or {\tt z}). Ordinary
+32-bit constants are written using decimal numbers.
+
+Single-bit signals are shown as thin arrows pointing from the driver to the
+load. Signals that are multiple bits wide are shown as think arrows.
+
+Finally {\it processes\/} are shown in boxes with round corners. Processes
+are Yosys' internal representation of the decision-trees and synchronization
+events modelled in a Verilog {\tt always}-block. The label reads {\tt PROC}
+followed by a unique identifier in the first line and contains the source code
+location of the original {\tt always}-block in the 2nd line. Note how the
+multiplexer from the {\tt ?:}-expression is represented as a {\tt \$mux} cell
+but the multiplexer from the {\tt if}-statement is yet still hidden within the
+process.
+
+\medskip
+
+The {\tt proc} command transforms the process from the first diagram into a
+multiplexer and a d-type flip-flip, which brings us to the 2nd diagram.
+
+The Rhombus shape to the right is a dangling wire. (Wire nodes are only shown
+if they are dangling or have ``public'' names, for example names assigned from
+the Verilog input.) Also note that the design now contains two instances of a
+{\tt BUF}-node. This are artefacts left behind by the {\tt proc}-command. It is
+quite usual to see such artefacts after calling commands that perform changes
+in the design, as most commands only care about doing the transformation in the
+least complicated way, not about cleaning up after them. The next call to {\tt
+clean} (or {\tt opt}, which includes {\tt clean} as one of its operations) will
+clean up this artefacts. This operation is so common in Yosys scripts that it
+can simply be abbreviated with the {\tt ;;} token, which doubles as
+separator for commands. Unless one wants to specifically analyze this artefacts
+left behind some operations, it is therefore recommended to always call {\tt clean}
+before calling {\tt show}.
+
+\medskip
+
+In this script we directly call {\tt opt} as next step, which finally leads us to
+the 3rd diagram in Fig.~\ref{example_out}. Here we see that the {\tt opt} command
+not only has removed the artifacts left behind by {\tt proc}, but also determined
+correctly that it can remove the first {\tt \$mux} cell without changing the behavior
+of the circuit.
+
+\begin{figure}[b!]
+\includegraphics[width=\linewidth,trim=0 2cm 0 0]{APPNOTE_011_Design_Investigation/splice.pdf}
+\caption{Output of {\tt yosys -p 'proc; opt; show' splice.v}}
+\label{splice_dia}
+\end{figure}
+
+\begin{figure}[b!]
+\lstinputlisting{APPNOTE_011_Design_Investigation/splice.v}
+\caption{\tt splice.v}
+\label{splice_src}
+\end{figure}
+
+\begin{figure}[t!]
+\includegraphics[height=\linewidth]{APPNOTE_011_Design_Investigation/cmos_00.pdf}
+\includegraphics[width=\linewidth]{APPNOTE_011_Design_Investigation/cmos_01.pdf}
+\caption{Effects of {\tt splitnets} command and of providing a cell library. (The
+circuit is a half-adder built from simple CMOS gates.)}
+\label{splitnets_libfile}
+\end{figure}
+
+\subsection{Break-out boxes for signal vectors}
+
+As has been indicated by the last example, Yosys is can manage signal vectors (aka.
+multi-bit wires or buses) as native objects. This provides great advantages
+when analyzing circuits that operate on wide integers. But it also introduces
+some additional complexity when the individual bits of of a signal vector
+are accessed. The example show in Fig.~\ref{splice_dia} and \ref{splice_src}
+demonstrates how such circuits are visualized by the {\tt show} command.
+
+The key elements in understanding this circuit diagram are of course the boxes
+with round corners and rows labeled {\tt <MSB\_LEFT>:<LSB\_LEFT> -- <MSB\_RIGHT>:<LSB\_RIGHT>}.
+Each of this boxes has one signal per row on one side and a common signal for all rows on the
+other side. The {\tt <MSB>:<LSB>} tuples specify which bits of the signals are broken out
+and connected. So the top row of the box connecting the signals {\tt a} and {\tt x} indicates
+that the bit 0 (i.e. the range 0:0) from signal {\tt a} is connected to bit 1 (i.e. the range
+1:1) of signal {\tt x}.
+
+Lines connecting such boxes together and lines connecting such boxes to cell
+ports have a slightly different look to emphasise that they are not actual signal
+wires but a necessity of the graphical representation. This distinction seems
+like a technicality, until one wants to debug a problem related to the way
+Yosys internally represents signal vectors, for example when writing custom
+Yosys commands.
+
+\subsection{Gate level netlists}
+
+Finally Fig.~\ref{splitnets_libfile} shows two common pitfalls when working
+with designs mapped to a cell library. The top figure has two problems: First
+Yosys did not have access to the cell library when this diagram was generated,
+resulting in all cell ports defaulting to being inputs. This is why all ports
+are drawn on the left side the cells are awkwardly arranged in a large column.
+Secondly the two-bit vector {\tt y} requires breakout-boxes for its individual
+bits, resulting in an unnecessary complex diagram.
+
+For the 2nd diagram Yosys has been given a description of the cell library as
+Verilog file containing blackbox modules. There are two ways to load cell
+descriptions into Yosys: First the Verilog file for the cell library can be
+passed directly to the {\tt show} command using the {\tt -lib <filename>}
+option. Secondly it is possible to load cell libraries into the design with
+the {\tt read\_verilog -lib <filename>} command. The 2nd method has the great
+advantage that the library only needs to be loaded once and can then be used
+in all subsequent calls to the {\tt show} command.
+
+In addition to that, the 2nd diagram was generated after {\tt splitnet -ports}
+was run on the design. This command splits all signal vectors into individual
+signal bits, which is often desirable when looking at gate-level circuits. The
+{\tt -ports} option is required to also split module ports. Per default the
+command only operates on interior signals.
+
+\subsection{Miscellaneous notes}
+
+Per default the {\tt show} command outputs a temporary {\tt dot} file and launches
+{\tt xdot} to display it. The options {\tt -format}, {\tt -viewer}
+and {\tt -prefix} can be used to change format, viewer and filename prefix.
+Note that the {\tt pdf} and {\tt ps} format are the only formats that support
+plotting multiple modules in one run.
+
+In densely connected circuits it is sometimes hard to keep track of the
+individual signal wires. For this cases it can be useful to call {\tt show}
+with the {\tt -colors <integer>} argument, which randomly assigns colors to the
+nets. The integer (> 0) is used as seed value for the random color
+assignments. Sometimes it is necessary it try some values to find an assignment
+of colors that looks good.
+
+The command {\tt help show} prints a complete listing of all options supported
+by the {\tt show} command.
+
+\section{Navigating the design}
+\label{navigate}
+
+Plotting circuit diagrams for entire modules in the design brings us only helps
+in simple cases. For complex modules the generated circuit diagrams are just stupidly big
+and are no help at all. In such cases one first has to select the relevant
+portions of the circuit.
+
+In addition to {\it what\/} to display one also needs to carefully decide
+{\it when\/} to display it, with respect to the synthesis flow. In general
+it is a good idea to troubleshoot a circuit in the earliest state in which
+a problem can be reproduced. So if, for example, the internal state before calling
+the {\tt techmap} command already fails to verify, it is better to troubleshoot
+the coarse-grain version of the circuit before {\tt techmap} than the gate-level
+circuit after {\tt techmap}.
+
+\medskip
+
+Note: It is generally recommended to verify the internal state of a design by
+writing it to a Verilog file using {\tt write\_verilog -noexpr} and using the
+simulation models from {\tt simlib.v} and {\tt simcells.v} from the Yosys data
+directory (as printed by {\tt yosys-config -{}-datdir}).
+
+\subsection{Interactive Navigation}
+
+\begin{figure}
+\begin{lstlisting}
+yosys> ls
+
+1 modules:
+ example
+
+yosys> cd example
+
+yosys [example]> ls
+
+7 wires:
+ $0\y[1:0]
+ $add$example.v:5$2_Y
+ a
+ b
+ c
+ clk
+ y
+
+3 cells:
+ $add$example.v:5$2
+ $procdff$7
+ $procmux$5
+\end{lstlisting}
+\caption{Demonstration of {\tt ls} and {\tt cd} using {\tt example.v} from Fig.~\ref{example_src}}
+\label{lscd}
+\end{figure}
+
+\begin{figure}[b]
+\begin{lstlisting}
+ attribute \src "example.v:5"
+ cell $add $add$example.v:5$2
+ parameter \A_SIGNED 0
+ parameter \A_WIDTH 1
+ parameter \B_SIGNED 0
+ parameter \B_WIDTH 1
+ parameter \Y_WIDTH 2
+ connect \A \a
+ connect \B \b
+ connect \Y $add$example.v:5$2_Y
+ end
+\end{lstlisting}
+\caption{Output of {\tt dump \$2} using the design from Fig.~\ref{example_src} and Fig.~\ref{example_out}}
+\label{dump2}
+\end{figure}
+
+Once the right state within the synthesis flow for debugging the circuit has
+been identified, it is recommended to simply add the {\tt shell} command
+to the matching place in the synthesis script. This command will stop the
+synthesis at the specified moment and go to shell mode, where the user can
+interactively enter commands.
+
+For most cases, the shell will start with the whole design selected (i.e. when
+the synthesis script does not already narrow the selection). The command {\tt
+ls} can now be used to create a list of all modules. The command {\tt cd} can
+be used to switch to one of the modules (type {\tt cd ..} to switch back). Now
+the {\tt ls} command lists the objects within that module. Fig.~\ref{lscd}
+demonstrates this using the design from Fig.~\ref{example_src}.
+
+There is a thing to note in Fig.~\ref{lscd}: We can see that the cell names
+from Fig.~\ref{example_out} are just abbreviations of the actual cell names,
+namely the part after the last dollar-sign. Most auto-generated names (the ones
+starting with a dollar sign) are rather long and contains some additional
+information on the origin of the named object. But in most cases those names
+can simply be abbreviated using the last part.
+
+Usually all interactive work is done with one module selected using the {\tt cd}
+command. But it is also possible to work from the design-context ({\tt cd ..}). In
+this case all object names must be prefixed with {\tt <module\_name>/}. For
+example {\tt a*/b*} would refer to all objects whose names start with {\tt b} from
+all modules whose names start with {\tt a}.
+
+The {\tt dump} command can be used to print all information about an object.
+For example {\tt dump \$2} will print Fig.~\ref{dump2}. This can for example
+be useful to determine the names of nets connected to cells, as the net-names
+are usually suppressed in the circuit diagram if they are auto-generated.
+
+For the remainder of this document we will assume that the commands are run from
+module-context and not design-context.
+
+\subsection{Working with selections}
+
+\begin{figure}[t]
+\includegraphics[width=\linewidth]{APPNOTE_011_Design_Investigation/example_03.pdf}
+\caption{Output of {\tt show} after {\tt select \$2} or {\tt select t:\$add}
+(see also Fig.~\ref{example_out})}
+\label{seladd}
+\end{figure}
+
+When a module is selected using the {\tt cd} command, all commands (with a few
+exceptions, such as the {\tt read\_*} and {\tt write\_*} commands) operate
+only on the selected module. This can also be useful for synthesis scripts
+where different synthesis strategies should be applied to different modules
+in the design.
+
+But for most interactive work we want to further narrow the set of selected
+objects. This can be done using the {\tt select} command.
+
+For example, if the command {\tt select \$2} is executed, a subsequent {\tt show}
+command will yield the diagram shown in Fig.~\ref{seladd}. Note that the nets are
+now displayed in ellipses. This indicates that they are not selected, but only
+shown because the diagram contains a cell that is connected to the net. This
+of course makes no difference for the circuit that is shown, but it can be a useful
+information when manipulating selections.
+
+Objects can not only be selected by their name but also by other properties.
+For example {\tt select t:\$add} will select all cells of type {\tt \$add}. In
+this case this is also yields the diagram shown in Fig.~\ref{seladd}.
+
+\begin{figure}[b]
+\lstinputlisting{APPNOTE_011_Design_Investigation/foobaraddsub.v}
+\caption{Test module for operations on selections}
+\label{foobaraddsub}
+\end{figure}
+
+The output of {\tt help select} contains a complete syntax reference for
+matching different properties.
+
+Many commands can operate on explicit selections. For example the command {\tt
+dump t:\$add} will print information on all {\tt \$add} cells in the active
+module. Whenever a command has {\tt [selection]} as last argument in its usage
+help, this means that it will use the engine behind the {\tt select} command
+to evaluate additional arguments and use the resulting selection instead of
+the selection created by the last {\tt select} command.
+
+Normally the {\tt select} command overwrites a previous selection. The
+commands {\tt select -add} and {\tt select -del} can be used to add
+or remove objects from the current selection.
+
+The command {\tt select -clear} can be used to reset the selection to the
+default, which is a complete selection of everything in the current module.
+
+\subsection{Operations on selections}
+
+\begin{figure}[t]
+\lstinputlisting{APPNOTE_011_Design_Investigation/sumprod.v}
+\caption{Another test module for operations on selections}
+\label{sumprod}
+\end{figure}
+
+\begin{figure}[b]
+\includegraphics[width=\linewidth]{APPNOTE_011_Design_Investigation/sumprod_00.pdf}
+\caption{Output of {\tt show a:sumstuff} on Fig.~\ref{sumprod}}
+\label{sumprod_00}
+\end{figure}
+
+The {\tt select} command is actually much more powerful than it might seem on
+the first glimpse. When it is called with multiple arguments, each argument is
+evaluated and pushed separately on a stack. After all arguments have been
+processed it simply creates the union of all elements on the stack. So the
+following command will select all {\tt \$add} cells and all objects with
+the {\tt foo} attribute set:
+
+\begin{verbatim}
+select t:$add a:foo
+\end{verbatim}
+
+(Try this with the design shown in Fig.~\ref{foobaraddsub}. Use the {\tt
+select -list} command to list the current selection.)
+
+In many cases simply adding more and more stuff to the selection is an
+ineffective way of selecting the interesting part of the design. Special
+arguments can be used to combine the elements on the stack.
+For example the {\tt \%i} arguments pops the last two elements from
+the stack, intersects them, and pushes the result back on the stack. So the
+following command will select all {\$add} cells that have the {\tt foo}
+attribute set:
+
+\begin{verbatim}
+select t:$add a:foo %i
+\end{verbatim}
+
+The listing in Fig.~\ref{sumprod} uses the Yosys non-standard {\tt \{* ... *\}}
+syntax to set the attribute {\tt sumstuff} on all cells generated by the first
+assign statement. (This works on arbitrary large blocks of Verilog code an
+can be used to mark portions of code for analysis.)
+
+Selecting {\tt a:sumstuff} in this module will yield the circuit diagram shown
+in Fig.~\ref{sumprod_00}. As only the cells themselves are selected, but not
+the temporary wire {\tt \$1\_Y}, the two adders are shown as two disjunct
+parts. This can be very useful for global signals like clock and reset signals: just
+unselect them using a command such as {\tt select -del clk rst} and each cell
+using them will get its own net label.
+
+In this case however we would like to see the cells connected properly. This
+can be achieved using the {\tt \%x} action, that broadens the selection, i.e.
+for each selected wire it selects all cells connected to the wire and vice
+versa. So {\tt show a:sumstuff \%x} yields the diagram shown in Fig.~\ref{sumprod_01}.
+
+\begin{figure}[t]
+\includegraphics[width=\linewidth]{APPNOTE_011_Design_Investigation/sumprod_01.pdf}
+\caption{Output of {\tt show a:sumstuff \%x} on Fig.~\ref{sumprod}}
+\label{sumprod_01}
+\end{figure}
+
+\subsection{Selecting logic cones}
+
+Fig.~\ref{sumprod_01} shows what is called the {\it input cone\/} of {\tt sum}, i.e.
+all cells and signals that are used to generate the signal {\tt sum}. The {\tt \%ci}
+action can be used to select the input cones of all object in the top selection
+in the stack maintained by the {\tt select} command.
+
+As the {\tt \%x} action, this commands broadens the selection by one ``step''. But
+this time the operation only works against the direction of data flow. That means,
+wires only select cells via output ports and cells only select wires via input ports.
+
+Fig.~\ref{select_prod} show the sequence of diagrams generated by the following
+commands:
+
+\begin{verbatim}
+show prod
+show prod %ci
+show prod %ci %ci
+show prod %ci %ci %ci
+\end{verbatim}
+
+When selecting many levels of logic, repeating {\tt \%ci} over and over again
+can be a bit dull. So there is a shortcut for that: the number of iterations
+can be appended to the action. So for example the action {\tt \%ci3} is
+identical to performing the {\tt \%ci} action three times.
+
+The action {\tt \%ci*} performs the {\tt \%ci} action over and over again until
+it has no effect anymore.
+
+\begin{figure}[t]
+\hfill \includegraphics[width=4cm,trim=0 1cm 0 1cm]{APPNOTE_011_Design_Investigation/sumprod_02.pdf} \\
+\includegraphics[width=\linewidth,trim=0 0cm 0 1cm]{APPNOTE_011_Design_Investigation/sumprod_03.pdf} \\
+\includegraphics[width=\linewidth,trim=0 0cm 0 1cm]{APPNOTE_011_Design_Investigation/sumprod_04.pdf} \\
+\includegraphics[width=\linewidth,trim=0 2cm 0 1cm]{APPNOTE_011_Design_Investigation/sumprod_05.pdf} \\
+\caption{Objects selected by {\tt select prod \%ci...}}
+\label{select_prod}
+\end{figure}
+
+\medskip
+
+In most cases there are certain cell types and/or ports that should not be considered for the {\tt \%ci}
+action, or we only want to follow certain cell types and/or ports. This can be achieved using additional
+patterns that can be appended to the {\tt \%ci} action.
+
+Lets consider the design from Fig.~\ref{memdemo_src}. It serves no purpose other than being a non-trivial
+circuit for demonstrating some of the advanced Yosys features. We synthesize the circuit using {\tt proc;
+opt; memory; opt} and change to the {\tt memdemo} module with {\tt cd memdemo}. If we type {\tt show}
+now we see the diagram shown in Fig.~\ref{memdemo_00}.
+
+\begin{figure}[b!]
+\lstinputlisting{APPNOTE_011_Design_Investigation/memdemo.v}
+\caption{Demo circuit for demonstrating some advanced Yosys features}
+\label{memdemo_src}
+\end{figure}
+
+\begin{figure*}[t]
+\includegraphics[width=\linewidth,trim=0 0cm 0 0cm]{APPNOTE_011_Design_Investigation/memdemo_00.pdf} \\
+\caption{Complete circuit diagram for the design shown in Fig.~\ref{memdemo_src}}
+\label{memdemo_00}
+\end{figure*}
+
+But maybe we are only interested in the tree of multiplexers that select the
+output value. In order to get there, we would start by just showing the output signal
+and its immediate predecessors:
+
+\begin{verbatim}
+show y %ci2
+\end{verbatim}
+
+From this we would learn that {\tt y} is driven by a {\tt \$dff cell}, that
+{\tt y} is connected to the output port {\tt Q}, that the {\tt clk} signal goes
+into the {\tt CLK} input port of the cell, and that the data comes from a
+auto-generated wire into the input {\tt D} of the flip-flop cell.
+
+As we are not interested in the clock signal we add an additional pattern to the {\tt \%ci}
+action, that tells it to only follow ports {\tt Q} and {\tt D} of {\tt \$dff} cells:
+
+\begin{verbatim}
+show y %ci2:+$dff[Q,D]
+\end{verbatim}
+
+To add a pattern we add a colon followed by the pattern to the {\tt \%ci}
+action. The pattern it self starts with {\tt -} or {\tt +}, indicating if it is
+an include or exclude pattern, followed by an optional comma separated list
+of cell types, followed by an optional comma separated list of port names in
+square brackets.
+
+Since we know that the only cell considered in this case is a {\tt \$dff} cell,
+we could as well only specify the port names:
+
+\begin{verbatim}
+show y %ci2:+[Q,D]
+\end{verbatim}
+
+Or we could decide to tell the {\tt \%ci} action to not follow the {\tt CLK} input:
+
+\begin{verbatim}
+show y %ci2:-[CLK]
+\end{verbatim}
+
+\begin{figure}[b]
+\includegraphics[width=\linewidth,trim=0 0cm 0 0cm]{APPNOTE_011_Design_Investigation/memdemo_01.pdf} \\
+\caption{Output of {\tt show y \%ci2:+\$dff[Q,D] \%ci*:-\$mux[S]:-\$dff}}
+\label{memdemo_01}
+\end{figure}
+
+Next we would investigate the next logic level by adding another {\tt \%ci2} to
+the command:
+
+\begin{verbatim}
+show y %ci2:-[CLK] %ci2
+\end{verbatim}
+
+From this we would learn that the next cell is a {\tt \$mux} cell and we would add additional
+pattern to narrow the selection on the path we are interested. In the end we would end up
+with a command such as
+
+\begin{verbatim}
+show y %ci2:+$dff[Q,D] %ci*:-$mux[S]:-$dff
+\end{verbatim}
+
+in which the first {\tt \%ci} jumps over the initial d-type flip-flop and the
+2nd action selects the entire input cone without going over multiplexer select
+inputs and flip-flop cells. The diagram produces by this command is shown in
+Fig.~\ref{memdemo_01}.
+
+\medskip
+
+Similar to {\tt \%ci} exists an action {\tt \%co} to select output cones that
+accepts the same syntax for pattern and repetition. The {\tt \%x} action mentioned
+previously also accepts this advanced syntax.
+
+This actions for traversing the circuit graph, combined with the actions for
+boolean operations such as intersection ({\tt \%i}) and difference ({\tt \%d})
+are powerful tools for extracting the relevant portions of the circuit under
+investigation.
+
+See {\tt help select} for a complete list of actions available in selections.
+
+\subsection{Storing and recalling selections}
+
+The current selection can be stored in memory with the command {\tt select -set
+<name>}. It can later be recalled using {\tt select @<name>}. In fact, the {\tt
+@<name>} expression pushes the stored selection on the stack maintained by the
+{\tt select} command. So for example
+
+\begin{verbatim}
+select @foo @bar %i
+\end{verbatim}
+
+will select the intersection between the stored selections {\tt foo} and {\tt bar}.
+
+\medskip
+
+In larger investigation efforts it is highly recommended to maintain a script that
+sets up relevant selections, so they can easily be recalled, for example when
+Yosys needs to be re-run after a design or source code change.
+
+The {\tt history} command can be used to list all recent interactive commands.
+This feature can be useful for creating such a script from the commands used in
+an interactive session.
+
+\section{Advanced investigation techniques}
+\label{poke}
+
+When working with very large modules, it is often not enough to just select the
+interesting part of the module. Instead it can be useful to extract the
+interesting part of the circuit into a separate module. This can for example be
+useful if one wants to run a series of synthesis commands on the critical part
+of the module and wants to carefully read all the debug output created by the
+commands in order to spot a problem. This kind of troubleshooting is much easier
+if the circuit under investigation is encapsulated in a separate module.
+
+Fig.~\ref{submod} shows how the {\tt submod} command can be used to split the
+circuit from Fig.~\ref{memdemo_src} and \ref{memdemo_00} into its components.
+The {\tt -name} option is used to specify the name of the new module and
+also the name of the new cell in the current module.
+
+\begin{figure}[t]
+\includegraphics[width=\linewidth,trim=0 1.3cm 0 0cm]{APPNOTE_011_Design_Investigation/submod_00.pdf} \\ \centerline{\tt memdemo} \vskip1em\par
+\includegraphics[width=\linewidth,trim=0 1.3cm 0 0cm]{APPNOTE_011_Design_Investigation/submod_01.pdf} \\ \centerline{\tt scramble} \vskip1em\par
+\includegraphics[width=\linewidth,trim=0 1.3cm 0 0cm]{APPNOTE_011_Design_Investigation/submod_02.pdf} \\ \centerline{\tt outstage} \vskip1em\par
+\includegraphics[width=\linewidth,trim=0 1.3cm 0 0cm]{APPNOTE_011_Design_Investigation/submod_03.pdf} \\ \centerline{\tt selstage} \vskip1em\par
+\begin{lstlisting}[basicstyle=\ttfamily\scriptsize]
+select -set outstage y %ci2:+$dff[Q,D] %ci*:-$mux[S]:-$dff
+select -set selstage y %ci2:+$dff[Q,D] %ci*:-$dff @outstage %d
+select -set scramble mem* %ci2 %ci*:-$dff mem* %d @selstage %d
+submod -name scramble @scramble
+submod -name outstage @outstage
+submod -name selstage @selstage
+\end{lstlisting}
+\caption{The circuit from Fig.~\ref{memdemo_src} and \ref{memdemo_00} broken up using {\tt submod}}
+\label{submod}
+\end{figure}
+
+\subsection{Evaluation of combinatorial circuits}
+
+The {\tt eval} command can be used to evaluate combinatorial circuits.
+For example (see Fig.~\ref{submod} for the circuit diagram of {\tt selstage}):
+
+{\scriptsize
+\begin{verbatim}
+ yosys [selstage]> eval -set s2,s1 4'b1001 -set d 4'hc -show n2 -show n1
+
+ 9. Executing EVAL pass (evaluate the circuit given an input).
+ Full command line: eval -set s2,s1 4'b1001 -set d 4'hc -show n2 -show n1
+ Eval result: \n2 = 2'10.
+ Eval result: \n1 = 2'10.
+\end{verbatim}
+\par}
+
+So the {\tt -set} option is used to set input values and the {\tt -show} option
+is used to specify the nets to evaluate. If no {\tt -show} option is specified,
+all selected output ports are used per default.
+
+If a necessary input value is not given, an error is produced. The option
+{\tt -set-undef} can be used to instead set all unspecified input nets to
+undef ({\tt x}).
+
+The {\tt -table} option can be used to create a truth table. For example:
+
+{\scriptsize
+\begin{verbatim}
+ yosys [selstage]> eval -set-undef -set d[3:1] 0 -table s1,d[0]
+
+ 10. Executing EVAL pass (evaluate the circuit given an input).
+ Full command line: eval -set-undef -set d[3:1] 0 -table s1,d[0]
+
+ \s1 \d [0] | \n1 \n2
+ ---- ------ | ---- ----
+ 2'00 1'0 | 2'00 2'00
+ 2'00 1'1 | 2'xx 2'00
+ 2'01 1'0 | 2'00 2'00
+ 2'01 1'1 | 2'xx 2'01
+ 2'10 1'0 | 2'00 2'00
+ 2'10 1'1 | 2'xx 2'10
+ 2'11 1'0 | 2'00 2'00
+ 2'11 1'1 | 2'xx 2'11
+
+ Assumed undef (x) value for the following signals: \s2
+\end{verbatim}
+}
+
+Note that the {\tt eval} command (as well as the {\tt sat} command discussed in
+the next sections) does only operate on flattened modules. It can not analyze
+signals that are passed through design hierarchy levels. So the {\tt flatten}
+command must be used on modules that instantiate other modules before this
+commands can be applied.
+
+\subsection{Solving combinatorial SAT problems}
+
+\begin{figure}[b]
+\lstinputlisting{APPNOTE_011_Design_Investigation/primetest.v}
+\caption{A simple miter circuit for testing if a number is prime. But it has a
+problem (see main text and Fig.~\ref{primesat}).}
+\label{primetest}
+\end{figure}
+
+\begin{figure*}[!t]
+\begin{lstlisting}[basicstyle=\ttfamily\small]
+yosys [primetest]> sat -prove ok 1 -set p 31
+
+8. Executing SAT pass (solving SAT problems in the circuit).
+Full command line: sat -prove ok 1 -set p 31
+
+Setting up SAT problem:
+Import set-constraint: \p = 16'0000000000011111
+Final constraint equation: \p = 16'0000000000011111
+Imported 6 cells to SAT database.
+Import proof-constraint: \ok = 1'1
+Final proof equation: \ok = 1'1
+
+Solving problem with 2790 variables and 8241 clauses..
+SAT proof finished - model found: FAIL!
+
+ ______ ___ ___ _ _ _ _
+ (_____ \ / __) / __) (_) | | | |
+ _____) )___ ___ ___ _| |__ _| |__ _____ _| | _____ __| | |
+ | ____/ ___) _ \ / _ (_ __) (_ __|____ | | || ___ |/ _ |_|
+ | | | | | |_| | |_| || | | | / ___ | | || ____( (_| |_
+ |_| |_| \___/ \___/ |_| |_| \_____|_|\_)_____)\____|_|
+
+
+ Signal Name Dec Hex Bin
+ -------------------- ---------- ---------- ---------------------
+ \a 15029 3ab5 0011101010110101
+ \b 4099 1003 0001000000000011
+ \ok 0 0 0
+ \p 31 1f 0000000000011111
+
+yosys [primetest]> sat -prove ok 1 -set p 31 -set a[15:8],b[15:8] 0
+
+9. Executing SAT pass (solving SAT problems in the circuit).
+Full command line: sat -prove ok 1 -set p 31 -set a[15:8],b[15:8] 0
+
+Setting up SAT problem:
+Import set-constraint: \p = 16'0000000000011111
+Import set-constraint: { \a [15:8] \b [15:8] } = 16'0000000000000000
+Final constraint equation: { \a [15:8] \b [15:8] \p } = { 16'0000000000000000 16'0000000000011111 }
+Imported 6 cells to SAT database.
+Import proof-constraint: \ok = 1'1
+Final proof equation: \ok = 1'1
+
+Solving problem with 2790 variables and 8257 clauses..
+SAT proof finished - no model found: SUCCESS!
+
+ /$$$$$$ /$$$$$$$$ /$$$$$$$
+ /$$__ $$ | $$_____/ | $$__ $$
+ | $$ \ $$ | $$ | $$ \ $$
+ | $$ | $$ | $$$$$ | $$ | $$
+ | $$ | $$ | $$__/ | $$ | $$
+ | $$/$$ $$ | $$ | $$ | $$
+ | $$$$$$/ /$$| $$$$$$$$ /$$| $$$$$$$//$$
+ \____ $$$|__/|________/|__/|_______/|__/
+ \__/
+\end{lstlisting}
+\caption{Experiments with the miter circuit from Fig.~\ref{primetest}. The first attempt of proving that 31
+is prime failed because the SAT solver found a creative way of factorizing 31 using integer overflow.}
+\label{primesat}
+\end{figure*}
+
+Often the opposite of the {\tt eval} command is needed, i.e. the circuits
+output is given and we want to find the matching input signals. For small
+circuits with only a few input bits this can be accomplished by trying all
+possible input combinations, as it is done by the {\tt eval -table} command.
+For larger circuits however, Yosys provides the {\tt sat} command that uses
+a SAT \cite{CircuitSAT} solver \cite{MiniSAT} to solve this kind of problems.
+
+The {\tt sat} command works very similar to the {\tt eval} command. The main
+difference is that it is now also possible to set output values and find the
+corresponding input values. For Example:
+
+{\scriptsize
+\begin{verbatim}
+ yosys [selstage]> sat -show s1,s2,d -set s1 s2 -set n2,n1 4'b1001
+
+ 11. Executing SAT pass (solving SAT problems in the circuit).
+ Full command line: sat -show s1,s2,d -set s1 s2 -set n2,n1 4'b1001
+
+ Setting up SAT problem:
+ Import set-constraint: \s1 = \s2
+ Import set-constraint: { \n2 \n1 } = 4'1001
+ Final constraint equation: { \n2 \n1 \s1 } = { 4'1001 \s2 }
+ Imported 3 cells to SAT database.
+ Import show expression: { \s1 \s2 \d }
+
+ Solving problem with 81 variables and 207 clauses..
+ SAT solving finished - model found:
+
+ Signal Name Dec Hex Bin
+ -------------------- ---------- ---------- ---------------
+ \d 9 9 1001
+ \s1 0 0 00
+ \s2 0 0 00
+\end{verbatim}
+}
+
+Note that the {\tt sat} command supports signal names in both arguments
+to the {\tt -set} option. In the above example we used {\tt -set s1 s2}
+to constraint {\tt s1} and {\tt s2} to be equal. When more complex
+constraints are needed, a wrapper circuit must be constructed that
+checks the constraints and signals if the constraint was met using an
+extra output port, which then can be forced to a value using the {\tt
+-set} option. (Such a circuit that contains the circuit under test
+plus additional constraint checking circuitry is called a {\it miter\/}
+circuit.)
+
+Fig.~\ref{primetest} shows a miter circuit that is supposed to be used as a
+prime number test. If {\tt ok} is 1 for all input values {\tt a} and {\tt b}
+for a given {\tt p}, then {\tt p} is prime, or at least that is the idea.
+
+The Yosys shell session shown in Fig.~\ref{primesat} demonstrates that SAT
+solvers can even find the unexpected solutions to a problem: Using integer
+overflow there actually is a way of ``factorizing'' 31. The clean solution
+would of course be to perform the test in 32 bits, for example by replacing
+{\tt p != a*b} in the miter with {\tt p != \{16'd0,a\}*b}, or by using a
+temporary variable for the 32 bit product {\tt a*b}. But as 31 fits well into
+8 bits (and as the purpose of this document is to show off Yosys features)
+we can also simply force the upper 8 bits of {\tt a} and {\tt b} to zero for
+the {\tt sat} call, as is done in the second command in Fig.~\ref{primesat}
+(line 31).
+
+The {\tt -prove} option used in this example works similar to {\tt -set}, but
+tries to find a case in which the two arguments are not equal. If such a case is
+not found, the property is proven to hold for all inputs that satisfy the other
+constraints.
+
+It might be worth noting, that SAT solvers are not particularly efficient at
+factorizing large numbers. But if a small factorization problem occurs as
+part of a larger circuit problem, the Yosys SAT solver is perfectly capable
+of solving it.
+
+\subsection{Solving sequential SAT problems}
+
+\begin{figure}[t!]
+\begin{lstlisting}[basicstyle=\ttfamily\scriptsize]
+yosys [memdemo]> sat -seq 6 -show y -show d -set-init-undef \
+ -max_undef -set-at 4 y 1 -set-at 5 y 2 -set-at 6 y 3
+
+6. Executing SAT pass (solving SAT problems in the circuit).
+Full command line: sat -seq 6 -show y -show d -set-init-undef
+ -max_undef -set-at 4 y 1 -set-at 5 y 2 -set-at 6 y 3
+
+Setting up time step 1:
+Final constraint equation: { } = { }
+Imported 29 cells to SAT database.
+
+Setting up time step 2:
+Final constraint equation: { } = { }
+Imported 29 cells to SAT database.
+
+Setting up time step 3:
+Final constraint equation: { } = { }
+Imported 29 cells to SAT database.
+
+Setting up time step 4:
+Import set-constraint for timestep: \y = 4'0001
+Final constraint equation: \y = 4'0001
+Imported 29 cells to SAT database.
+
+Setting up time step 5:
+Import set-constraint for timestep: \y = 4'0010
+Final constraint equation: \y = 4'0010
+Imported 29 cells to SAT database.
+
+Setting up time step 6:
+Import set-constraint for timestep: \y = 4'0011
+Final constraint equation: \y = 4'0011
+Imported 29 cells to SAT database.
+
+Setting up initial state:
+Final constraint equation: { \y \s2 \s1 \mem[3] \mem[2] \mem[1]
+ \mem[0] } = 24'xxxxxxxxxxxxxxxxxxxxxxxx
+
+Import show expression: \y
+Import show expression: \d
+
+Solving problem with 10322 variables and 27881 clauses..
+SAT model found. maximizing number of undefs.
+SAT solving finished - model found:
+
+ Time Signal Name Dec Hex Bin
+ ---- -------------------- ---------- ---------- ---------------
+ init \mem[0] -- -- xxxx
+ init \mem[1] -- -- xxxx
+ init \mem[2] -- -- xxxx
+ init \mem[3] -- -- xxxx
+ init \s1 -- -- xx
+ init \s2 -- -- xx
+ init \y -- -- xxxx
+ ---- -------------------- ---------- ---------- ---------------
+ 1 \d 0 0 0000
+ 1 \y -- -- xxxx
+ ---- -------------------- ---------- ---------- ---------------
+ 2 \d 1 1 0001
+ 2 \y -- -- xxxx
+ ---- -------------------- ---------- ---------- ---------------
+ 3 \d 2 2 0010
+ 3 \y 0 0 0000
+ ---- -------------------- ---------- ---------- ---------------
+ 4 \d 3 3 0011
+ 4 \y 1 1 0001
+ ---- -------------------- ---------- ---------- ---------------
+ 5 \d -- -- 001x
+ 5 \y 2 2 0010
+ ---- -------------------- ---------- ---------- ---------------
+ 6 \d -- -- xxxx
+ 6 \y 3 3 0011
+\end{lstlisting}
+\caption{Solving a sequential SAT problem in the {\tt memdemo} module from Fig.~\ref{memdemo_src}.}
+\label{memdemo_sat}
+\end{figure}
+
+The SAT solver functionality in Yosys can not only be used to solve
+combinatorial problems, but can also solve sequential problems. Let's consider
+the entire {\tt memdemo} module from Fig.~\ref{memdemo_src} and suppose we
+want to know which sequence of input values for {\tt d} will cause the output
+{\tt y} to produce the sequence 1, 2, 3 from any initial state.
+Fig.~\ref{memdemo_sat} show the solution to this question, as produced by
+the following command:
+
+\begin{verbatim}
+ sat -seq 6 -show y -show d -set-init-undef \
+ -max_undef -set-at 4 y 1 -set-at 5 y 2 -set-at 6 y 3
+\end{verbatim}
+
+The {\tt -seq 6} option instructs the {\tt sat} command to solve a sequential
+problem in 6 time steps. (Experiments with lower number of steps have show that
+at least 3 cycles are necessary to bring the circuit in a state from which
+the sequence 1, 2, 3 can be produced.)
+
+The {\tt -set-init-undef} option tells the {\tt sat} command to initialize
+all registers to the undef ({\tt x}) state. The way the {\tt x} state
+is treated in Verilog will ensure that the solution will work for any
+initial state.
+
+The {\tt -max\_undef} option instructs the {\tt sat} command to find a solution
+with a maximum number of undefs. This way we can see clearly which inputs bits
+are relevant to the solution.
+
+Finally the three {\tt -set-at} options add constraints for the {\tt y}
+signal to play the 1, 2, 3 sequence, starting with time step 4.
+
+It is not surprising that the solution sets {\tt d = 0} in the first step, as
+this is the only way of setting the {\tt s1} and {\tt s2} registers to a known
+value. The input values for the other steps are a bit harder to work out
+manually, but the SAT solver finds the correct solution in an instant.
+
+\medskip
+
+There is much more to write about the {\tt sat} command. For example, there is
+a set of options that can be used to performs sequential proofs using temporal
+induction \cite{tip}. The command {\tt help sat} can be used to print a list
+of all options with short descriptions of their functions.
+
+\section{Conclusion}
+\label{conclusion}
+
+Yosys provides a wide range of functions to analyze and investigate designs. For
+many cases it is sufficient to simply display circuit diagrams, maybe use some
+additional commands to narrow the scope of the circuit diagrams to the interesting
+parts of the circuit. But some cases require more than that. For this applications
+Yosys provides commands that can be used to further inspect the behavior of the
+circuit, either by evaluating which output values are generated from certain input values
+({\tt eval}) or by evaluation which input values and initial conditions can result
+in a certain behavior at the outputs ({\tt sat}). The SAT command can even be used
+to prove (or disprove) theorems regarding the circuit, in more advanced cases
+with the additional help of a miter circuit.
+
+This features can be powerful tools for the circuit designer using Yosys as a
+utility for building circuits and the software developer using Yosys as a
+framework for new algorithms alike.
+
+\begin{thebibliography}{9}
+
+\bibitem{yosys}
+Clifford Wolf. The Yosys Open SYnthesis Suite.
+\url{http://www.clifford.at/yosys/}
+
+\bibitem{graphviz}
+Graphviz - Graph Visualization Software.
+\url{http://www.graphviz.org/}
+
+\bibitem{xdot}
+xdot.py - an interactive viewer for graphs written in Graphviz's dot language.
+\url{https://github.com/jrfonseca/xdot.py}
+
+\bibitem{CircuitSAT}
+{\it Circuit satisfiability problem} on Wikipedia
+\url{http://en.wikipedia.org/wiki/Circuit_satisfiability}
+
+\bibitem{MiniSAT}
+MiniSat: a minimalistic open-source SAT solver.
+\url{http://minisat.se/}
+
+\bibitem{tip}
+Niklas Een and Niklas S\"orensson (2003).
+Temporal Induction by Incremental SAT Solving.
+\url{http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.8161}
+
+\end{thebibliography}
+
+\end{document}
diff --git a/manual/APPNOTE_011_Design_Investigation/cmos.v b/manual/APPNOTE_011_Design_Investigation/cmos.v
new file mode 100644
index 00000000..2912c760
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/cmos.v
@@ -0,0 +1,3 @@
+module cmos_demo(input a, b, output [1:0] y);
+assign y = a + b;
+endmodule
diff --git a/manual/APPNOTE_011_Design_Investigation/cmos_00.dot b/manual/APPNOTE_011_Design_Investigation/cmos_00.dot
new file mode 100644
index 00000000..49c63008
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/cmos_00.dot
@@ -0,0 +1,34 @@
+digraph "cmos_demo" {
+rankdir="LR";
+remincross=true;
+n4 [ shape=octagon, label="a", color="black", fontcolor="black" ];
+n5 [ shape=octagon, label="b", color="black", fontcolor="black" ];
+n6 [ shape=octagon, label="y", color="black", fontcolor="black" ];
+c10 [ shape=record, label="{{<p7> A|<p8> B|<p9> Y}|$g0\nNOR|{}}" ];
+c11 [ shape=record, label="{{<p7> A|<p9> Y}|$g1\nNOT|{}}" ];
+c12 [ shape=record, label="{{<p7> A|<p9> Y}|$g2\nNOT|{}}" ];
+c13 [ shape=record, label="{{<p7> A|<p8> B|<p9> Y}|$g3\nNOR|{}}" ];
+x0 [ shape=record, style=rounded, label="<s0> 1:1 - 0:0 " ];
+x0:e -> c13:p9:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""];
+c14 [ shape=record, label="{{<p7> A|<p8> B|<p9> Y}|$g4\nNOR|{}}" ];
+x1 [ shape=record, style=rounded, label="<s0> 1:1 - 0:0 " ];
+x1:e -> c14:p8:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""];
+x2 [ shape=record, style=rounded, label="<s0> 0:0 - 0:0 " ];
+x2:e -> c14:p9:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""];
+n1 [ shape=diamond, label="$n4" ];
+n1:e -> c10:p9:w [color="black", label=""];
+n1:e -> c14:p7:w [color="black", label=""];
+n2 [ shape=diamond, label="$n5" ];
+n2:e -> c11:p9:w [color="black", label=""];
+n2:e -> c13:p7:w [color="black", label=""];
+n3 [ shape=diamond, label="$n6_1" ];
+n3:e -> c12:p9:w [color="black", label=""];
+n3:e -> c13:p8:w [color="black", label=""];
+n4:e -> c10:p8:w [color="black", label=""];
+n4:e -> c12:p7:w [color="black", label=""];
+n5:e -> c10:p7:w [color="black", label=""];
+n5:e -> c11:p7:w [color="black", label=""];
+n6:e -> x0:s0:w [color="black", label=""];
+n6:e -> x1:s0:w [color="black", label=""];
+n6:e -> x2:s0:w [color="black", label=""];
+}
diff --git a/manual/APPNOTE_011_Design_Investigation/cmos_01.dot b/manual/APPNOTE_011_Design_Investigation/cmos_01.dot
new file mode 100644
index 00000000..ea6f4403
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/cmos_01.dot
@@ -0,0 +1,23 @@
+digraph "cmos_demo" {
+rankdir="LR";
+remincross=true;
+n4 [ shape=octagon, label="a", color="black", fontcolor="black" ];
+n5 [ shape=octagon, label="b", color="black", fontcolor="black" ];
+n6 [ shape=octagon, label="y[0]", color="black", fontcolor="black" ];
+n7 [ shape=octagon, label="y[1]", color="black", fontcolor="black" ];
+c11 [ shape=record, label="{{<p8> A|<p9> B}|$g0\nNOR|{<p10> Y}}" ];
+c12 [ shape=record, label="{{<p8> A}|$g1\nNOT|{<p10> Y}}" ];
+c13 [ shape=record, label="{{<p8> A}|$g2\nNOT|{<p10> Y}}" ];
+c14 [ shape=record, label="{{<p8> A|<p9> B}|$g3\nNOR|{<p10> Y}}" ];
+c15 [ shape=record, label="{{<p8> A|<p9> B}|$g4\nNOR|{<p10> Y}}" ];
+c11:p10:e -> c15:p8:w [color="black", label=""];
+c12:p10:e -> c14:p8:w [color="black", label=""];
+c13:p10:e -> c14:p9:w [color="black", label=""];
+n4:e -> c11:p9:w [color="black", label=""];
+n4:e -> c13:p8:w [color="black", label=""];
+n5:e -> c11:p8:w [color="black", label=""];
+n5:e -> c12:p8:w [color="black", label=""];
+c15:p10:e -> n6:w [color="black", label=""];
+c14:p10:e -> n7:w [color="black", label=""];
+n7:e -> c15:p9:w [color="black", label=""];
+}
diff --git a/manual/APPNOTE_011_Design_Investigation/example.v b/manual/APPNOTE_011_Design_Investigation/example.v
new file mode 100644
index 00000000..8c71989b
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/example.v
@@ -0,0 +1,6 @@
+module example(input clk, a, b, c,
+ output reg [1:0] y);
+ always @(posedge clk)
+ if (c)
+ y <= c ? a + b : 2'd0;
+endmodule
diff --git a/manual/APPNOTE_011_Design_Investigation/example.ys b/manual/APPNOTE_011_Design_Investigation/example.ys
new file mode 100644
index 00000000..b1e95608
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/example.ys
@@ -0,0 +1,11 @@
+read_verilog example.v
+show -format dot -prefix example_00
+proc
+show -format dot -prefix example_01
+opt
+show -format dot -prefix example_02
+
+cd example
+select t:$add
+show -format dot -prefix example_03
+
diff --git a/manual/APPNOTE_011_Design_Investigation/example_00.dot b/manual/APPNOTE_011_Design_Investigation/example_00.dot
new file mode 100644
index 00000000..1e23ed0e
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/example_00.dot
@@ -0,0 +1,23 @@
+digraph "example" {
+rankdir="LR";
+remincross=true;
+n4 [ shape=octagon, label="a", color="black", fontcolor="black" ];
+n5 [ shape=octagon, label="b", color="black", fontcolor="black" ];
+n6 [ shape=octagon, label="c", color="black", fontcolor="black" ];
+n7 [ shape=octagon, label="clk", color="black", fontcolor="black" ];
+n8 [ shape=octagon, label="y", color="black", fontcolor="black" ];
+c12 [ shape=record, label="{{<p9> A|<p10> B}|$2\n$add|{<p11> Y}}" ];
+v0 [ label="2'00" ];
+c14 [ shape=record, label="{{<p9> A|<p10> B|<p13> S}|$3\n$mux|{<p11> Y}}" ];
+p1 [shape=box, style=rounded, label="PROC $1\nexample.v:3"];
+c12:p11:e -> c14:p10:w [color="black", style="setlinewidth(3)", label=""];
+c14:p11:e -> p1:w [color="black", style="setlinewidth(3)", label=""];
+n4:e -> c12:p9:w [color="black", label=""];
+n5:e -> c12:p10:w [color="black", label=""];
+n6:e -> c14:p13:w [color="black", label=""];
+n6:e -> p1:w [color="black", label=""];
+n7:e -> p1:w [color="black", label=""];
+p1:e -> n8:w [color="black", style="setlinewidth(3)", label=""];
+n8:e -> p1:w [color="black", style="setlinewidth(3)", label=""];
+v0:e -> c14:p9:w [color="black", style="setlinewidth(3)", label=""];
+}
diff --git a/manual/APPNOTE_011_Design_Investigation/example_01.dot b/manual/APPNOTE_011_Design_Investigation/example_01.dot
new file mode 100644
index 00000000..e89292b5
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/example_01.dot
@@ -0,0 +1,33 @@
+digraph "example" {
+rankdir="LR";
+remincross=true;
+n6 [ shape=octagon, label="a", color="black", fontcolor="black" ];
+n7 [ shape=octagon, label="b", color="black", fontcolor="black" ];
+n8 [ shape=octagon, label="c", color="black", fontcolor="black" ];
+n9 [ shape=octagon, label="clk", color="black", fontcolor="black" ];
+n10 [ shape=octagon, label="y", color="black", fontcolor="black" ];
+c14 [ shape=record, label="{{<p11> A|<p12> B}|$2\n$add|{<p13> Y}}" ];
+c18 [ shape=record, label="{{<p15> CLK|<p16> D}|$7\n$dff|{<p17> Q}}" ];
+c20 [ shape=record, label="{{<p11> A|<p12> B|<p19> S}|$5\n$mux|{<p13> Y}}" ];
+v0 [ label="2'00" ];
+c21 [ shape=record, label="{{<p11> A|<p12> B|<p19> S}|$3\n$mux|{<p13> Y}}" ];
+x1 [shape=box, style=rounded, label="BUF"];
+x2 [shape=box, style=rounded, label="BUF"];
+n1 [ shape=diamond, label="$0\\y[1:0]" ];
+x2:e:e -> n1:w [color="black", style="setlinewidth(3)", label=""];
+c18:p17:e -> n10:w [color="black", style="setlinewidth(3)", label=""];
+n10:e -> c20:p11:w [color="black", style="setlinewidth(3)", label=""];
+c14:p13:e -> c21:p12:w [color="black", style="setlinewidth(3)", label=""];
+n3 [ shape=point ];
+c20:p13:e -> n3:w [color="black", style="setlinewidth(3)", label=""];
+n3:e -> c18:p16:w [color="black", style="setlinewidth(3)", label=""];
+n3:e -> x2:w:w [color="black", style="setlinewidth(3)", label=""];
+x1:e:e -> c20:p19:w [color="black", label=""];
+c21:p13:e -> c20:p12:w [color="black", style="setlinewidth(3)", label=""];
+n6:e -> c14:p11:w [color="black", label=""];
+n7:e -> c14:p12:w [color="black", label=""];
+n8:e -> c21:p19:w [color="black", label=""];
+n8:e -> x1:w:w [color="black", label=""];
+n9:e -> c18:p15:w [color="black", label=""];
+v0:e -> c21:p11:w [color="black", style="setlinewidth(3)", label=""];
+}
diff --git a/manual/APPNOTE_011_Design_Investigation/example_02.dot b/manual/APPNOTE_011_Design_Investigation/example_02.dot
new file mode 100644
index 00000000..f950ed2e
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/example_02.dot
@@ -0,0 +1,20 @@
+digraph "example" {
+rankdir="LR";
+remincross=true;
+n3 [ shape=octagon, label="a", color="black", fontcolor="black" ];
+n4 [ shape=octagon, label="b", color="black", fontcolor="black" ];
+n5 [ shape=octagon, label="c", color="black", fontcolor="black" ];
+n6 [ shape=octagon, label="clk", color="black", fontcolor="black" ];
+n7 [ shape=octagon, label="y", color="black", fontcolor="black" ];
+c11 [ shape=record, label="{{<p8> A|<p9> B}|$2\n$add|{<p10> Y}}" ];
+c15 [ shape=record, label="{{<p12> CLK|<p13> D}|$7\n$dff|{<p14> Q}}" ];
+c17 [ shape=record, label="{{<p8> A|<p9> B|<p16> S}|$5\n$mux|{<p10> Y}}" ];
+c17:p10:e -> c15:p13:w [color="black", style="setlinewidth(3)", label=""];
+c11:p10:e -> c17:p9:w [color="black", style="setlinewidth(3)", label=""];
+n3:e -> c11:p8:w [color="black", label=""];
+n4:e -> c11:p9:w [color="black", label=""];
+n5:e -> c17:p16:w [color="black", label=""];
+n6:e -> c15:p12:w [color="black", label=""];
+c15:p14:e -> n7:w [color="black", style="setlinewidth(3)", label=""];
+n7:e -> c17:p8:w [color="black", style="setlinewidth(3)", label=""];
+}
diff --git a/manual/APPNOTE_011_Design_Investigation/example_03.dot b/manual/APPNOTE_011_Design_Investigation/example_03.dot
new file mode 100644
index 00000000..e19d24af
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/example_03.dot
@@ -0,0 +1,11 @@
+digraph "example" {
+rankdir="LR";
+remincross=true;
+v0 [ label="a" ];
+v1 [ label="b" ];
+v2 [ label="$2_Y" ];
+c4 [ shape=record, label="{{<p1> A|<p2> B}|$2\n$add|{<p3> Y}}" ];
+v0:e -> c4:p1:w [color="black", label=""];
+v1:e -> c4:p2:w [color="black", label=""];
+c4:p3:e -> v2:w [color="black", style="setlinewidth(3)", label=""];
+}
diff --git a/manual/APPNOTE_011_Design_Investigation/foobaraddsub.v b/manual/APPNOTE_011_Design_Investigation/foobaraddsub.v
new file mode 100644
index 00000000..0f277211
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/foobaraddsub.v
@@ -0,0 +1,8 @@
+module foobaraddsub(a, b, c, d, fa, fs, ba, bs);
+ input [7:0] a, b, c, d;
+ output [7:0] fa, fs, ba, bs;
+ assign fa = a + (* foo *) b;
+ assign fs = a - (* foo *) b;
+ assign ba = c + (* bar *) d;
+ assign bs = c - (* bar *) d;
+endmodule
diff --git a/manual/APPNOTE_011_Design_Investigation/make.sh b/manual/APPNOTE_011_Design_Investigation/make.sh
new file mode 100644
index 00000000..3845dac6
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/make.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+set -ex
+if false; then
+ rm -f *.dot
+ ../../yosys example.ys
+ ../../yosys -p 'proc; opt; show -format dot -prefix splice' splice.v
+ ../../yosys -p 'techmap; abc -liberty ../../techlibs/cmos/cmos_cells.lib;; show -format dot -prefix cmos_00' cmos.v
+ ../../yosys -p 'techmap; splitnets -ports; abc -liberty ../../techlibs/cmos/cmos_cells.lib;; show -lib ../../techlibs/cmos/cmos_cells.v -format dot -prefix cmos_01' cmos.v
+ ../../yosys -p 'opt; cd sumprod; select a:sumstuff; show -format dot -prefix sumprod_00' sumprod.v
+ ../../yosys -p 'opt; cd sumprod; select a:sumstuff %x; show -format dot -prefix sumprod_01' sumprod.v
+ ../../yosys -p 'opt; cd sumprod; select prod; show -format dot -prefix sumprod_02' sumprod.v
+ ../../yosys -p 'opt; cd sumprod; select prod %ci; show -format dot -prefix sumprod_03' sumprod.v
+ ../../yosys -p 'opt; cd sumprod; select prod %ci2; show -format dot -prefix sumprod_04' sumprod.v
+ ../../yosys -p 'opt; cd sumprod; select prod %ci3; show -format dot -prefix sumprod_05' sumprod.v
+ ../../yosys -p 'proc; opt; memory; opt; cd memdemo; show -format dot -prefix memdemo_00' memdemo.v
+ ../../yosys -p 'proc; opt; memory; opt; cd memdemo; show -format dot -prefix memdemo_01 y %ci2:+$dff[Q,D] %ci*:-$mux[S]:-$dff' memdemo.v
+ ../../yosys submod.ys
+ sed -i '/^label=/ d;' *.dot
+fi
+for dot_file in *.dot; do
+ pdf_file=${dot_file%.dot}.pdf
+ dot -Tpdf -o $pdf_file $dot_file
+done
diff --git a/manual/APPNOTE_011_Design_Investigation/memdemo.v b/manual/APPNOTE_011_Design_Investigation/memdemo.v
new file mode 100644
index 00000000..b39564dd
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/memdemo.v
@@ -0,0 +1,19 @@
+module memdemo(clk, d, y);
+
+input clk;
+input [3:0] d;
+output reg [3:0] y;
+
+integer i;
+reg [1:0] s1, s2;
+reg [3:0] mem [0:3];
+
+always @(posedge clk) begin
+ for (i = 0; i < 4; i = i+1)
+ mem[i] <= mem[(i+1) % 4] + mem[(i+2) % 4];
+ { s2, s1 } = d ? { s1, s2 } ^ d : 4'b0;
+ mem[s1] <= d;
+ y <= mem[s2];
+end
+
+endmodule
diff --git a/manual/APPNOTE_011_Design_Investigation/memdemo_00.dot b/manual/APPNOTE_011_Design_Investigation/memdemo_00.dot
new file mode 100644
index 00000000..0336a9aa
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/memdemo_00.dot
@@ -0,0 +1,138 @@
+digraph "memdemo" {
+rankdir="LR";
+remincross=true;
+n24 [ shape=octagon, label="clk", color="black", fontcolor="black" ];
+n25 [ shape=octagon, label="d", color="black", fontcolor="black" ];
+n26 [ shape=diamond, label="mem[0]", color="black", fontcolor="black" ];
+n27 [ shape=diamond, label="mem[1]", color="black", fontcolor="black" ];
+n28 [ shape=diamond, label="mem[2]", color="black", fontcolor="black" ];
+n29 [ shape=diamond, label="mem[3]", color="black", fontcolor="black" ];
+n30 [ shape=diamond, label="s1", color="black", fontcolor="black" ];
+n31 [ shape=diamond, label="s2", color="black", fontcolor="black" ];
+n32 [ shape=octagon, label="y", color="black", fontcolor="black" ];
+c36 [ shape=record, label="{{<p33> A|<p34> B}|$28\n$add|{<p35> Y}}" ];
+c37 [ shape=record, label="{{<p33> A|<p34> B}|$31\n$add|{<p35> Y}}" ];
+c38 [ shape=record, label="{{<p33> A|<p34> B}|$34\n$add|{<p35> Y}}" ];
+c39 [ shape=record, label="{{<p33> A|<p34> B}|$37\n$add|{<p35> Y}}" ];
+c41 [ shape=record, label="{{<p33> A|<p34> B|<p40> S}|$110\n$mux|{<p35> Y}}" ];
+x0 [ shape=record, style=rounded, label="<s0> 1:1 - 0:0 " ];
+x0:e -> c41:p40:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""];
+c42 [ shape=record, label="{{<p33> A|<p34> B|<p40> S}|$113\n$mux|{<p35> Y}}" ];
+x1 [ shape=record, style=rounded, label="<s0> 0:0 - 0:0 " ];
+x1:e -> c42:p40:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""];
+c43 [ shape=record, label="{{<p33> A|<p34> B|<p40> S}|$116\n$mux|{<p35> Y}}" ];
+x2 [ shape=record, style=rounded, label="<s0> 0:0 - 0:0 " ];
+x2:e -> c43:p40:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""];
+v3 [ label="1'1" ];
+c44 [ shape=record, label="{{<p33> A|<p34> B}|$145\n$and|{<p35> Y}}" ];
+v4 [ label="1'1" ];
+c45 [ shape=record, label="{{<p33> A|<p34> B}|$175\n$and|{<p35> Y}}" ];
+v5 [ label="1'1" ];
+c46 [ shape=record, label="{{<p33> A|<p34> B}|$205\n$and|{<p35> Y}}" ];
+v6 [ label="1'1" ];
+c47 [ shape=record, label="{{<p33> A|<p34> B}|$235\n$and|{<p35> Y}}" ];
+v7 [ label="2'00" ];
+c48 [ shape=record, label="{{<p33> A|<p34> B}|$143\n$eq|{<p35> Y}}" ];
+v8 [ label="2'01" ];
+c49 [ shape=record, label="{{<p33> A|<p34> B}|$173\n$eq|{<p35> Y}}" ];
+v9 [ label="2'10" ];
+c50 [ shape=record, label="{{<p33> A|<p34> B}|$203\n$eq|{<p35> Y}}" ];
+v10 [ label="2'11" ];
+c51 [ shape=record, label="{{<p33> A|<p34> B}|$233\n$eq|{<p35> Y}}" ];
+c52 [ shape=record, label="{{<p33> A|<p34> B|<p40> S}|$147\n$mux|{<p35> Y}}" ];
+c53 [ shape=record, label="{{<p33> A|<p34> B|<p40> S}|$177\n$mux|{<p35> Y}}" ];
+c54 [ shape=record, label="{{<p33> A|<p34> B|<p40> S}|$207\n$mux|{<p35> Y}}" ];
+c55 [ shape=record, label="{{<p33> A|<p34> B|<p40> S}|$237\n$mux|{<p35> Y}}" ];
+c59 [ shape=record, label="{{<p56> CLK|<p57> D}|$66\n$dff|{<p58> Q}}" ];
+c60 [ shape=record, label="{{<p56> CLK|<p57> D}|$68\n$dff|{<p58> Q}}" ];
+c61 [ shape=record, label="{{<p56> CLK|<p57> D}|$70\n$dff|{<p58> Q}}" ];
+c62 [ shape=record, label="{{<p56> CLK|<p57> D}|$72\n$dff|{<p58> Q}}" ];
+c63 [ shape=record, label="{{<p56> CLK|<p57> D}|$59\n$dff|{<p58> Q}}" ];
+c64 [ shape=record, label="{{<p56> CLK|<p57> D}|$63\n$dff|{<p58> Q}}" ];
+c65 [ shape=record, label="{{<p56> CLK|<p57> D}|$64\n$dff|{<p58> Q}}" ];
+c66 [ shape=record, label="{{<p33> A}|$39\n$reduce_bool|{<p35> Y}}" ];
+v11 [ label="4'0000" ];
+c67 [ shape=record, label="{{<p33> A|<p34> B|<p40> S}|$40\n$mux|{<p35> Y}}" ];
+x12 [ shape=record, style=rounded, label="<s1> 3:2 - 1:0 |<s0> 1:0 - 1:0 " ];
+c67:p35:e -> x12:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", style="setlinewidth(3)", label=""];
+c68 [ shape=record, label="{{<p33> A|<p34> B}|$38\n$xor|{<p35> Y}}" ];
+x13 [ shape=record, style=rounded, label="<s1> 1:0 - 3:2 |<s0> 1:0 - 1:0 " ];
+x13:e -> c68:p33:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", style="setlinewidth(3)", label=""];
+c36:p35:e -> c52:p33:w [color="black", style="setlinewidth(3)", label=""];
+c44:p35:e -> c52:p40:w [color="black", label=""];
+c45:p35:e -> c53:p40:w [color="black", label=""];
+c46:p35:e -> c54:p40:w [color="black", label=""];
+c47:p35:e -> c55:p40:w [color="black", label=""];
+c48:p35:e -> c44:p33:w [color="black", label=""];
+c49:p35:e -> c45:p33:w [color="black", label=""];
+c50:p35:e -> c46:p33:w [color="black", label=""];
+c51:p35:e -> c47:p33:w [color="black", label=""];
+c52:p35:e -> c59:p57:w [color="black", style="setlinewidth(3)", label=""];
+c53:p35:e -> c60:p57:w [color="black", style="setlinewidth(3)", label=""];
+c37:p35:e -> c53:p33:w [color="black", style="setlinewidth(3)", label=""];
+c54:p35:e -> c61:p57:w [color="black", style="setlinewidth(3)", label=""];
+c55:p35:e -> c62:p57:w [color="black", style="setlinewidth(3)", label=""];
+c66:p35:e -> c67:p40:w [color="black", label=""];
+c68:p35:e -> c67:p34:w [color="black", style="setlinewidth(3)", label=""];
+n24:e -> c59:p56:w [color="black", label=""];
+n24:e -> c60:p56:w [color="black", label=""];
+n24:e -> c61:p56:w [color="black", label=""];
+n24:e -> c62:p56:w [color="black", label=""];
+n24:e -> c63:p56:w [color="black", label=""];
+n24:e -> c64:p56:w [color="black", label=""];
+n24:e -> c65:p56:w [color="black", label=""];
+n25:e -> c52:p34:w [color="black", style="setlinewidth(3)", label=""];
+n25:e -> c53:p34:w [color="black", style="setlinewidth(3)", label=""];
+n25:e -> c54:p34:w [color="black", style="setlinewidth(3)", label=""];
+n25:e -> c55:p34:w [color="black", style="setlinewidth(3)", label=""];
+n25:e -> c66:p33:w [color="black", style="setlinewidth(3)", label=""];
+n25:e -> c68:p34:w [color="black", style="setlinewidth(3)", label=""];
+c59:p58:e -> n26:w [color="black", style="setlinewidth(3)", label=""];
+n26:e -> c38:p34:w [color="black", style="setlinewidth(3)", label=""];
+n26:e -> c39:p33:w [color="black", style="setlinewidth(3)", label=""];
+n26:e -> c42:p33:w [color="black", style="setlinewidth(3)", label=""];
+c60:p58:e -> n27:w [color="black", style="setlinewidth(3)", label=""];
+n27:e -> c36:p33:w [color="black", style="setlinewidth(3)", label=""];
+n27:e -> c39:p34:w [color="black", style="setlinewidth(3)", label=""];
+n27:e -> c42:p34:w [color="black", style="setlinewidth(3)", label=""];
+c61:p58:e -> n28:w [color="black", style="setlinewidth(3)", label=""];
+n28:e -> c36:p34:w [color="black", style="setlinewidth(3)", label=""];
+n28:e -> c37:p33:w [color="black", style="setlinewidth(3)", label=""];
+n28:e -> c43:p33:w [color="black", style="setlinewidth(3)", label=""];
+c62:p58:e -> n29:w [color="black", style="setlinewidth(3)", label=""];
+n29:e -> c37:p34:w [color="black", style="setlinewidth(3)", label=""];
+n29:e -> c38:p33:w [color="black", style="setlinewidth(3)", label=""];
+n29:e -> c43:p34:w [color="black", style="setlinewidth(3)", label=""];
+c38:p35:e -> c54:p33:w [color="black", style="setlinewidth(3)", label=""];
+c63:p58:e -> n30:w [color="black", style="setlinewidth(3)", label=""];
+n30:e -> x13:s1:w [color="black", style="setlinewidth(3)", label=""];
+c64:p58:e -> n31:w [color="black", style="setlinewidth(3)", label=""];
+n31:e -> x13:s0:w [color="black", style="setlinewidth(3)", label=""];
+c65:p58:e -> n32:w [color="black", style="setlinewidth(3)", label=""];
+c39:p35:e -> c55:p33:w [color="black", style="setlinewidth(3)", label=""];
+n5 [ shape=point ];
+x12:s0:e -> n5:w [color="black", style="setlinewidth(3)", label=""];
+n5:e -> c48:p34:w [color="black", style="setlinewidth(3)", label=""];
+n5:e -> c49:p34:w [color="black", style="setlinewidth(3)", label=""];
+n5:e -> c50:p34:w [color="black", style="setlinewidth(3)", label=""];
+n5:e -> c51:p34:w [color="black", style="setlinewidth(3)", label=""];
+n5:e -> c63:p57:w [color="black", style="setlinewidth(3)", label=""];
+n6 [ shape=point ];
+x12:s1:e -> n6:w [color="black", style="setlinewidth(3)", label=""];
+n6:e -> c64:p57:w [color="black", style="setlinewidth(3)", label=""];
+n6:e -> x0:s0:w [color="black", style="setlinewidth(3)", label=""];
+n6:e -> x1:s0:w [color="black", style="setlinewidth(3)", label=""];
+n6:e -> x2:s0:w [color="black", style="setlinewidth(3)", label=""];
+c41:p35:e -> c65:p57:w [color="black", style="setlinewidth(3)", label=""];
+c42:p35:e -> c41:p33:w [color="black", style="setlinewidth(3)", label=""];
+c43:p35:e -> c41:p34:w [color="black", style="setlinewidth(3)", label=""];
+v10:e -> c51:p33:w [color="black", style="setlinewidth(3)", label=""];
+v11:e -> c67:p33:w [color="black", style="setlinewidth(3)", label=""];
+v3:e -> c44:p34:w [color="black", label=""];
+v4:e -> c45:p34:w [color="black", label=""];
+v5:e -> c46:p34:w [color="black", label=""];
+v6:e -> c47:p34:w [color="black", label=""];
+v7:e -> c48:p33:w [color="black", style="setlinewidth(3)", label=""];
+v8:e -> c49:p33:w [color="black", style="setlinewidth(3)", label=""];
+v9:e -> c50:p33:w [color="black", style="setlinewidth(3)", label=""];
+}
diff --git a/manual/APPNOTE_011_Design_Investigation/memdemo_01.dot b/manual/APPNOTE_011_Design_Investigation/memdemo_01.dot
new file mode 100644
index 00000000..2ad92c78
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/memdemo_01.dot
@@ -0,0 +1,29 @@
+digraph "memdemo" {
+rankdir="LR";
+remincross=true;
+n4 [ shape=diamond, label="mem[0]", color="black", fontcolor="black" ];
+n5 [ shape=diamond, label="mem[1]", color="black", fontcolor="black" ];
+n6 [ shape=diamond, label="mem[2]", color="black", fontcolor="black" ];
+n7 [ shape=diamond, label="mem[3]", color="black", fontcolor="black" ];
+n8 [ shape=octagon, label="y", color="black", fontcolor="black" ];
+v0 [ label="$0\\s2[1:0] [1]" ];
+c13 [ shape=record, label="{{<p9> A|<p10> B|<p11> S}|$110\n$mux|{<p12> Y}}" ];
+v1 [ label="$0\\s2[1:0] [0]" ];
+c14 [ shape=record, label="{{<p9> A|<p10> B|<p11> S}|$113\n$mux|{<p12> Y}}" ];
+v2 [ label="$0\\s2[1:0] [0]" ];
+c15 [ shape=record, label="{{<p9> A|<p10> B|<p11> S}|$116\n$mux|{<p12> Y}}" ];
+v3 [ label="clk" ];
+c19 [ shape=record, label="{{<p16> CLK|<p17> D}|$64\n$dff|{<p18> Q}}" ];
+c13:p12:e -> c19:p17:w [color="black", style="setlinewidth(3)", label=""];
+c14:p12:e -> c13:p9:w [color="black", style="setlinewidth(3)", label=""];
+c15:p12:e -> c13:p10:w [color="black", style="setlinewidth(3)", label=""];
+n4:e -> c14:p9:w [color="black", style="setlinewidth(3)", label=""];
+n5:e -> c14:p10:w [color="black", style="setlinewidth(3)", label=""];
+n6:e -> c15:p9:w [color="black", style="setlinewidth(3)", label=""];
+n7:e -> c15:p10:w [color="black", style="setlinewidth(3)", label=""];
+c19:p18:e -> n8:w [color="black", style="setlinewidth(3)", label=""];
+v0:e -> c13:p11:w [color="black", label=""];
+v1:e -> c14:p11:w [color="black", label=""];
+v2:e -> c15:p11:w [color="black", label=""];
+v3:e -> c19:p16:w [color="black", label=""];
+}
diff --git a/manual/APPNOTE_011_Design_Investigation/primetest.v b/manual/APPNOTE_011_Design_Investigation/primetest.v
new file mode 100644
index 00000000..6cb766b7
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/primetest.v
@@ -0,0 +1,4 @@
+module primetest(p, a, b, ok);
+input [15:0] p, a, b;
+output ok = p != a*b || a == 1 || b == 1;
+endmodule
diff --git a/manual/APPNOTE_011_Design_Investigation/splice.dot b/manual/APPNOTE_011_Design_Investigation/splice.dot
new file mode 100644
index 00000000..4657feed
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/splice.dot
@@ -0,0 +1,39 @@
+digraph "splice_demo" {
+rankdir="LR";
+remincross=true;
+n1 [ shape=octagon, label="a", color="black", fontcolor="black" ];
+n2 [ shape=octagon, label="b", color="black", fontcolor="black" ];
+n3 [ shape=octagon, label="c", color="black", fontcolor="black" ];
+n4 [ shape=octagon, label="d", color="black", fontcolor="black" ];
+n5 [ shape=octagon, label="e", color="black", fontcolor="black" ];
+n6 [ shape=octagon, label="f", color="black", fontcolor="black" ];
+n7 [ shape=octagon, label="x", color="black", fontcolor="black" ];
+n8 [ shape=octagon, label="y", color="black", fontcolor="black" ];
+c11 [ shape=record, label="{{<p9> A}|$2\n$neg|{<p10> Y}}" ];
+x0 [ shape=record, style=rounded, label="<s1> 1:0 - 3:2 |<s0> 1:0 - 1:0 " ];
+x0:e -> c11:p9:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", style="setlinewidth(3)", label=""];
+x1 [ shape=record, style=rounded, label="<s0> 3:0 - 7:4 " ];
+c11:p10:e -> x1:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", style="setlinewidth(3)", label=""];
+c12 [ shape=record, label="{{<p9> A}|$1\n$not|{<p10> Y}}" ];
+x2 [ shape=record, style=rounded, label="<s1> 1:0 - 3:2 |<s0> 1:0 - 1:0 " ];
+x2:e -> c12:p9:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", style="setlinewidth(3)", label=""];
+x3 [ shape=record, style=rounded, label="<s1> 3:2 - 1:0 |<s0> 1:0 - 3:2 " ];
+c12:p10:e -> x3:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", style="setlinewidth(3)", label=""];
+x4 [ shape=record, style=rounded, label="<s1> 0:0 - 1:1 |<s0> 1:1 - 0:0 " ];
+x5 [ shape=record, style=rounded, label="<s1> 1:0 - 3:2 |<s0> 1:0 - 1:0 " ];
+x6 [ shape=record, style=rounded, label="<s0> 3:0 - 11:8 " ];
+x5:e -> x6:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", style="setlinewidth(3)", label=""];
+n1:e -> x4:s0:w [color="black", style="setlinewidth(3)", label=""];
+n1:e -> x4:s1:w [color="black", style="setlinewidth(3)", label=""];
+n1:e -> x5:s1:w [color="black", style="setlinewidth(3)", label=""];
+n2:e -> x5:s0:w [color="black", style="setlinewidth(3)", label=""];
+n3:e -> x0:s1:w [color="black", style="setlinewidth(3)", label=""];
+n4:e -> x0:s0:w [color="black", style="setlinewidth(3)", label=""];
+n5:e -> x2:s1:w [color="black", style="setlinewidth(3)", label=""];
+n6:e -> x2:s0:w [color="black", style="setlinewidth(3)", label=""];
+x4:e -> n7:w [color="black", style="setlinewidth(3)", label=""];
+x1:s0:e -> n8:w [color="black", style="setlinewidth(3)", label=""];
+x3:s0:e -> n8:w [color="black", style="setlinewidth(3)", label=""];
+x3:s1:e -> n8:w [color="black", style="setlinewidth(3)", label=""];
+x6:s0:e -> n8:w [color="black", style="setlinewidth(3)", label=""];
+}
diff --git a/manual/APPNOTE_011_Design_Investigation/splice.v b/manual/APPNOTE_011_Design_Investigation/splice.v
new file mode 100644
index 00000000..1cf7274c
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/splice.v
@@ -0,0 +1,10 @@
+module splice_demo(a, b, c, d, e, f, x, y);
+
+input [1:0] a, b, c, d, e, f;
+output [1:0] x = {a[0], a[1]};
+
+output [11:0] y;
+assign {y[11:4], y[1:0], y[3:2]} =
+ {a, b, -{c, d}, ~{e, f}};
+
+endmodule
diff --git a/manual/APPNOTE_011_Design_Investigation/submod.ys b/manual/APPNOTE_011_Design_Investigation/submod.ys
new file mode 100644
index 00000000..29ad6107
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/submod.ys
@@ -0,0 +1,16 @@
+read_verilog memdemo.v
+proc; opt; memory; opt
+
+cd memdemo
+select -set outstage y %ci2:+$dff[Q,D] %ci*:-$mux[S]:-$dff
+select -set selstage y %ci2:+$dff[Q,D] %ci*:-$dff @outstage %d
+select -set scramble mem* %ci2 %ci*:-$dff mem* %d @selstage %d
+submod -name scramble @scramble
+submod -name outstage @outstage
+submod -name selstage @selstage
+
+cd ..
+show -format dot -prefix submod_00 memdemo
+show -format dot -prefix submod_01 scramble
+show -format dot -prefix submod_02 outstage
+show -format dot -prefix submod_03 selstage
diff --git a/manual/APPNOTE_011_Design_Investigation/submod_00.dot b/manual/APPNOTE_011_Design_Investigation/submod_00.dot
new file mode 100644
index 00000000..2e55268e
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/submod_00.dot
@@ -0,0 +1,45 @@
+digraph "memdemo" {
+rankdir="LR";
+remincross=true;
+n5 [ shape=octagon, label="clk", color="black", fontcolor="black" ];
+n6 [ shape=octagon, label="d", color="black", fontcolor="black" ];
+n7 [ shape=diamond, label="mem[0]", color="black", fontcolor="black" ];
+n8 [ shape=diamond, label="mem[1]", color="black", fontcolor="black" ];
+n9 [ shape=diamond, label="mem[2]", color="black", fontcolor="black" ];
+n10 [ shape=diamond, label="mem[3]", color="black", fontcolor="black" ];
+n11 [ shape=diamond, label="s1", color="black", fontcolor="black" ];
+n12 [ shape=diamond, label="s2", color="black", fontcolor="black" ];
+n13 [ shape=octagon, label="y", color="black", fontcolor="black" ];
+c17 [ shape=record, label="{{<p14> CLK|<p15> D}|$59\n$dff|{<p16> Q}}" ];
+c18 [ shape=record, label="{{<p14> CLK|<p15> D}|$63\n$dff|{<p16> Q}}" ];
+c20 [ shape=record, label="{{<p5> clk|<p7> mem[0]|<p8> mem[1]|<p9> mem[2]|<p10> mem[3]|<p19> n1}|outstage\noutstage|{<p13> y}}" ];
+c21 [ shape=record, label="{{<p5> clk|<p6> d|<p19> n1}|scramble\nscramble|{<p7> mem[0]|<p8> mem[1]|<p9> mem[2]|<p10> mem[3]}}" ];
+c23 [ shape=record, label="{{<p6> d|<p11> s1|<p12> s2}|selstage\nselstage|{<p19> n1|<p22> n2}}" ];
+n1 [ shape=point ];
+c23:p19:e -> n1:w [color="black", style="setlinewidth(3)", label=""];
+n1:e -> c17:p15:w [color="black", style="setlinewidth(3)", label=""];
+n1:e -> c21:p19:w [color="black", style="setlinewidth(3)", label=""];
+c21:p10:e -> n10:w [color="black", style="setlinewidth(3)", label=""];
+n10:e -> c20:p10:w [color="black", style="setlinewidth(3)", label=""];
+c17:p16:e -> n11:w [color="black", style="setlinewidth(3)", label=""];
+n11:e -> c23:p11:w [color="black", style="setlinewidth(3)", label=""];
+c18:p16:e -> n12:w [color="black", style="setlinewidth(3)", label=""];
+n12:e -> c23:p12:w [color="black", style="setlinewidth(3)", label=""];
+c20:p13:e -> n13:w [color="black", style="setlinewidth(3)", label=""];
+n2 [ shape=point ];
+c23:p22:e -> n2:w [color="black", style="setlinewidth(3)", label=""];
+n2:e -> c18:p15:w [color="black", style="setlinewidth(3)", label=""];
+n2:e -> c20:p19:w [color="black", style="setlinewidth(3)", label=""];
+n5:e -> c17:p14:w [color="black", label=""];
+n5:e -> c18:p14:w [color="black", label=""];
+n5:e -> c20:p5:w [color="black", label=""];
+n5:e -> c21:p5:w [color="black", label=""];
+n6:e -> c21:p6:w [color="black", style="setlinewidth(3)", label=""];
+n6:e -> c23:p6:w [color="black", style="setlinewidth(3)", label=""];
+c21:p7:e -> n7:w [color="black", style="setlinewidth(3)", label=""];
+n7:e -> c20:p7:w [color="black", style="setlinewidth(3)", label=""];
+c21:p8:e -> n8:w [color="black", style="setlinewidth(3)", label=""];
+n8:e -> c20:p8:w [color="black", style="setlinewidth(3)", label=""];
+c21:p9:e -> n9:w [color="black", style="setlinewidth(3)", label=""];
+n9:e -> c20:p9:w [color="black", style="setlinewidth(3)", label=""];
+}
diff --git a/manual/APPNOTE_011_Design_Investigation/submod_01.dot b/manual/APPNOTE_011_Design_Investigation/submod_01.dot
new file mode 100644
index 00000000..f8f8c008
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/submod_01.dot
@@ -0,0 +1,87 @@
+digraph "scramble" {
+rankdir="LR";
+remincross=true;
+n17 [ shape=octagon, label="clk", color="black", fontcolor="black" ];
+n18 [ shape=octagon, label="d", color="black", fontcolor="black" ];
+n19 [ shape=octagon, label="mem[0]", color="black", fontcolor="black" ];
+n20 [ shape=octagon, label="mem[1]", color="black", fontcolor="black" ];
+n21 [ shape=octagon, label="mem[2]", color="black", fontcolor="black" ];
+n22 [ shape=octagon, label="mem[3]", color="black", fontcolor="black" ];
+n23 [ shape=octagon, label="n1", color="black", fontcolor="black" ];
+c27 [ shape=record, label="{{<p24> A|<p25> B}|$28\n$add|{<p26> Y}}" ];
+c28 [ shape=record, label="{{<p24> A|<p25> B}|$31\n$add|{<p26> Y}}" ];
+c29 [ shape=record, label="{{<p24> A|<p25> B}|$34\n$add|{<p26> Y}}" ];
+c30 [ shape=record, label="{{<p24> A|<p25> B}|$37\n$add|{<p26> Y}}" ];
+v0 [ label="1'1" ];
+c31 [ shape=record, label="{{<p24> A|<p25> B}|$145\n$and|{<p26> Y}}" ];
+v1 [ label="1'1" ];
+c32 [ shape=record, label="{{<p24> A|<p25> B}|$175\n$and|{<p26> Y}}" ];
+v2 [ label="1'1" ];
+c33 [ shape=record, label="{{<p24> A|<p25> B}|$205\n$and|{<p26> Y}}" ];
+v3 [ label="1'1" ];
+c34 [ shape=record, label="{{<p24> A|<p25> B}|$235\n$and|{<p26> Y}}" ];
+v4 [ label="2'00" ];
+c35 [ shape=record, label="{{<p24> A|<p25> B}|$143\n$eq|{<p26> Y}}" ];
+v5 [ label="2'01" ];
+c36 [ shape=record, label="{{<p24> A|<p25> B}|$173\n$eq|{<p26> Y}}" ];
+v6 [ label="2'10" ];
+c37 [ shape=record, label="{{<p24> A|<p25> B}|$203\n$eq|{<p26> Y}}" ];
+v7 [ label="2'11" ];
+c38 [ shape=record, label="{{<p24> A|<p25> B}|$233\n$eq|{<p26> Y}}" ];
+c40 [ shape=record, label="{{<p24> A|<p25> B|<p39> S}|$147\n$mux|{<p26> Y}}" ];
+c41 [ shape=record, label="{{<p24> A|<p25> B|<p39> S}|$177\n$mux|{<p26> Y}}" ];
+c42 [ shape=record, label="{{<p24> A|<p25> B|<p39> S}|$207\n$mux|{<p26> Y}}" ];
+c43 [ shape=record, label="{{<p24> A|<p25> B|<p39> S}|$237\n$mux|{<p26> Y}}" ];
+c47 [ shape=record, label="{{<p44> CLK|<p45> D}|$66\n$dff|{<p46> Q}}" ];
+c48 [ shape=record, label="{{<p44> CLK|<p45> D}|$68\n$dff|{<p46> Q}}" ];
+c49 [ shape=record, label="{{<p44> CLK|<p45> D}|$70\n$dff|{<p46> Q}}" ];
+c50 [ shape=record, label="{{<p44> CLK|<p45> D}|$72\n$dff|{<p46> Q}}" ];
+c27:p26:e -> c40:p24:w [color="black", style="setlinewidth(3)", label=""];
+c36:p26:e -> c32:p24:w [color="black", label=""];
+c37:p26:e -> c33:p24:w [color="black", label=""];
+c38:p26:e -> c34:p24:w [color="black", label=""];
+c40:p26:e -> c47:p45:w [color="black", style="setlinewidth(3)", label=""];
+c41:p26:e -> c48:p45:w [color="black", style="setlinewidth(3)", label=""];
+c42:p26:e -> c49:p45:w [color="black", style="setlinewidth(3)", label=""];
+c43:p26:e -> c50:p45:w [color="black", style="setlinewidth(3)", label=""];
+n17:e -> c47:p44:w [color="black", label=""];
+n17:e -> c48:p44:w [color="black", label=""];
+n17:e -> c49:p44:w [color="black", label=""];
+n17:e -> c50:p44:w [color="black", label=""];
+n18:e -> c40:p25:w [color="black", style="setlinewidth(3)", label=""];
+n18:e -> c41:p25:w [color="black", style="setlinewidth(3)", label=""];
+n18:e -> c42:p25:w [color="black", style="setlinewidth(3)", label=""];
+n18:e -> c43:p25:w [color="black", style="setlinewidth(3)", label=""];
+c47:p46:e -> n19:w [color="black", style="setlinewidth(3)", label=""];
+n19:e -> c29:p25:w [color="black", style="setlinewidth(3)", label=""];
+n19:e -> c30:p24:w [color="black", style="setlinewidth(3)", label=""];
+c28:p26:e -> c41:p24:w [color="black", style="setlinewidth(3)", label=""];
+c48:p46:e -> n20:w [color="black", style="setlinewidth(3)", label=""];
+n20:e -> c27:p24:w [color="black", style="setlinewidth(3)", label=""];
+n20:e -> c30:p25:w [color="black", style="setlinewidth(3)", label=""];
+c49:p46:e -> n21:w [color="black", style="setlinewidth(3)", label=""];
+n21:e -> c27:p25:w [color="black", style="setlinewidth(3)", label=""];
+n21:e -> c28:p24:w [color="black", style="setlinewidth(3)", label=""];
+c50:p46:e -> n22:w [color="black", style="setlinewidth(3)", label=""];
+n22:e -> c28:p25:w [color="black", style="setlinewidth(3)", label=""];
+n22:e -> c29:p24:w [color="black", style="setlinewidth(3)", label=""];
+n23:e -> c35:p25:w [color="black", style="setlinewidth(3)", label=""];
+n23:e -> c36:p25:w [color="black", style="setlinewidth(3)", label=""];
+n23:e -> c37:p25:w [color="black", style="setlinewidth(3)", label=""];
+n23:e -> c38:p25:w [color="black", style="setlinewidth(3)", label=""];
+c29:p26:e -> c42:p24:w [color="black", style="setlinewidth(3)", label=""];
+c30:p26:e -> c43:p24:w [color="black", style="setlinewidth(3)", label=""];
+c31:p26:e -> c40:p39:w [color="black", label=""];
+c32:p26:e -> c41:p39:w [color="black", label=""];
+c33:p26:e -> c42:p39:w [color="black", label=""];
+c34:p26:e -> c43:p39:w [color="black", label=""];
+c35:p26:e -> c31:p24:w [color="black", label=""];
+v0:e -> c31:p25:w [color="black", label=""];
+v1:e -> c32:p25:w [color="black", label=""];
+v2:e -> c33:p25:w [color="black", label=""];
+v3:e -> c34:p25:w [color="black", label=""];
+v4:e -> c35:p24:w [color="black", style="setlinewidth(3)", label=""];
+v5:e -> c36:p24:w [color="black", style="setlinewidth(3)", label=""];
+v6:e -> c37:p24:w [color="black", style="setlinewidth(3)", label=""];
+v7:e -> c38:p24:w [color="black", style="setlinewidth(3)", label=""];
+}
diff --git a/manual/APPNOTE_011_Design_Investigation/submod_02.dot b/manual/APPNOTE_011_Design_Investigation/submod_02.dot
new file mode 100644
index 00000000..1a672c48
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/submod_02.dot
@@ -0,0 +1,33 @@
+digraph "outstage" {
+rankdir="LR";
+remincross=true;
+n4 [ shape=octagon, label="clk", color="black", fontcolor="black" ];
+n5 [ shape=octagon, label="mem[0]", color="black", fontcolor="black" ];
+n6 [ shape=octagon, label="mem[1]", color="black", fontcolor="black" ];
+n7 [ shape=octagon, label="mem[2]", color="black", fontcolor="black" ];
+n8 [ shape=octagon, label="mem[3]", color="black", fontcolor="black" ];
+n9 [ shape=octagon, label="n1", color="black", fontcolor="black" ];
+n10 [ shape=octagon, label="y", color="black", fontcolor="black" ];
+c15 [ shape=record, label="{{<p11> A|<p12> B|<p13> S}|$110\n$mux|{<p14> Y}}" ];
+x0 [ shape=record, style=rounded, label="<s0> 1:1 - 0:0 " ];
+x0:e -> c15:p13:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""];
+c16 [ shape=record, label="{{<p11> A|<p12> B|<p13> S}|$113\n$mux|{<p14> Y}}" ];
+x1 [ shape=record, style=rounded, label="<s0> 0:0 - 0:0 " ];
+x1:e -> c16:p13:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""];
+c17 [ shape=record, label="{{<p11> A|<p12> B|<p13> S}|$116\n$mux|{<p14> Y}}" ];
+x2 [ shape=record, style=rounded, label="<s0> 0:0 - 0:0 " ];
+x2:e -> c17:p13:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""];
+c21 [ shape=record, label="{{<p18> CLK|<p19> D}|$64\n$dff|{<p20> Q}}" ];
+c15:p14:e -> c21:p19:w [color="black", style="setlinewidth(3)", label=""];
+c21:p20:e -> n10:w [color="black", style="setlinewidth(3)", label=""];
+c16:p14:e -> c15:p11:w [color="black", style="setlinewidth(3)", label=""];
+c17:p14:e -> c15:p12:w [color="black", style="setlinewidth(3)", label=""];
+n4:e -> c21:p18:w [color="black", label=""];
+n5:e -> c16:p11:w [color="black", style="setlinewidth(3)", label=""];
+n6:e -> c16:p12:w [color="black", style="setlinewidth(3)", label=""];
+n7:e -> c17:p11:w [color="black", style="setlinewidth(3)", label=""];
+n8:e -> c17:p12:w [color="black", style="setlinewidth(3)", label=""];
+n9:e -> x0:s0:w [color="black", label=""];
+n9:e -> x1:s0:w [color="black", label=""];
+n9:e -> x2:s0:w [color="black", label=""];
+}
diff --git a/manual/APPNOTE_011_Design_Investigation/submod_03.dot b/manual/APPNOTE_011_Design_Investigation/submod_03.dot
new file mode 100644
index 00000000..0dbbe3ba
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/submod_03.dot
@@ -0,0 +1,26 @@
+digraph "selstage" {
+rankdir="LR";
+remincross=true;
+n3 [ shape=octagon, label="d", color="black", fontcolor="black" ];
+n4 [ shape=octagon, label="n1", color="black", fontcolor="black" ];
+n5 [ shape=octagon, label="n2", color="black", fontcolor="black" ];
+n6 [ shape=octagon, label="s1", color="black", fontcolor="black" ];
+n7 [ shape=octagon, label="s2", color="black", fontcolor="black" ];
+c10 [ shape=record, label="{{<p8> A}|$39\n$reduce_bool|{<p9> Y}}" ];
+v0 [ label="4'0000" ];
+c13 [ shape=record, label="{{<p8> A|<p11> B|<p12> S}|$40\n$mux|{<p9> Y}}" ];
+x1 [ shape=record, style=rounded, label="<s1> 3:2 - 1:0 |<s0> 1:0 - 1:0 " ];
+c13:p9:e -> x1:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", style="setlinewidth(3)", label=""];
+c14 [ shape=record, label="{{<p8> A|<p11> B}|$38\n$xor|{<p9> Y}}" ];
+x2 [ shape=record, style=rounded, label="<s1> 1:0 - 3:2 |<s0> 1:0 - 1:0 " ];
+x2:e -> c14:p8:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", style="setlinewidth(3)", label=""];
+c10:p9:e -> c13:p12:w [color="black", label=""];
+c14:p9:e -> c13:p11:w [color="black", style="setlinewidth(3)", label=""];
+n3:e -> c10:p8:w [color="black", style="setlinewidth(3)", label=""];
+n3:e -> c14:p11:w [color="black", style="setlinewidth(3)", label=""];
+x1:s0:e -> n4:w [color="black", style="setlinewidth(3)", label=""];
+x1:s1:e -> n5:w [color="black", style="setlinewidth(3)", label=""];
+n6:e -> x2:s1:w [color="black", style="setlinewidth(3)", label=""];
+n7:e -> x2:s0:w [color="black", style="setlinewidth(3)", label=""];
+v0:e -> c13:p8:w [color="black", style="setlinewidth(3)", label=""];
+}
diff --git a/manual/APPNOTE_011_Design_Investigation/sumprod.v b/manual/APPNOTE_011_Design_Investigation/sumprod.v
new file mode 100644
index 00000000..4091bf0a
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/sumprod.v
@@ -0,0 +1,12 @@
+module sumprod(a, b, c, sum, prod);
+
+ input [7:0] a, b, c;
+ output [7:0] sum, prod;
+
+ {* sumstuff *}
+ assign sum = a + b + c;
+ {* *}
+
+ assign prod = a * b * c;
+
+endmodule
diff --git a/manual/APPNOTE_011_Design_Investigation/sumprod_00.dot b/manual/APPNOTE_011_Design_Investigation/sumprod_00.dot
new file mode 100644
index 00000000..06522dcc
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/sumprod_00.dot
@@ -0,0 +1,18 @@
+digraph "sumprod" {
+rankdir="LR";
+remincross=true;
+v0 [ label="a" ];
+v1 [ label="b" ];
+v2 [ label="$1_Y" ];
+c4 [ shape=record, label="{{<p1> A|<p2> B}|$1\n$add|{<p3> Y}}" ];
+v3 [ label="$1_Y" ];
+v4 [ label="c" ];
+v5 [ label="sum" ];
+c5 [ shape=record, label="{{<p1> A|<p2> B}|$2\n$add|{<p3> Y}}" ];
+v0:e -> c4:p1:w [color="black", style="setlinewidth(3)", label=""];
+v1:e -> c4:p2:w [color="black", style="setlinewidth(3)", label=""];
+c4:p3:e -> v2:w [color="black", style="setlinewidth(3)", label=""];
+v3:e -> c5:p1:w [color="black", style="setlinewidth(3)", label=""];
+v4:e -> c5:p2:w [color="black", style="setlinewidth(3)", label=""];
+c5:p3:e -> v5:w [color="black", style="setlinewidth(3)", label=""];
+}
diff --git a/manual/APPNOTE_011_Design_Investigation/sumprod_01.dot b/manual/APPNOTE_011_Design_Investigation/sumprod_01.dot
new file mode 100644
index 00000000..aefe7a6d
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/sumprod_01.dot
@@ -0,0 +1,15 @@
+digraph "sumprod" {
+rankdir="LR";
+remincross=true;
+n2 [ shape=octagon, label="a", color="black", fontcolor="black" ];
+n3 [ shape=octagon, label="b", color="black", fontcolor="black" ];
+n4 [ shape=octagon, label="c", color="black", fontcolor="black" ];
+n5 [ shape=octagon, label="sum", color="black", fontcolor="black" ];
+c9 [ shape=record, label="{{<p6> A|<p7> B}|$1\n$add|{<p8> Y}}" ];
+c10 [ shape=record, label="{{<p6> A|<p7> B}|$2\n$add|{<p8> Y}}" ];
+c9:p8:e -> c10:p6:w [color="black", style="setlinewidth(3)", label=""];
+n2:e -> c9:p6:w [color="black", style="setlinewidth(3)", label=""];
+n3:e -> c9:p7:w [color="black", style="setlinewidth(3)", label=""];
+n4:e -> c10:p7:w [color="black", style="setlinewidth(3)", label=""];
+c10:p8:e -> n5:w [color="black", style="setlinewidth(3)", label=""];
+}
diff --git a/manual/APPNOTE_011_Design_Investigation/sumprod_02.dot b/manual/APPNOTE_011_Design_Investigation/sumprod_02.dot
new file mode 100644
index 00000000..4646c994
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/sumprod_02.dot
@@ -0,0 +1,5 @@
+digraph "sumprod" {
+rankdir="LR";
+remincross=true;
+n1 [ shape=octagon, label="prod", color="black", fontcolor="black" ];
+}
diff --git a/manual/APPNOTE_011_Design_Investigation/sumprod_03.dot b/manual/APPNOTE_011_Design_Investigation/sumprod_03.dot
new file mode 100644
index 00000000..dcfea2b5
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/sumprod_03.dot
@@ -0,0 +1,11 @@
+digraph "sumprod" {
+rankdir="LR";
+remincross=true;
+n1 [ shape=octagon, label="prod", color="black", fontcolor="black" ];
+v0 [ label="$3_Y" ];
+v1 [ label="c" ];
+c5 [ shape=record, label="{{<p2> A|<p3> B}|$4\n$mul|{<p4> Y}}" ];
+c5:p4:e -> n1:w [color="black", style="setlinewidth(3)", label=""];
+v0:e -> c5:p2:w [color="black", style="setlinewidth(3)", label=""];
+v1:e -> c5:p3:w [color="black", style="setlinewidth(3)", label=""];
+}
diff --git a/manual/APPNOTE_011_Design_Investigation/sumprod_04.dot b/manual/APPNOTE_011_Design_Investigation/sumprod_04.dot
new file mode 100644
index 00000000..e77c41aa
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/sumprod_04.dot
@@ -0,0 +1,11 @@
+digraph "sumprod" {
+rankdir="LR";
+remincross=true;
+n2 [ shape=octagon, label="c", color="black", fontcolor="black" ];
+n3 [ shape=octagon, label="prod", color="black", fontcolor="black" ];
+c7 [ shape=record, label="{{<p4> A|<p5> B}|$4\n$mul|{<p6> Y}}" ];
+n1 [ shape=diamond, label="$3_Y" ];
+n1:e -> c7:p4:w [color="black", style="setlinewidth(3)", label=""];
+n2:e -> c7:p5:w [color="black", style="setlinewidth(3)", label=""];
+c7:p6:e -> n3:w [color="black", style="setlinewidth(3)", label=""];
+}
diff --git a/manual/APPNOTE_011_Design_Investigation/sumprod_05.dot b/manual/APPNOTE_011_Design_Investigation/sumprod_05.dot
new file mode 100644
index 00000000..b5444129
--- /dev/null
+++ b/manual/APPNOTE_011_Design_Investigation/sumprod_05.dot
@@ -0,0 +1,15 @@
+digraph "sumprod" {
+rankdir="LR";
+remincross=true;
+n2 [ shape=octagon, label="c", color="black", fontcolor="black" ];
+n3 [ shape=octagon, label="prod", color="black", fontcolor="black" ];
+v0 [ label="a" ];
+v1 [ label="b" ];
+c7 [ shape=record, label="{{<p4> A|<p5> B}|$3\n$mul|{<p6> Y}}" ];
+c8 [ shape=record, label="{{<p4> A|<p5> B}|$4\n$mul|{<p6> Y}}" ];
+c7:p6:e -> c8:p4:w [color="black", style="setlinewidth(3)", label=""];
+n2:e -> c8:p5:w [color="black", style="setlinewidth(3)", label=""];
+c8:p6:e -> n3:w [color="black", style="setlinewidth(3)", label=""];
+v0:e -> c7:p4:w [color="black", style="setlinewidth(3)", label=""];
+v1:e -> c7:p5:w [color="black", style="setlinewidth(3)", label=""];
+}
diff --git a/manual/APPNOTE_012_Verilog_to_BTOR.tex b/manual/APPNOTE_012_Verilog_to_BTOR.tex
new file mode 100644
index 00000000..1bc27787
--- /dev/null
+++ b/manual/APPNOTE_012_Verilog_to_BTOR.tex
@@ -0,0 +1,435 @@
+
+% IEEEtran howto:
+% http://ftp.univie.ac.at/packages/tex/macros/latex/contrib/IEEEtran/IEEEtran_HOWTO.pdf
+\documentclass[9pt,technote,a4paper]{IEEEtran}
+
+\usepackage[T1]{fontenc} % required for luximono!
+\usepackage[scaled=0.8]{luximono} % typewriter font with bold face
+
+% To install the luximono font files:
+% getnonfreefonts-sys --all or
+% getnonfreefonts-sys luximono
+%
+% when there are trouble you might need to:
+% - Create /etc/texmf/updmap.d/99local-luximono.cfg
+% containing the single line: Map ul9.map
+% - Run update-updmap followed by mktexlsr and updmap-sys
+%
+% This commands must be executed as root with a root environment
+% (i.e. run "sudo su" and then execute the commands in the root
+% shell, don't just prefix the commands with "sudo").
+
+\usepackage[unicode,bookmarks=false]{hyperref}
+\usepackage[english]{babel}
+\usepackage[utf8]{inputenc}
+\usepackage{amssymb}
+\usepackage{amsmath}
+\usepackage{amsfonts}
+\usepackage{units}
+\usepackage{nicefrac}
+\usepackage{eurosym}
+\usepackage{graphicx}
+\usepackage{verbatim}
+\usepackage{algpseudocode}
+\usepackage{scalefnt}
+\usepackage{xspace}
+\usepackage{color}
+\usepackage{colortbl}
+\usepackage{multirow}
+\usepackage{hhline}
+\usepackage{listings}
+\usepackage{float}
+
+\usepackage{tikz}
+\usetikzlibrary{calc}
+\usetikzlibrary{arrows}
+\usetikzlibrary{scopes}
+\usetikzlibrary{through}
+\usetikzlibrary{shapes.geometric}
+
+\lstset{basicstyle=\ttfamily,frame=trBL,xleftmargin=2em,xrightmargin=1em,numbers=left}
+
+\begin{document}
+
+\title{Yosys Application Note 012: \\ Converting Verilog to BTOR}
+\author{Ahmed Irfan and Clifford Wolf \\ April 2015}
+\maketitle
+
+\begin{abstract}
+Verilog-2005 is a powerful Hardware Description Language (HDL) that
+can be used to easily create complex designs from small HDL code.
+BTOR~\cite{btor} is a bit-precise word-level format for model
+checking. It is a simple format and easy to parse. It allows to model
+the model checking problem over the theory of bit-vectors with
+one-dimensional arrays, thus enabling to model Verilog designs with
+registers and memories. Yosys~\cite{yosys} is an Open-Source Verilog
+synthesis tool that can be used to convert Verilog designs with simple
+assertions to BTOR format.
+
+\end{abstract}
+
+\section{Installation}
+
+Yosys written in C++ (using features from C++11) and is tested on
+modern Linux. It should compile fine on most UNIX systems with a
+C++11 compiler. The README file contains useful information on
+building Yosys and its prerequisites.
+
+Yosys is a large and feature-rich program with some dependencies. For
+this work, we may deactivate other extra features such as {\tt TCL}
+and {\tt ABC} support in the {\tt Makefile}.
+
+\bigskip
+
+This Application Note is based on GIT Rev. {\tt 082550f} from
+2015-04-04 of Yosys~\cite{yosys}.
+
+\section{Quick Start}
+
+We assume that the Verilog design is synthesizable and we also assume
+that the design does not have multi-dimensional memories. As BTOR
+implicitly initializes registers to zero value and memories stay
+uninitialized, we assume that the Verilog design does
+not contain initial blocks. For more details about the BTOR format,
+please refer to~\cite{btor}.
+
+We provide a shell script {\tt verilog2btor.sh} which can be used to
+convert a Verilog design to BTOR. The script can be found in the
+{\tt backends/btor} directory. The following example shows its usage:
+
+\begin{figure}[H]
+\begin{lstlisting}[language=sh,numbers=none]
+verilog2btor.sh fsm.v fsm.btor test
+\end{lstlisting}
+ \renewcommand{\figurename}{Listing}
+\caption{Using verilog2btor script}
+\end{figure}
+
+The script {\tt verilog2btor.sh} takes three parameters. In the above
+example, the first parameter {\tt fsm.v} is the input design, the second
+parameter {\tt fsm.btor} is the file name of BTOR output, and the third
+parameter {\tt test} is the name of top module in the design.
+
+To specify the properties (that need to be checked), we have two
+options:
+\begin{itemize}
+\item We can use the Verilog {\tt assert} statement in the procedural block
+ or module body of the Verilog design, as shown in
+ Listing~\ref{specifying_property_assert}. This is the preferred option.
+\item We can use a single-bit output wire, whose name starts with
+ {\tt safety}. The value of this output wire needs to be driven low
+ when the property is met, i.e. the solver will try to find a model
+ that makes the safety pin go high. This is demonstrated in
+ Listing~\ref{specifying_property_output}.
+\end{itemize}
+
+\begin{figure}[H]
+\begin{lstlisting}[language=Verilog,numbers=none]
+module test(input clk, input rst, output y);
+
+ reg [2:0] state;
+
+ always @(posedge clk) begin
+ if (rst || state == 3) begin
+ state <= 0;
+ end else begin
+ assert(state < 3);
+ state <= state + 1;
+ end
+ end
+
+ assign y = state[2];
+
+ assert property (y !== 1'b1);
+
+endmodule
+\end{lstlisting}
+\renewcommand{\figurename}{Listing}
+\caption{Specifying property in Verilog design with {\tt assert}}
+\label{specifying_property_assert}
+\end{figure}
+
+\begin{figure}[H]
+\begin{lstlisting}[language=Verilog,numbers=none]
+module test(input clk, input rst,
+ output y, output safety1);
+
+ reg [2:0] state;
+
+ always @(posedge clk) begin
+ if (rst || state == 3)
+ state <= 0;
+ else
+ state <= state + 1;
+ end
+
+ assign y = state[2];
+
+ assign safety1 = !(y !== 1'b1);
+
+endmodule
+\end{lstlisting}
+\renewcommand{\figurename}{Listing}
+\caption{Specifying property in Verilog design with output wire}
+\label{specifying_property_output}
+\end{figure}
+
+We can run Boolector~\cite{boolector}~$1.4.1$\footnote{
+Newer version of Boolector do not support sequential models.
+Boolector 1.4.1 can be built with picosat-951. Newer versions
+of picosat have an incompatible API.} on the generated BTOR
+file:
+
+\begin{figure}[H]
+\begin{lstlisting}[language=sh,numbers=none]
+$ boolector fsm.btor
+unsat
+\end{lstlisting}
+ \renewcommand{\figurename}{Listing}
+\caption{Running boolector on BTOR file}
+\end{figure}
+
+We can also use nuXmv~\cite{nuxmv}, but on BTOR designs it does not
+support memories yet. With the next release of nuXmv, we will be also
+able to verify designs with memories.
+
+\section{Detailed Flow}
+
+Yosys is able to synthesize Verilog designs up to the gate level.
+We are interested in keeping registers and memories when synthesizing
+the design. For this purpose, we describe a customized Yosys synthesis
+flow, that is also provided by the {\tt verilog2btor.sh} script.
+Listing~\ref{btor_script_memory} shows the Yosys commands that are
+executed by {\tt verilog2btor.sh}.
+
+\begin{figure}[H]
+\begin{lstlisting}[language=sh]
+read_verilog -sv $1;
+hierarchy -top $3; hierarchy -libdir $DIR;
+hierarchy -check;
+proc; opt;
+opt_expr -mux_undef; opt;
+rename -hide;;;
+splice; opt;
+memory_dff -wr_only; memory_collect;;
+flatten;;
+memory_unpack;
+splitnets -driver;
+setundef -zero -undriven;
+opt;;;
+write_btor $2;
+\end{lstlisting}
+ \renewcommand{\figurename}{Listing}
+\caption{Synthesis Flow for BTOR with memories}
+\label{btor_script_memory}
+\end{figure}
+
+Here is short description of what is happening in the script line by
+line:
+
+\begin{enumerate}
+\item Reading the input file.
+\item Setting the top module in the hierarchy and trying to read
+ automatically the files which are given as {\tt include} in the file
+ read in first line.
+\item Checking the design hierarchy.
+\item Converting processes to multiplexers (muxs) and flip-flops.
+\item Removing undef signals from muxs.
+\item Hiding all signal names that are not used as module ports.
+\item Explicit type conversion, by introducing slice and concat cells
+ in the circuit.
+\item Converting write memories to synchronous memories, and
+ collecting the memories to multi-port memories.
+\item Flattening the design to get only one module.
+\item Separating read and write memories.
+\item Splitting the signals that are partially assigned
+\item Setting undef to zero value.
+\item Final optimization pass.
+\item Writing BTOR file.
+\end{enumerate}
+
+For detailed description of the commands mentioned above, please refer
+to the Yosys documentation, or run {\tt yosys -h \it command\_name}.
+
+The script presented earlier can be easily modified to have a BTOR
+file that does not contain memories. This is done by removing the line
+number~8 and 10, and introduces a new command {\tt memory} at line
+number~8. Listing~\ref{btor_script_without_memory} shows the
+modified Yosys script file:
+
+\begin{figure}[H]
+\begin{lstlisting}[language=sh,numbers=none]
+read_verilog -sv $1;
+hierarchy -top $3; hierarchy -libdir $DIR;
+hierarchy -check;
+proc; opt;
+opt_expr -mux_undef; opt;
+rename -hide;;;
+splice; opt;
+memory;;
+flatten;;
+splitnets -driver;
+setundef -zero -undriven;
+opt;;;
+write_btor $2;
+\end{lstlisting}
+ \renewcommand{\figurename}{Listing}
+\caption{Synthesis Flow for BTOR without memories}
+\label{btor_script_without_memory}
+\end{figure}
+
+\section{Example}
+
+Here is an example Verilog design that we want to convert to BTOR:
+
+\begin{figure}[H]
+\begin{lstlisting}[language=Verilog,numbers=none]
+module array(input clk);
+
+ reg [7:0] counter;
+ reg [7:0] mem [7:0];
+
+ always @(posedge clk) begin
+ counter <= counter + 8'd1;
+ mem[counter] <= counter;
+ end
+
+ assert property (!(counter > 8'd0) ||
+ mem[counter - 8'd1] == counter - 8'd1);
+
+endmodule
+\end{lstlisting}
+\renewcommand{\figurename}{Listing}
+\caption{Example - Verilog Design}
+\label{example_verilog}
+\end{figure}
+
+The generated BTOR file that contain memories, using the script shown
+in Listing~\ref{btor_script_memory}:
+\begin{figure}[H]
+\begin{lstlisting}[numbers=none]
+1 var 1 clk
+2 array 8 3
+3 var 8 $auto$rename.cc:150:execute$20
+4 const 8 00000001
+5 sub 8 3 4
+6 slice 3 5 2 0
+7 read 8 2 6
+8 slice 3 3 2 0
+9 add 8 3 4
+10 const 8 00000000
+11 ugt 1 3 10
+12 not 1 11
+13 const 8 11111111
+14 slice 1 13 0 0
+15 one 1
+16 eq 1 1 15
+17 and 1 16 14
+18 write 8 3 2 8 3
+19 acond 8 3 17 18 2
+20 anext 8 3 2 19
+21 eq 1 7 5
+22 or 1 12 21
+23 const 1 1
+24 one 1
+25 eq 1 23 24
+26 cond 1 25 22 24
+27 root 1 -26
+28 cond 8 1 9 3
+29 next 8 3 28
+\end{lstlisting}
+\renewcommand{\figurename}{Listing}
+\caption{Example - Converted BTOR with memory}
+\label{example_btor}
+\end{figure}
+
+And the BTOR file obtained by the script shown in
+Listing~\ref{btor_script_without_memory}, which expands the memory
+into individual elements:
+\begin{figure}[H]
+\begin{lstlisting}[numbers=none,escapechar=@]
+1 var 1 clk
+2 var 8 mem[0]
+3 var 8 $auto$rename.cc:150:execute$20
+4 slice 3 3 2 0
+5 slice 1 4 0 0
+6 not 1 5
+7 slice 1 4 1 1
+8 not 1 7
+9 slice 1 4 2 2
+10 not 1 9
+11 and 1 8 10
+12 and 1 6 11
+13 cond 8 12 3 2
+14 cond 8 1 13 2
+15 next 8 2 14
+16 const 8 00000001
+17 add 8 3 16
+18 const 8 00000000
+19 ugt 1 3 18
+20 not 1 19
+21 var 8 mem[2]
+22 and 1 7 10
+23 and 1 6 22
+24 cond 8 23 3 21
+25 cond 8 1 24 21
+26 next 8 21 25
+27 sub 8 3 16
+
+@\vbox to 0pt{\vss\vdots\vskip3pt}@
+54 cond 1 53 50 52
+55 root 1 -54
+
+@\vbox to 0pt{\vss\vdots\vskip3pt}@
+77 cond 8 76 3 44
+78 cond 8 1 77 44
+79 next 8 44 78
+\end{lstlisting}
+\renewcommand{\figurename}{Listing}
+\caption{Example - Converted BTOR without memory}
+\label{example_btor}
+\end{figure}
+
+\section{Limitations}
+
+BTOR does not support initialization of memories and registers, i.e. they are
+implicitly initialized to value zero, so the initial block for
+memories need to be removed when converting to BTOR. It should
+also be kept in consideration that BTOR does not support the {\tt x} or {\tt z}
+values of Verilog.
+
+Another thing to bear in mind is that Yosys will convert multi-dimensional
+memories to one-dimensional memories and address decoders. Therefore
+out-of-bounds memory accesses can yield unexpected results.
+
+\section{Conclusion}
+
+Using the described flow, we can use Yosys to generate word-level
+verification benchmarks with or without memories from Verilog designs.
+
+\begin{thebibliography}{9}
+
+\bibitem{yosys}
+Clifford Wolf. The Yosys Open SYnthesis Suite. \\
+\url{http://www.clifford.at/yosys/}
+
+\bibitem{boolector}
+Robert Brummayer and Armin Biere, Boolector: An Efficient SMT Solver for Bit-Vectors and Arrays\\
+\url{http://fmv.jku.at/boolector/}
+
+\bibitem{btor}
+Robert Brummayer and Armin Biere and Florian Lonsing, BTOR:
+Bit-Precise Modelling of Word-Level Problems for Model Checking\\
+\url{http://fmv.jku.at/papers/BrummayerBiereLonsing-BPR08.pdf}
+
+\bibitem{nuxmv}
+Roberto Cavada and Alessandro Cimatti and Michele Dorigatti and
+Alberto Griggio and Alessandro Mariotti and Andrea Micheli and Sergio
+Mover and Marco Roveri and Stefano Tonetta, The nuXmv Symbolic Model
+Checker\\
+\url{https://es-static.fbk.eu/tools/nuxmv/index.php}
+
+\end{thebibliography}
+
+
+\end{document}
diff --git a/manual/CHAPTER_Appnotes.tex b/manual/CHAPTER_Appnotes.tex
new file mode 100644
index 00000000..e0d09329
--- /dev/null
+++ b/manual/CHAPTER_Appnotes.tex
@@ -0,0 +1,29 @@
+
+\chapter{Application Notes}
+\label{chapter:appnotes}
+
+% \begin{fixme}
+% This appendix will cover some typical use-cases of Yosys in the form of application notes.
+% \end{fixme}
+%
+% \section{Synthesizing using a Cell Library in Liberty Format}
+% \section{Reverse Engineering the MOS6502 from an NMOS Transistor Netlist}
+% \section{Reconfigurable Coarse-Grain Synthesis using Intersynth}
+
+This appendix contains copies of the Yosys application notes.
+
+\begin{itemize}
+\item Yosys AppNote 010: Converting Verilog to BLIF \dotfill Page \pageref{app:010} \hskip2cm\null
+\item Yosys AppNote 011: Interactive Design Investigation \dotfill Page \pageref{app:011} \hskip2cm\null
+\item Yosys AppNote 012: Converting Verilog to BTOR \dotfill Page \pageref{app:012} \hskip2cm\null
+\end{itemize}
+
+\eject\label{app:010}
+\includepdf[pages=-,pagecommand=\thispagestyle{plain}]{APPNOTE_010_Verilog_to_BLIF.pdf}
+
+\eject\label{app:011}
+\includepdf[pages=-,pagecommand=\thispagestyle{plain}]{APPNOTE_011_Design_Investigation.pdf}
+
+\eject\label{app:012}
+\includepdf[pages=-,pagecommand=\thispagestyle{plain}]{APPNOTE_012_Verilog_to_BTOR.pdf}
+
diff --git a/manual/CHAPTER_Approach.tex b/manual/CHAPTER_Approach.tex
new file mode 100644
index 00000000..4b170ee0
--- /dev/null
+++ b/manual/CHAPTER_Approach.tex
@@ -0,0 +1,145 @@
+
+\chapter{Approach}
+\label{chapter:approach}
+
+Yosys is a tool for synthesising (behavioural) Verilog HDL code to target architecture netlists. Yosys aims at a wide
+range of application domains and thus must be flexible and easy to adapt to new tasks. This chapter covers the general
+approach followed in the effort to implement this tool.
+
+\section{Data- and Control-Flow}
+
+The data- and control-flow of a typical synthesis tool is very similar to the data- and control-flow of a typical
+compiler: different subsystems are called in a predetermined order, each consuming the data generated by the
+last subsystem and generating the data for the next subsystem (see Fig.~\ref{fig:approach_flow}).
+
+\begin{figure}[b]
+ \hfil
+ \begin{tikzpicture}
+ \path (-1.5,3) coordinate (cursor);
+ \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0);
+ \draw[fill=orange!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Frontend} ++(1,3) coordinate (cursor);
+ \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0);
+ \draw[fill=green!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Pass} ++(1,3) coordinate (cursor);
+ \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0);
+ \draw[fill=green!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Pass} ++(1,3) coordinate (cursor);
+ \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0);
+ \draw[fill=green!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Pass} ++(1,3) coordinate (cursor);
+ \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0);
+ \draw[fill=orange!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Backend} ++(1,3) coordinate (cursor);
+ \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0);
+
+ \path (-3,-0.5) coordinate (cursor);
+ \draw (cursor) -- node[below] {HDL} ++(3,0) coordinate (cursor);
+ \draw[|-|] (cursor) -- node[below] {Internal Format(s)} ++(8,0) coordinate (cursor);
+ \draw (cursor) -- node[below] {Netlist} ++(3,0);
+
+ \path (-3,3.5) coordinate (cursor);
+ \draw[-] (cursor) -- node[above] {High-Level} ++(3,0) coordinate (cursor);
+ \draw[-] (cursor) -- ++(8,0) coordinate (cursor);
+ \draw[->] (cursor) -- node[above] {Low-Level} ++(3,0);
+
+ \end{tikzpicture}
+ \caption{General data- and control-flow of a synthesis tool}
+ \label{fig:approach_flow}
+\end{figure}
+
+The first subsystem to be called is usually called a {\it frontend}. It does not process the data generated by
+another subsystem but instead reads the user input---in the case of a HDL synthesis tool, the behavioural
+HDL code.
+
+The subsystems that consume data from previous subsystems and produce data for the next subsystems (usually in the
+same or a similar format) are called {\it passes}.
+
+The last subsystem that is executed transforms the data generated by the last pass into a suitable output
+format and writes it to a disk file. This subsystem is usually called the {\it backend}.
+
+In Yosys all frontends, passes and backends are directly available as commands in the synthesis script. Thus
+the user can easily create a custom synthesis flow just by calling passes in the right order in a synthesis
+script.
+
+\section{Internal Formats in Yosys}
+
+Yosys uses two different internal formats. The first is used to store an abstract syntax tree (AST) of a Verilog
+input file. This format is simply called {\it AST} and is generated by the Verilog Frontend. This data structure
+is consumed by a subsystem called {\it AST Frontend}\footnote{In Yosys the term {\it pass} is only used to
+refer to commands that operate on the RTLIL data structure.}. This AST Frontend then generates a design in Yosys'
+main internal format, the Register-Transfer-Level-Intermediate-Language (RTLIL) representation. It does that
+by first performing a number of simplifications within the AST representation and then generating RTLIL from
+the simplified AST data structure.
+
+The RTLIL representation is used by all passes as input and outputs. This has the following advantages over
+using different representational formats between different passes:
+
+\begin{itemize}
+\item The passes can be rearranged in a different order and passes can be removed or inserted.
+\item Passes can simply pass-thru the parts of the design they don't change without the need
+ to convert between formats. In fact Yosys passes output the same data structure they received
+ as input and performs all changes in place.
+\item All passes use the same interface, thus reducing the effort required to understand a pass
+ when reading the Yosys source code, e.g.~when adding additional features.
+\end{itemize}
+
+The RTLIL representation is basically a netlist representation with the following additional features:
+
+\begin{itemize}
+\item An internal cell library with fixed-function cells to represent RTL datapath and register cells as well
+as logical gate-level cells (single-bit gates and registers).
+\item Support for multi-bit values that can use individual bits from wires as well as constant bits to
+represent coarse-grain netlists.
+\item Support for basic behavioural constructs (if-then-else structures and multi-case switches with
+a sensitivity list for updating the outputs).
+\item Support for multi-port memories.
+\end{itemize}
+
+The use of RTLIL also has the disadvantage of having a very powerful format
+between all passes, even when doing gate-level synthesis where the more
+advanced features are not needed. In order to reduce complexity for passes that
+operate on a low-level representation, these passes check the features used in
+the input RTLIL and fail to run when unsupported high-level constructs are
+used. In such cases a pass that transforms the higher-level constructs to
+lower-level constructs must be called from the synthesis script first.
+
+\section{Typical Use Case}
+\label{sec:typusecase}
+
+The following example script may be used in a synthesis flow to convert the behavioural Verilog code
+from the input file {\tt design.v} to a gate-level netlist {\tt synth.v} using the cell library
+described by the Liberty file \citeweblink{LibertyFormat} {\tt cells.lib}:
+
+\begin{lstlisting}[language=sh,numbers=left,frame=single]
+# read input file to internal representation
+read_verilog design.v
+
+# convert high-level behavioral parts ("processes") to d-type flip-flops and muxes
+proc
+
+# perform some simple optimizations
+opt
+
+# convert high-level memory constructs to d-type flip-flops and multiplexers
+memory
+
+# perform some simple optimizations
+opt
+
+# convert design to (logical) gate-level netlists
+techmap
+
+# perform some simple optimizations
+opt
+
+# map internal register types to the ones from the cell library
+dfflibmap -liberty cells.lib
+
+# use ABC to map remaining logic to cells from the cell library
+abc -liberty cells.lib
+
+# cleanup
+opt
+
+# write results to output file
+write_verilog synth.v
+\end{lstlisting}
+
+A detailed description of the commands available in Yosys can be found in App.~\ref{commandref}.
+
diff --git a/manual/CHAPTER_Auxlibs.tex b/manual/CHAPTER_Auxlibs.tex
new file mode 100644
index 00000000..440ea137
--- /dev/null
+++ b/manual/CHAPTER_Auxlibs.tex
@@ -0,0 +1,35 @@
+
+\chapter{Auxiliary Libraries}
+
+The Yosys source distribution contains some auxiliary libraries that are bundled
+with Yosys.
+
+\section{SHA1}
+
+The files in {\tt libs/sha1/} provide a public domain SHA1 implementation written
+by Steve Reid, Bruce Guenter, and Volker Grabsch. It is used for generating
+unique names when specializing parameterized modules.
+
+\section{BigInt}
+
+The files in {\tt libs/bigint/} provide a library for performing arithmetic with
+arbitrary length integers. It is written by Matt McCutchen \citeweblink{bigint}.
+
+The BigInt library is used for evaluating constant expressions, e.g.~using the {\tt
+ConstEval} class provided in {\tt kernel/consteval.h}.
+
+\section{SubCircuit}
+\label{sec:SubCircuit}
+
+The files in {\tt libs/subcircuit} provide a library for solving the subcircuit
+isomorphism problem. It is written by Clifford Wolf and based on the Ullmann
+Subgraph Isomorphism Algorithm \cite{UllmannSubgraphIsomorphism}. It is used by
+the {\tt extract} pass (see {\tt help extract} or Sec.~\ref{cmd:extract}).
+
+\section{ezSAT}
+
+The files in {\tt libs/ezsat} provide a library for simplifying generating CNF
+formulas for SAT solvers. It also contains bindings of MiniSAT. The ezSAT
+library is written by Clifford Wolf. It is used by the {\tt sat} pass (see
+{\tt help sat} or Sec.~\ref{cmd:sat}).
+
diff --git a/manual/CHAPTER_Auxprogs.tex b/manual/CHAPTER_Auxprogs.tex
new file mode 100644
index 00000000..724d37f0
--- /dev/null
+++ b/manual/CHAPTER_Auxprogs.tex
@@ -0,0 +1,25 @@
+
+\chapter{Auxiliary Programs}
+
+Besides the main {\tt yosys} executable, the Yosys distribution contains a set
+of additional helper programs.
+
+\section{yosys-config}
+
+The {\tt yosys-config} tool (an auto-generated shell-script) can be used to
+query compiler options and other information needed for building loadable
+modules for Yosys. FIXME: See Sec.~\ref{chapter:prog} for details.
+
+\section{yosys-filterlib}
+\label{sec:filterlib}
+
+The {\tt yosys-filterlib} tool is a small utility that can be used to strip
+or extract information from a Liberty file. See Sec.~\ref{sec:techmap_extern}
+for details.
+
+\section{yosys-abc}
+
+This is a unmodified copy of ABC \citeweblink{ABC}. Not all versions of Yosys
+work with all versions of ABC. So Yosys comes with its own yosys-abc to avoid
+compatibility issues between the two.
+
diff --git a/manual/CHAPTER_Basics.tex b/manual/CHAPTER_Basics.tex
new file mode 100644
index 00000000..5c60b730
--- /dev/null
+++ b/manual/CHAPTER_Basics.tex
@@ -0,0 +1,839 @@
+
+\chapter{Basic Principles}
+\label{chapter:basics}
+
+This chapter contains a short introduction to the basic principles of digital
+circuit synthesis.
+
+\section{Levels of Abstraction}
+
+Digital circuits can be represented at different levels of abstraction.
+During the design process a circuit is usually first specified using a higher
+level abstraction. Implementation can then be understood as finding a
+functionally equivalent representation at a lower abstraction level. When
+this is done automatically using software, the term {\it synthesis} is used.
+
+So synthesis is the automatic conversion of a high-level representation of a
+circuit to a functionally equivalent low-level representation of a circuit.
+Figure~\ref{fig:Basics_abstractions} lists the different levels of abstraction
+and how they relate to different kinds of synthesis.
+
+\begin{figure}[b!]
+ \hfil
+ \begin{tikzpicture}
+ \tikzstyle{lvl} = [draw, fill=green!10, rectangle, minimum height=2em, minimum width=15em]
+ \node[lvl] (sys) {System Level};
+ \node[lvl] (hl) [below of=sys] {High Level};
+ \node[lvl] (beh) [below of=hl] {Behavioral Level};
+ \node[lvl] (rtl) [below of=beh] {Register-Transfer Level (RTL)};
+ \node[lvl] (lg) [below of=rtl] {Logical Gate Level};
+ \node[lvl] (pg) [below of=lg] {Physical Gate Level};
+ \node[lvl] (sw) [below of=pg] {Switch Level};
+
+ \draw[dotted] (sys.east) -- ++(1,0) coordinate (sysx);
+ \draw[dotted] (hl.east) -- ++(1,0) coordinate (hlx);
+ \draw[dotted] (beh.east) -- ++(1,0) coordinate (behx);
+ \draw[dotted] (rtl.east) -- ++(1,0) coordinate (rtlx);
+ \draw[dotted] (lg.east) -- ++(1,0) coordinate (lgx);
+ \draw[dotted] (pg.east) -- ++(1,0) coordinate (pgx);
+ \draw[dotted] (sw.east) -- ++(1,0) coordinate (swx);
+
+ \draw[gray,|->] (sysx) -- node[right] {System Design} (hlx);
+ \draw[|->|] (hlx) -- node[right] {High Level Synthesis (HLS)} (behx);
+ \draw[->|] (behx) -- node[right] {Behavioral Synthesis} (rtlx);
+ \draw[->|] (rtlx) -- node[right] {RTL Synthesis} (lgx);
+ \draw[->|] (lgx) -- node[right] {Logic Synthesis} (pgx);
+ \draw[gray,->|] (pgx) -- node[right] {Cell Library} (swx);
+
+ \draw[dotted] (behx) -- ++(5,0) coordinate (a);
+ \draw[dotted] (pgx) -- ++(5,0) coordinate (b);
+ \draw[|->|] (a) -- node[right] {Yosys} (b);
+ \end{tikzpicture}
+ \caption{Different levels of abstraction and synthesis.}
+ \label{fig:Basics_abstractions}
+\end{figure}
+
+Regardless of the way a lower level representation of a circuit is
+obtained (synthesis or manual design), the lower level representation is usually
+verified by comparing simulation results of the lower level and the higher level
+representation \footnote{In recent years formal equivalence
+checking also became an important verification method for validating RTL and
+lower abstraction representation of the design.}.
+Therefore even if no synthesis is used, there must still be a simulatable
+representation of the circuit in all levels to allow for verification of the
+design.
+
+Note: The exact meaning of terminology such as ``High-Level'' is of course not
+fixed over time. For example the HDL ``ABEL'' was first introduced in 1985 as ``A High-Level
+Design Language for Programmable Logic Devices'' \cite{ABEL}, but would not
+be considered a ``High-Level Language'' today.
+
+\subsection{System Level}
+
+The System Level abstraction of a system only looks at its biggest building
+blocks like CPUs and computing cores. At this level the circuit is usually described
+using traditional programming languages like C/C++ or Matlab. Sometimes special
+software libraries are used that are aimed at simulation circuits on the system
+level, such as SystemC.
+
+Usually no synthesis tools are used to automatically transform a system level
+representation of a circuit to a lower-level representation. But system level
+design tools exist that can be used to connect system level building blocks.
+
+The IEEE 1685-2009 standard defines the IP-XACT file format that can be used to
+represent designs on the system level and building blocks that can be used in
+such system level designs. \cite{IP-XACT}
+
+\subsection{High Level}
+
+The high-level abstraction of a system (sometimes referred to as {\it
+algorithmic} level) is also often represented using traditional programming
+languages, but with a reduced feature set. For example when representing a
+design at the high level abstraction in C, pointers can only be used to mimic
+concepts that can be found in hardware, such as memory interfaces. Full
+featured dynamic memory management is not allowed as it has no corresponding
+concept in digital circuits.
+
+Tools exist to synthesize high level code (usually in the form of C/C++/SystemC
+code with additional metadata) to behavioural HDL code (usually in the form of
+Verilog or VHDL code). Aside from the many commercial tools for high level synthesis
+there are also a number of FOSS tools for high level synthesis
+\citeweblink{C_to_Verilog} \citeweblink{LegUp}.
+
+\subsection{Behavioural Level}
+
+At the behavioural abstraction level a language aimed at hardware description such
+as Verilog or VHDL is used to describe the circuit, but so-called {\it behavioural
+modelling} is used in at least part of the circuit description. In behavioural
+modelling there must be a language feature that allows for imperative programming to be used to
+describe data paths and registers. This is the {\tt always}-block in Verilog and
+the {\tt process}-block in VHDL.
+
+In behavioural modelling, code fragments are provided together with a {\it
+sensitivity list}; a list of signals and conditions. In simulation, the code
+fragment is executed whenever a signal in the sensitivity list changes its
+value or a condition in the sensitivity list is triggered. A synthesis tool
+must be able to transfer this representation into an appropriate datapath followed
+by the appropriate types of register.
+
+For example consider the following Verilog code fragment:
+
+\begin{lstlisting}[numbers=left,frame=single,language=Verilog]
+always @(posedge clk)
+ y <= a + b;
+\end{lstlisting}
+
+In simulation the statement \lstinline[language=Verilog]{y <= a + b} is executed whenever
+a positive edge on the signal \lstinline[language=Verilog]{clk} is detected. The synthesis
+result however will contain an adder that calculates the sum \lstinline[language=Verilog]{a + b}
+all the time, followed by a d-type flip-flop with the adder output on its D-input and the
+signal \lstinline[language=Verilog]{y} on its Q-output.
+
+Usually the imperative code fragments used in behavioural modelling can contain
+statements for conditional execution (\lstinline[language=Verilog]{if}- and
+\lstinline[language=Verilog]{case}-statements in Verilog) as well as loops,
+as long as those loops can be completely unrolled.
+
+Interestingly there seems to be no other FOSS Tool that is capable of
+performing Verilog or VHDL behavioural syntheses besides Yosys (see
+App.~\ref{chapter:sota}).
+
+\subsection{Register-Transfer Level (RTL)}
+
+On the Register-Transfer Level the design is represented by combinatorial data
+paths and registers (usually d-type flip flops). The following Verilog code fragment
+is equivalent to the previous Verilog example, but is in RTL representation:
+
+\begin{lstlisting}[numbers=left,frame=single,language=Verilog]
+assign tmp = a + b; // combinatorial data path
+
+always @(posedge clk) // register
+ y <= tmp;
+\end{lstlisting}
+
+A design in RTL representation is usually stored using HDLs like Verilog and VHDL. But only
+a very limited subset of features is used, namely minimalistic {\tt always}-blocks (Verilog)
+or {\tt process}-blocks (VHDL) that model the register type used and unconditional assignments
+for the datapath logic. The use of HDLs on this level simplifies simulation as no additional
+tools are required to simulate a design in RTL representation.
+
+Many optimizations and analyses can be performed best at the RTL level. Examples include FSM
+detection and optimization, identification of memories or other larger building blocks
+and identification of shareable resources.
+
+Note that RTL is the first abstraction level in which the circuit is represented as a
+graph of circuit elements (registers and combinatorial cells) and signals. Such a graph,
+when encoded as list of cells and connections, is called a netlist.
+
+RTL synthesis is easy as each circuit node element in the netlist can simply be replaced
+with an equivalent gate-level circuit. However, usually the term {\it RTL synthesis} does
+not only refer to synthesizing an RTL netlist to a gate level netlist but also to performing
+a number of highly sophisticated optimizations within the RTL representation, such as
+the examples listed above.
+
+A number of FOSS tools exist that can perform isolated tasks within the domain of RTL
+synthesis steps. But there seems to be no FOSS tool that covers a wide range of RTL
+synthesis operations.
+
+\subsection{Logical Gate Level}
+
+At the logical gate level the design is represented by a netlist that uses only
+cells from a small number of single-bit cells, such as basic logic gates (AND,
+OR, NOT, XOR, etc.) and registers (usually D-Type Flip-flops).
+
+A number of netlist formats exists that can be used on this level, e.g.~the Electronic Design
+Interchange Format (EDIF), but for ease of simulation often a HDL netlist is used. The latter
+is a HDL file (Verilog or VHDL) that only uses the most basic language constructs for instantiation
+and connecting of cells.
+
+There are two challenges in logic synthesis: First finding opportunities for optimizations
+within the gate level netlist and second the optimal (or at least good) mapping of the logic
+gate netlist to an equivalent netlist of physically available gate types.
+
+The simplest approach to logic synthesis is {\it two-level logic synthesis}, where a logic function
+is converted into a sum-of-products representation, e.g.~using a Karnaugh map.
+This is a simple approach, but has exponential worst-case effort and cannot make efficient use of
+physical gates other than AND/NAND-, OR/NOR- and NOT-Gates.
+
+Therefore modern logic synthesis tools utilize much more complicated {\it multi-level logic
+synthesis} algorithms \cite{MultiLevelLogicSynth}. Most of these algorithms convert the
+logic function to a Binary-Decision-Diagram (BDD) or And-Inverter-Graph (AIG) and work from that
+representation. The former has the advantage that it has a unique normalized form. The latter has
+much better worst case performance and is therefore better suited for the synthesis of large
+logic functions.
+
+Good FOSS tools exists for multi-level logic synthesis \citeweblink{ABC}
+\citeweblink{AIGER} \citeweblink{MVSIS}.
+
+Yosys contains basic logic synthesis functionality but can also use ABC
+\citeweblink{ABC} for the logic synthesis step. Using ABC is recommended.
+
+\subsection{Physical Gate Level}
+
+On the physical gate level only gates are used that are physically available on
+the target architecture. In some cases this may only be NAND, NOR and NOT gates as well as
+D-Type registers. In other cases this might include cells that are more complex than the cells
+used at the logical gate level (e.g.~complete half-adders). In the case of an FPGA-based
+design the physical gate level representation is a netlist of LUTs with optional output
+registers, as these are the basic building blocks of FPGA logic cells.
+
+For the synthesis tool chain this abstraction is usually the lowest level. In
+case of an ASIC-based design the cell library might contain further information on
+how the physical cells map to individual switches (transistors).
+
+\subsection{Switch Level}
+
+A switch level representation of a circuit is a netlist utilizing single transistors as cells.
+Switch level modelling is possible in Verilog and VHDL, but is seldom used in modern designs,
+as in modern digital ASIC or FPGA flows the physical gates are considered the atomic build blocks
+of the logic circuit.
+
+\subsection{Yosys}
+
+Yosys is a Verilog HDL synthesis tool. This means that it takes a behavioural
+design description as input and generates an RTL, logical gate or physical gate
+level description of the design as output. Yosys' main strengths are behavioural
+and RTL synthesis. A wide range of commands (synthesis passes) exist
+within Yosys that can be used to perform a wide range of synthesis tasks within
+the domain of behavioural, rtl and logic synthesis. Yosys is designed to be
+extensible and therefore is a good basis for implementing custom synthesis
+tools for specialised tasks.
+
+\section{Features of Synthesizable Verilog}
+
+The subset of Verilog \cite{Verilog2005} that is synthesizable is specified in
+a separate IEEE standards document, the IEEE standard 1364.1-2002 \cite{VerilogSynth}.
+This standard also describes how certain language constructs are to be interpreted in
+the scope of synthesis.
+
+This section provides a quick overview of the most important features of
+synthesizable Verilog, structured in order of increasing complexity.
+
+\subsection{Structural Verilog}
+
+{\it Structural Verilog} (also known as {\it Verilog Netlists}) is a Netlist in
+Verilog syntax. Only the following language constructs are used in this case:
+
+\begin{itemize}
+\item Constant values
+\item Wire and port declarations
+\item Static assignments of signals to other signals
+\item Cell instantiations
+\end{itemize}
+
+Many tools (especially at the back end of the synthesis chain) only support
+structural Verilog as input. ABC is an example of such a tool. Unfortunately
+there is no standard specifying what {\it Structural Verilog} actually is,
+leading to some confusion about what syntax constructs are supported in
+structural Verilog when it comes to features such as attributes or multi-bit
+signals.
+
+\subsection{Expressions in Verilog}
+
+In all situations where Verilog accepts a constant value or signal name,
+expressions using arithmetic operations such as
+\lstinline[language=Verilog]{+}, \lstinline[language=Verilog]{-} and \lstinline[language=Verilog]{*},
+boolean operations such as
+\lstinline[language=Verilog]{&} (AND), \lstinline[language=Verilog]{|} (OR) and \lstinline[language=Verilog]{^} (XOR)
+and many others (comparison operations, unary operator, etc.) can also be used.
+
+During synthesis these operators are replaced by cells that implement the respective function.
+
+Many FOSS tools that claim to be able to process Verilog in fact only support
+basic structural Verilog and simple expressions. Yosys can be used to convert
+full featured synthesizable Verilog to this simpler subset, thus enabling such
+applications to be used with a richer set of Verilog features.
+
+\subsection{Behavioural Modelling}
+
+Code that utilizes the Verilog {\tt always} statement is using {\it Behavioural
+Modelling}. In behavioural modelling, a circuit is described by means of imperative
+program code that is executed on certain events, namely any change, a rising
+edge, or a falling edge of a signal. This is a very flexible construct during
+simulation but is only synthesizable when one of the following is modelled:
+
+\begin{itemize}
+\item {\bf Asynchronous or latched logic} \\
+In this case the sensitivity list must contain all expressions that are used within
+the {\tt always} block. The syntax \lstinline[language=Verilog]{@*} can be used
+for these cases. Examples of this kind include:
+
+\begin{lstlisting}[numbers=left,frame=single,language=Verilog]
+// asynchronous
+always @* begin
+ if (add_mode)
+ y <= a + b;
+ else
+ y <= a - b;
+end
+
+// latched
+always @* begin
+ if (!hold)
+ y <= a + b;
+end
+\end{lstlisting}
+
+Note that latched logic is often considered bad style and in many cases just
+the result of sloppy HDL design. Therefore many synthesis tools generate warnings
+whenever latched logic is generated.
+
+\item {\bf Synchronous logic (with optional synchronous reset)} \\
+This is logic with d-type flip-flops on the output. In this case the sensitivity
+list must only contain the respective clock edge. Example:
+\begin{lstlisting}[numbers=left,frame=single,language=Verilog]
+// counter with synchronous reset
+always @(posedge clk) begin
+ if (reset)
+ y <= 0;
+ else
+ y <= y + 1;
+end
+\end{lstlisting}
+
+\item {\bf Synchronous logic with asynchronous reset} \\
+This is logic with d-type flip-flops with asynchronous resets on the output. In
+this case the sensitivity list must only contain the respective clock and reset edges.
+The values assigned in the reset branch must be constant. Example:
+\begin{lstlisting}[numbers=left,frame=single,language=Verilog]
+// counter with asynchronous reset
+always @(posedge clk, posedge reset) begin
+ if (reset)
+ y <= 0;
+ else
+ y <= y + 1;
+end
+\end{lstlisting}
+\end{itemize}
+
+Many synthesis tools support a wider subset of flip-flops that can be modelled
+using {\tt always}-statements (including Yosys). But only the ones listed above
+are covered by the Verilog synthesis standard and when writing new designs one
+should limit herself or himself to these cases.
+
+In behavioural modelling, blocking assignments (=) and non-blocking assignments
+(<=) can be used. The concept of blocking vs.~non-blocking assignment is one
+of the most misunderstood constructs in Verilog \cite{Cummings00}.
+
+The blocking assignment behaves exactly like an assignment in any imperative
+programming language, while with the non-blocking assignment the right hand side
+of the assignment is evaluated immediately but the actual update of the left
+hand side register is delayed until the end of the time-step. For example the Verilog
+code \lstinline[language=Verilog]{a <= b; b <= a;} exchanges the values of
+the two registers. See Sec.~\ref{sec:blocking_nonblocking} for a more
+detailed description of this behaviour.
+
+\subsection{Functions and Tasks}
+
+Verilog supports {\it Functions} and {\it Tasks} to bundle statements that are
+used in multiple places (similar to {\it Procedures} in imperative programming).
+Both constructs can be implemented easily by substituting the function/task-call
+with the body of the function or task.
+
+\subsection{Conditionals, Loops and Generate-Statements}
+
+Verilog supports \lstinline[language=Verilog]{if-else}-statements and
+\lstinline[language=Verilog]{for}-loops inside \lstinline[language=Verilog]{always}-statements.
+
+It also supports both features in \lstinline[language=Verilog]{generate}-statements
+on the module level. This can be used to selectively enable or disable parts of the
+module based on the module parameters (\lstinline[language=Verilog]{if-else})
+or to generate a set of similar subcircuits (\lstinline[language=Verilog]{for}).
+
+While the \lstinline[language=Verilog]{if-else}-statement
+inside an always-block is part of behavioural modelling, the three other cases
+are (at least for a synthesis tool) part of a built-in macro processor. Therefore it must
+be possible for the synthesis tool to completely unroll all loops and evaluate the
+condition in all \lstinline[language=Verilog]{if-else}-statement in
+\lstinline[language=Verilog]{generate}-statements using const-folding.
+
+Examples for this can be found in Fig.~\ref{fig:StateOfTheArt_for} and
+Fig.~\ref{fig:StateOfTheArt_gen} in App.~\ref{chapter:sota}.
+
+\subsection{Arrays and Memories}
+
+Verilog supports arrays. This is in general a synthesizable language feature.
+In most cases arrays can be synthesized by generating addressable memories.
+However, when complex or asynchronous access patterns are used, it is not
+possible to model an array as memory. In these cases the array must
+be modelled using individual signals for each word and all accesses to the array
+must be implemented using large multiplexers.
+
+In some cases it would be possible to model an array using memories, but it
+is not desired. Consider the following delay circuit:
+\begin{lstlisting}[numbers=left,frame=single,language=Verilog]
+module (clk, in_data, out_data);
+
+parameter BITS = 8;
+parameter STAGES = 4;
+
+input clk;
+input [BITS-1:0] in_data;
+output [BITS-1:0] out_data;
+reg [BITS-1:0] ffs [STAGES-1:0];
+
+integer i;
+always @(posedge clk) begin
+ ffs[0] <= in_data;
+ for (i = 1; i < STAGES; i = i+1)
+ ffs[i] <= ffs[i-1];
+end
+
+assign out_data = ffs[STAGES-1];
+
+endmodule
+\end{lstlisting}
+
+This could be implemented using an addressable memory with {\tt STAGES} input
+and output ports. A better implementation would be to use a simple chain of flip-flops
+(a so-called shift register).
+This better implementation can either be obtained by first creating a memory-based
+implementation and then optimizing it based on the static address signals for all ports
+or directly identifying such situations in the language front end and converting
+all memory accesses to direct accesses to the correct signals.
+
+\section{Challenges in Digital Circuit Synthesis}
+
+This section summarizes the most important challenges in digital circuit
+synthesis. Tools can be characterized by how well they address these topics.
+
+\subsection{Standards Compliance}
+
+The most important challenge is compliance with the HDL standards in question (in case
+of Verilog the IEEE Standards 1364.1-2002 and 1364-2005). This can be broken down in two
+items:
+
+\begin{itemize}
+\item Completeness of implementation of the standard
+\item Correctness of implementation of the standard
+\end{itemize}
+
+Completeness is mostly important to guarantee compatibility
+with existing HDL code. Once a design has been verified and tested, HDL designers
+are very reluctant regarding changes to the design, even if it is only about
+a few minor changes to work around a missing feature in a new synthesis tool.
+
+Correctness is crucial. In some areas this is obvious (such as
+correct synthesis of basic behavioural models). But it is also crucial for the
+areas that concern minor details of the standard, such as the exact rules
+for handling signed expressions, even when the HDL code does not target
+different synthesis tools. This is because (unlike software source code that
+is only processed by compilers), in most design flows HDL code is not only
+processed by the synthesis tool but also by one or more simulators and sometimes
+even a formal verification tool. It is key for this verification process
+that all these tools use the same interpretation for the HDL code.
+
+\subsection{Optimizations}
+
+Generally it is hard to give a one-dimensional description of how well a synthesis tool
+optimizes the design. First of all because not all optimizations are applicable to all
+designs and all synthesis tasks. Some optimizations work (best) on a coarse-grained level
+(with complex cells such as adders or multipliers) and others work (best) on a fine-grained
+level (single bit gates). Some optimizations target area and others target speed.
+Some work well on large designs while others don't scale well and can only be applied
+to small designs.
+
+A good tool is capable of applying a wide range of optimizations at different
+levels of abstraction and gives the designer control over which optimizations
+are performed (or skipped) and what the optimization goals are.
+
+\subsection{Technology Mapping}
+
+Technology mapping is the process of converting the design into a netlist of
+cells that are available in the target architecture. In an ASIC flow this might
+be the process-specific cell library provided by the fab. In an FPGA flow this
+might be LUT cells as well as special function units such as dedicated multipliers.
+In a coarse-grain flow this might even be more complex special function units.
+
+An open and vendor independent tool is especially of interest if it supports
+a wide range of different types of target architectures.
+
+\section{Script-Based Synthesis Flows}
+
+A digital design is usually started by implementing a high-level or
+system-level simulation of the desired function. This description is then
+manually transformed (or re-implemented) into a synthesizable lower-level
+description (usually at the behavioural level) and the equivalence of the
+two representations is verified by simulating both and comparing the simulation
+results.
+
+Then the synthesizable description is transformed to lower-level
+representations using a series of tools and the results are again verified
+using simulation. This process is illustrated in Fig.~\ref{fig:Basics_flow}.
+
+\begin{figure}[t!]
+ \hfil
+ \begin{tikzpicture}
+ \tikzstyle{manual} = [draw, fill=green!10, rectangle, minimum height=2em, minimum width=8em, node distance=10em]
+ \tikzstyle{auto} = [draw, fill=orange!10, rectangle, minimum height=2em, minimum width=8em, node distance=10em]
+
+ \node[manual] (sys) {\begin{minipage}{8em}
+ \center
+ System Level \\
+ Model
+ \end{minipage}};
+ \node[manual] (beh) [right of=sys] {\begin{minipage}{8em}
+ \center
+ Behavioral \\
+ Model
+ \end{minipage}};
+ \node[auto] (rtl) [right of=beh] {\begin{minipage}{8em}
+ \center
+ RTL \\
+ Model
+ \end{minipage}};
+ \node[auto] (gates) [right of=rtl] {\begin{minipage}{8em}
+ \center
+ Gate-Level \\
+ Model
+ \end{minipage}};
+
+ \draw[-latex] (beh) edge[double, bend left] node[above] {synthesis} (rtl);
+ \draw[-latex] (rtl) edge[double, bend left] node[above] {synthesis} (gates);
+
+ \draw[latex-latex] (sys) edge[bend right] node[below] {verify} (beh);
+ \draw[latex-latex] (beh) edge[bend right] node[below] {verify} (rtl);
+ \draw[latex-latex] (rtl) edge[bend right] node[below] {verify} (gates);
+ \end{tikzpicture}
+ \caption{Typical design flow. Green boxes represent manually created models. Orange boxes represent
+ models generated by synthesis tools.}
+ \label{fig:Basics_flow}
+\end{figure}
+
+In this example the System Level Model and the Behavioural Model are both
+manually written design files. After the equivalence of system level model
+and behavioural model has been verified, the lower level representations of the
+design can be generated using synthesis tools. Finally the RTL Model and
+the Gate-Level Model are verified and the design process is finished.
+
+However, in any real-world design effort there will be multiple iterations for
+this design process. The reason for this can be the late change of a design
+requirement or the fact that the analysis of a low-abstraction model (e.g.~gate-level
+timing analysis) revealed that a design change is required in order to meet
+the design requirements (e.g.~maximum possible clock speed).
+
+Whenever the behavioural model or the system level model is
+changed their equivalence must be re-verified by re-running the simulations
+and comparing the results. Whenever the behavioural model is changed the
+synthesis must be re-run and the synthesis results must be re-verified.
+
+In order to guarantee reproducibility it is important to be able to re-run all
+automatic steps in a design project with a fixed set of settings easily.
+Because of this, usually all programs used in a synthesis flow can be
+controlled using scripts. This means that all functions are available via
+text commands. When such a tool provides a GUI, this is complementary to,
+and not instead of, a command line interface.
+
+Usually a synthesis flow in an UNIX/Linux environment would be controlled by a
+shell script that calls all required tools (synthesis and simulation/verification
+in this example) in the correct order. Each of these tools would be called with
+a script file containing commands for the respective tool. All settings required
+for the tool would be provided by these script files so that no manual interaction
+would be necessary. These script files are considered design sources and should
+be kept under version control just like the source code of the system level and the
+behavioural model.
+
+\section{Methods from Compiler Design}
+
+Some parts of synthesis tools involve problem domains that are traditionally known from
+compiler design. This section addresses some of these domains.
+
+\subsection{Lexing and Parsing}
+
+The best known concepts from compiler design are probably {\it lexing} and {\it parsing}.
+These are two methods that together can be used to process complex computer languages
+easily. \cite{Dragonbook}
+
+A {\it lexer} consumes single characters from the input and generates a stream of {\it lexical
+tokens} that consist of a {\it type} and a {\it value}. For example the Verilog input
+``\lstinline[language=Verilog]{assign foo = bar + 42;}'' might be translated by the lexer
+to the list of lexical tokens given in Tab.~\ref{tab:Basics_tokens}.
+
+\begin{table}[t]
+\hfil
+\begin{tabular}{ll}
+Token-Type & Token-Value \\
+\hline
+\tt TOK\_ASSIGN & - \\
+\tt TOK\_IDENTIFIER & ``{\tt foo}'' \\
+\tt TOK\_EQ & - \\
+\tt TOK\_IDENTIFIER & ``{\tt bar}'' \\
+\tt TOK\_PLUS & - \\
+\tt TOK\_NUMBER & 42 \\
+\tt TOK\_SEMICOLON & - \\
+\end{tabular}
+\caption{Exemplary token list for the statement ``\lstinline[language=Verilog]{assign foo = bar + 42;}''.}
+\label{tab:Basics_tokens}
+\end{table}
+
+The lexer is usually generated by a lexer generator (e.g.~{\tt flex} \citeweblink{flex}) from a
+description file that is using regular expressions to specify the text pattern that should match
+the individual tokens.
+
+The lexer is also responsible for skipping ignored characters (such as whitespace outside string
+constants and comments in the case of Verilog) and converting the original text snippet to a token
+value.
+
+Note that individual keywords use different token types (instead of a keyword type with different
+token values). This is because the parser usually can only use the Token-Type to make a decision on
+the grammatical role of a token.
+
+The parser then transforms the list of tokens into a parse tree that closely resembles the productions
+from the computer languages grammar. As the lexer, the parser is also typically generated by a code
+generator (e.g.~{\tt bison} \citeweblink{bison}) from a grammar description in Backus-Naur Form (BNF).
+
+Let's consider the following BNF (in Bison syntax):
+
+\begin{lstlisting}[numbers=left,frame=single]
+assign_stmt: TOK_ASSIGN TOK_IDENTIFIER TOK_EQ expr TOK_SEMICOLON;
+expr: TOK_IDENTIFIER | TOK_NUMBER | expr TOK_PLUS expr;
+\end{lstlisting}
+
+\begin{figure}[b!]
+ \hfil
+ \begin{tikzpicture}
+ \tikzstyle{node} = [draw, fill=green!10, ellipse, minimum height=2em, minimum width=8em, node distance=10em]
+
+ \draw (+0,+1) node[node] (n1) {\tt assign\_stmt};
+
+ \draw (-6,-1) node[node] (n11) {\tt TOK\_ASSIGN};
+ \draw (-3,-2) node[node] (n12) {\tt TOK\_IDENTIFIER};
+ \draw (+0,-1) node[node] (n13) {\tt TOK\_EQ};
+ \draw (+3,-2) node[node] (n14) {\tt expr};
+ \draw (+6,-1) node[node] (n15) {\tt TOK\_SEMICOLON};
+
+ \draw (-1,-4) node[node] (n141) {\tt expr};
+ \draw (+3,-4) node[node] (n142) {\tt TOK\_PLUS};
+ \draw (+7,-4) node[node] (n143) {\tt expr};
+
+ \draw (-1,-5.5) node[node] (n1411) {\tt TOK\_IDENTIFIER};
+ \draw (+7,-5.5) node[node] (n1431) {\tt TOK\_NUMBER};
+
+ \draw[-latex] (n1) -- (n11);
+ \draw[-latex] (n1) -- (n12);
+ \draw[-latex] (n1) -- (n13);
+ \draw[-latex] (n1) -- (n14);
+ \draw[-latex] (n1) -- (n15);
+
+ \draw[-latex] (n14) -- (n141);
+ \draw[-latex] (n14) -- (n142);
+ \draw[-latex] (n14) -- (n143);
+
+ \draw[-latex] (n141) -- (n1411);
+ \draw[-latex] (n143) -- (n1431);
+ \end{tikzpicture}
+ \caption{Example parse tree for the Verilog expression ``\lstinline[language=Verilog]{assign foo = bar + 42;}''.}
+ \label{fig:Basics_parsetree}
+\end{figure}
+
+The parser converts the token list to the parse tree in Fig.~\ref{fig:Basics_parsetree}. Note that the parse
+tree never actually exists as a whole as data structure in memory. Instead the parser calls user-specified
+code snippets (so-called {\it reduce-functions}) for all inner nodes of the parse tree in depth-first order.
+
+In some very simple applications (e.g.~code generation for stack machines) it is possible to perform the
+task at hand directly in the reduce functions. But usually the reduce functions are only used to build an in-memory
+data structure with the relevant information from the parse tree. This data structure is called an {\it abstract
+syntax tree} (AST).
+
+The exact format for the abstract syntax tree is application specific (while the format of the parse tree and token
+list are mostly dictated by the grammar of the language at hand). Figure~\ref{fig:Basics_ast} illustrates what an
+AST for the parse tree in Fig.~\ref{fig:Basics_parsetree} could look like.
+
+Usually the AST is then converted into yet another representation that is more suitable for further processing.
+In compilers this is often an assembler-like three-address-code intermediate representation. \cite{Dragonbook}
+
+\begin{figure}[t]
+ \hfil
+ \begin{tikzpicture}
+ \tikzstyle{node} = [draw, fill=green!10, ellipse, minimum height=2em, minimum width=8em, node distance=10em]
+
+ \draw (+0,+0) node[node] (n1) {\tt ASSIGN};
+
+ \draw (-2,-2) node[node] (n11) {\tt ID: foo};
+ \draw (+2,-2) node[node] (n12) {\tt PLUS};
+
+ \draw (+0,-4) node[node] (n121) {\tt ID: bar};
+ \draw (+4,-4) node[node] (n122) {\tt CONST: 42};
+
+ \draw[-latex] (n1) -- (n11);
+ \draw[-latex] (n1) -- (n12);
+
+ \draw[-latex] (n12) -- (n121);
+ \draw[-latex] (n12) -- (n122);
+ \end{tikzpicture}
+ \caption{Example abstract syntax tree for the Verilog expression ``\lstinline[language=Verilog]{assign foo = bar + 42;}''.}
+ \label{fig:Basics_ast}
+\end{figure}
+
+\subsection{Multi-Pass Compilation}
+
+Complex problems are often best solved when split up into smaller problems. This is certainly true
+for compilers as well as for synthesis tools. The components responsible for solving the smaller problems can
+be connected in two different ways: through {\it Single-Pass Pipelining} and by using {\it Multiple Passes}.
+
+Traditionally a parser and lexer are connected using the pipelined approach: The lexer provides a function that
+is called by the parser. This function reads data from the input until a complete lexical token has been read. Then
+this token is returned to the parser. So the lexer does not first generate a complete list of lexical tokens
+and then pass it to the parser. Instead they run concurrently and the parser can consume tokens as
+the lexer produces them.
+
+The single-pass pipelining approach has the advantage of lower memory footprint (at no time must the complete design
+be kept in memory) but has the disadvantage of tighter coupling between the interacting components.
+
+Therefore single-pass pipelining should only be used when the lower memory footprint is required or the
+components are also conceptually tightly coupled. The latter certainly is the case for a parser and its lexer.
+But when data is passed between two conceptually loosely coupled components it is often
+beneficial to use a multi-pass approach.
+
+In the multi-pass approach the first component processes all the data and the result is stored in a in-memory
+data structure. Then the second component is called with this data. This reduces complexity, as only one
+component is running at a time. It also improves flexibility as components can be exchanged easier.
+
+Most modern compilers are multi-pass compilers.
+
+\iffalse
+\subsection{Static Single Assignment Form}
+
+In imperative programming (and behavioural HDL design) it is possible to assign the same variable multiple times.
+This can either mean that the variable is independently used in two different contexts or that the final value
+of the variable depends on a condition.
+
+The following examples show C code in which one variable is used independently in two different contexts:
+
+\begin{minipage}{7.7cm}
+\begin{lstlisting}[numbers=left,frame=single,language=C++]
+void demo1()
+{
+ int a = 1;
+ printf("%d\n", a);
+
+ a = 2;
+ printf("%d\n", a);
+}
+\end{lstlisting}
+\end{minipage}
+\hfil
+\begin{minipage}{7.7cm}
+\begin{lstlisting}[frame=single,language=C++]
+void demo1()
+{
+ int a = 1;
+ printf("%d\n", a);
+
+ int b = 2;
+ printf("%d\n", b);
+}
+\end{lstlisting}
+\end{minipage}
+
+\begin{minipage}{7.7cm}
+\begin{lstlisting}[numbers=left,frame=single,language=C++]
+void demo2(bool foo)
+{
+ int a;
+ if (foo) {
+ a = 23;
+ printf("%d\n", a);
+ } else {
+ a = 42;
+ printf("%d\n", a);
+ }
+}
+\end{lstlisting}
+\end{minipage}
+\hfil
+\begin{minipage}{7.7cm}
+\begin{lstlisting}[frame=single,language=C++]
+void demo2(bool foo)
+{
+ int a, b;
+ if (foo) {
+ a = 23;
+ printf("%d\n", a);
+ } else {
+ b = 42;
+ printf("%d\n", b);
+ }
+}
+\end{lstlisting}
+\end{minipage}
+
+In both examples the left version (only variable \lstinline[language=C++]{a}) and the right version (variables
+\lstinline[language=Verilog]{a} and \lstinline[language=Verilog]{b}) are equivalent. Therefore it is
+desired for further processing to bring the code in an equivalent form for both cases.
+
+In the following example the variable is assigned twice but it cannot be easily replaced by two variables:
+
+\begin{lstlisting}[frame=single,language=C++]
+void demo3(bool foo)
+{
+ int a = 23
+ if (foo)
+ a = 42;
+ printf("%d\n", a);
+}
+\end{lstlisting}
+
+Static single assignment (SSA) form is a representation of imperative code that uses identical representations
+for the left and right version of demos 1 and 2, but can still represent demo 3. In SSA form each assignment
+assigns a new variable (usually written with an index). But it also introduces a special $\Phi$-function to
+merge the different instances of a variable when needed. In C-pseudo-code the demo 3 would be written as follows
+using SSA from:
+
+\begin{lstlisting}[frame=single,language=C++]
+void demo3(bool foo)
+{
+ int a_1, a_2, a_3;
+ a_1 = 23
+ if (foo)
+ a_2 = 42;
+ a_3 = phi(a_1, a_2);
+ printf("%d\n", a_3);
+}
+\end{lstlisting}
+
+The $\Phi$-function is usually interpreted as ``these variables must be stored
+in the same memory location'' during code generation. Most modern compilers for imperative languages
+such as C/C++ use SSA form for at least some of its passes as it is very easy to manipulate and analyse.
+\fi
+
diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex
new file mode 100644
index 00000000..a831fdf3
--- /dev/null
+++ b/manual/CHAPTER_CellLib.tex
@@ -0,0 +1,454 @@
+
+\chapter{Internal Cell Library}
+\label{chapter:celllib}
+
+Most of the passes in Yosys operate on netlists, i.e.~they only care about the RTLIL::Wire and RTLIL::Cell
+objects in an RTLIL::Module. This chapter discusses the cell types used by Yosys to represent a behavioural
+design internally.
+
+This chapter is split in two parts. In the first part the internal RTL cells are covered. These cells
+are used to represent the design on a coarse grain level. Like in the original HDL code on this level the
+cells operate on vectors of signals and complex cells like adders exist. In the second part the internal
+gate cells are covered. These cells are used to represent the design on a fine-grain gate-level. All cells
+from this category operate on single bit signals.
+
+\section{RTL Cells}
+
+Most of the RTL cells closely resemble the operators available in HDLs such as
+Verilog or VHDL. Therefore Verilog operators are used in the following sections
+to define the behaviour of the RTL cells.
+
+Note that all RTL cells have parameters indicating the size of inputs and outputs. When
+passes modify RTL cells they must always keep the values of these parameters in sync with
+the size of the signals connected to the inputs and outputs.
+
+Simulation models for the RTL cells can be found in the file {\tt techlibs/common/simlib.v} in the Yosys
+source tree.
+
+\subsection{Unary Operators}
+
+All unary RTL cells have one input port \B{A} and one output port \B{Y}. They also
+have the following parameters:
+
+\begin{itemize}
+\item \B{A\_SIGNED} \\
+Set to a non-zero value if the input \B{A} is signed and therefore should be sign-extended
+when needed.
+
+\item \B{A\_WIDTH} \\
+The width of the input port \B{A}.
+
+\item \B{Y\_WIDTH} \\
+The width of the output port \B{Y}.
+\end{itemize}
+
+Table~\ref{tab:CellLib_unary} lists all cells for unary RTL operators.
+
+\begin{table}[t!]
+\hfil
+\begin{tabular}{ll}
+Verilog & Cell Type \\
+\hline
+\lstinline[language=Verilog]; Y = ~A ; & {\tt \$not} \\
+\lstinline[language=Verilog]; Y = +A ; & {\tt \$pos} \\
+\lstinline[language=Verilog]; Y = -A ; & {\tt \$neg} \\
+\hline
+\lstinline[language=Verilog]; Y = &A ; & {\tt \$reduce\_and} \\
+\lstinline[language=Verilog]; Y = |A ; & {\tt \$reduce\_or} \\
+\lstinline[language=Verilog]; Y = ^A ; & {\tt \$reduce\_xor} \\
+\lstinline[language=Verilog]; Y = ~^A ; & {\tt \$reduce\_xnor} \\
+\hline
+\lstinline[language=Verilog]; Y = |A ; & {\tt \$reduce\_bool} \\
+\lstinline[language=Verilog]; Y = !A ; & {\tt \$logic\_not}
+\end{tabular}
+\caption{Cell types for unary operators with their corresponding Verilog expressions.}
+\label{tab:CellLib_unary}
+\end{table}
+
+Note that {\tt \$reduce\_or} and {\tt \$reduce\_bool} actually represent the same
+logic function. But the HDL frontends generate them in different situations. A
+{\tt \$reduce\_or} cell is generated when the prefix {\tt |} operator is being used. A
+{\tt \$reduce\_bool} cell is generated when a bit vector is used as a condition in
+an {\tt if}-statement or {\tt ?:}-expression.
+
+\subsection{Binary Operators}
+
+All binary RTL cells have two input ports \B{A} and \B{B} and one output port \B{Y}. They
+also have the following parameters:
+
+\begin{itemize}
+\item \B{A\_SIGNED} \\
+Set to a non-zero value if the input \B{A} is signed and therefore should be sign-extended
+when needed.
+
+\item \B{A\_WIDTH} \\
+The width of the input port \B{A}.
+
+\item \B{B\_SIGNED} \\
+Set to a non-zero value if the input \B{B} is signed and therefore should be sign-extended
+when needed.
+
+\item \B{B\_WIDTH} \\
+The width of the input port \B{B}.
+
+\item \B{Y\_WIDTH} \\
+The width of the output port \B{Y}.
+\end{itemize}
+
+Table~\ref{tab:CellLib_binary} lists all cells for binary RTL operators.
+
+\subsection{Multiplexers}
+
+Multiplexers are generated by the Verilog HDL frontend for {\tt
+?:}-expressions. Multiplexers are also generated by the {\tt proc} pass to map the decision trees
+from RTLIL::Process objects to logic.
+
+The simplest multiplexer cell type is {\tt \$mux}. Cells of this type have a \B{WIDTH} parameter
+and data inputs \B{A} and \B{B} and a data output \B{Y}, all of the specified width. This cell also
+has a single bit control input \B{S}. If \B{S} is 0 the value from the \B{A} input is sent to
+the output, if it is 1 the value from the \B{B} input is sent to the output. So the {\tt \$mux}
+cell implements the function \lstinline[language=Verilog]; Y = S ? B : A;.
+
+The {\tt \$pmux} cell is used to multiplex between many inputs using a one-hot select signal. Cells
+of this type have a \B{WIDTH} and a \B{S\_WIDTH} parameter and inputs \B{A}, \B{B}, and \B{S} and
+an output \B{Y}. The \B{S} input is \B{S\_WIDTH} bits wide. The \B{A} input and the output are both
+\B{WIDTH} bits wide and the \B{B} input is \B{WIDTH}*\B{S\_WIDTH} bits wide. When all bits of
+\B{S} are zero, the value from \B{A} input is sent to the output. If the $n$'th bit from \B{S} is
+set, the value $n$'th \B{WIDTH} bits wide slice of the \B{B} input is sent to the output. When more
+than one bit from \B{S} is set the output is undefined. Cells of this type are used to model
+``parallel cases'' (defined by using the {\tt parallel\_case} attribute or detected by
+an optimization).
+
+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
+understand dependencies between signals. Therefore optimizations should not
+break these multiplexer trees (e.g.~by replacing a multiplexer between a
+calculated signal and a constant zero with an {\tt \$and} gate).
+
+\begin{table}[t!]
+\hfil
+\begin{tabular}[t]{ll}
+Verilog & Cell Type \\
+\hline
+\lstinline[language=Verilog]; Y = A & B; & {\tt \$and} \\
+\lstinline[language=Verilog]; Y = A | B; & {\tt \$or} \\
+\lstinline[language=Verilog]; Y = A ^ B; & {\tt \$xor} \\
+\lstinline[language=Verilog]; Y = A ~^ B; & {\tt \$xnor} \\
+\hline
+\lstinline[language=Verilog]; Y = A << B; & {\tt \$shl} \\
+\lstinline[language=Verilog]; Y = A >> B; & {\tt \$shr} \\
+\lstinline[language=Verilog]; Y = A <<< B; & {\tt \$sshl} \\
+\lstinline[language=Verilog]; Y = A >>> B; & {\tt \$sshr} \\
+\hline
+\lstinline[language=Verilog]; Y = A && B; & {\tt \$logic\_and} \\
+\lstinline[language=Verilog]; Y = A || B; & {\tt \$logic\_or} \\
+\hline
+\lstinline[language=Verilog]; Y = A === B; & {\tt \$eqx} \\
+\lstinline[language=Verilog]; Y = A !== B; & {\tt \$nex} \\
+\end{tabular}
+\hfil
+\begin{tabular}[t]{ll}
+Verilog & Cell Type \\
+\hline
+\lstinline[language=Verilog]; Y = A < B; & {\tt \$lt} \\
+\lstinline[language=Verilog]; Y = A <= B; & {\tt \$le} \\
+\lstinline[language=Verilog]; Y = A == B; & {\tt \$eq} \\
+\lstinline[language=Verilog]; Y = A != B; & {\tt \$ne} \\
+\lstinline[language=Verilog]; Y = A >= B; & {\tt \$ge} \\
+\lstinline[language=Verilog]; Y = A > B; & {\tt \$gt} \\
+\hline
+\lstinline[language=Verilog]; Y = A + B; & {\tt \$add} \\
+\lstinline[language=Verilog]; Y = A - B; & {\tt \$sub} \\
+\lstinline[language=Verilog]; Y = A * B; & {\tt \$mul} \\
+\lstinline[language=Verilog]; Y = A / B; & {\tt \$div} \\
+\lstinline[language=Verilog]; Y = A % B; & {\tt \$mod} \\
+\lstinline[language=Verilog]; Y = A ** B; & {\tt \$pow} \\
+\end{tabular}
+\caption{Cell types for binary operators with their corresponding Verilog expressions.}
+\label{tab:CellLib_binary}
+\end{table}
+
+\subsection{Registers}
+
+D-Type Flip-Flops are represented by {\tt \$dff} cells. These cells have a clock port \B{CLK},
+an input port \B{D} and an output port \B{Q}. The following parameters are available for \$dff
+cells:
+
+\begin{itemize}
+\item \B{WIDTH} \\
+The width of input \B{D} and output \B{Q}.
+
+\item \B{CLK\_POLARITY} \\
+Clock is active on the positive edge if this parameter has the value {\tt 1'b1} and on the negative
+edge if this parameter is {\tt 1'b0}.
+\end{itemize}
+
+D-Type Flip-Flops with asynchronous resets are represented by {\tt \$adff} cells. As the {\tt \$dff}
+cells they have \B{CLK}, \B{D} and \B{Q} ports. In addition they also have a single-bit \B{ARST}
+input port for the reset pin and the following additional two parameters:
+
+\begin{itemize}
+\item \B{ARST\_POLARITY} \\
+The asynchronous reset is high-active if this parameter has the value {\tt 1'b1} and low-active
+if this parameter is {\tt 1'b0}.
+
+\item \B{ARST\_VALUE} \\
+The state of \B{Q} will be set to this value when the reset is active.
+\end{itemize}
+
+Note that the {\tt \$adff} cell can only be used when the reset value is constant.
+
+\begin{sloppypar}
+Usually these cells are generated by the {\tt proc} pass using the information
+in the designs RTLIL::Process objects.
+\end{sloppypar}
+
+\begin{fixme}
+Add information about {\tt \$sr} cells (set-reset flip-flops) and d-type latches.
+\end{fixme}
+
+\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.
+
+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.
+
+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
+following parameters:
+
+\begin{itemize}
+\item \B{MEMID} \\
+The name of the RTLIL::Memory object that is associated with this read port.
+
+\item \B{ABITS} \\
+The number of address bits (width of the \B{ADDR} input port).
+
+\item \B{WIDTH} \\
+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
+the \B{CLK} input is not used.
+
+\item \B{CLK\_POLARITY} \\
+Clock is active on the positive edge if this parameter has the value {\tt 1'b1} and on the negative
+edge if this parameter is {\tt 1'b0}.
+
+\item \B{TRANSPARENT} \\
+If this parameter is set to {\tt 1'b1}, a read and write to the same address in the same cycle will
+return the new value. Otherwise the old value is returned.
+\end{itemize}
+
+The {\tt \$memwr} cells have a clock input \B{CLK}, an enable input \B{EN} (one
+enable bit for each data bit), an address input \B{ADDR} and a data input
+\B{DATA}. They also have the following parameters:
+
+\begin{itemize}
+\item \B{MEMID} \\
+The name of the RTLIL::Memory object that is associated with this read port.
+
+\item \B{ABITS} \\
+The number of address bits (width of the \B{ADDR} input port).
+
+\item \B{WIDTH} \\
+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
+the \B{CLK} input is not used.
+
+\item \B{CLK\_POLARITY} \\
+Clock is active on positive edge if this parameter has the value {\tt 1'b1} and on the negative
+edge if this parameter is {\tt 1'b0}.
+
+\item \B{PRIORITY} \\
+The cell with the higher integer value in this parameter wins a write 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
+converts them to a single {\tt \$mem} cell and (optionally) maps this cell type
+to {\tt \$dff} cells for the individual words and multiplexer-based address decoders for the read and
+write interfaces. When the last step is disabled or not possible, a {\tt \$mem} cell is left in the design.
+
+The {\tt \$mem} cell provides the following parameters:
+
+\begin{itemize}
+\item \B{MEMID} \\
+The name of the original RTLIL::Memory object that became this {\tt \$mem} cell.
+
+\item \B{SIZE} \\
+The number of words in the memory.
+
+\item \B{ABITS} \\
+The number of address bits.
+
+\item \B{WIDTH} \\
+The number of data bits per word.
+
+\item \B{RD\_PORTS} \\
+The number of read ports on this memory cell.
+
+\item \B{RD\_CLK\_ENABLE} \\
+This parameter is \B{RD\_PORTS} bits wide, containing a clock enable bit for each read port.
+
+\item \B{RD\_CLK\_POLARITY} \\
+This parameter is \B{RD\_PORTS} bits wide, containing a clock polarity bit for each read port.
+
+\item \B{RD\_TRANSPARENT} \\
+This parameter is \B{RD\_PORTS} bits wide, containing a transparent bit for each read port.
+
+\item \B{WR\_PORTS} \\
+The number of write ports on this memory cell.
+
+\item \B{WR\_CLK\_ENABLE} \\
+This parameter is \B{WR\_PORTS} bits wide, containing a clock enable bit for each write port.
+
+\item \B{WR\_CLK\_POLARITY} \\
+This parameter is \B{WR\_PORTS} bits wide, containing a clock polarity bit for each write port.
+\end{itemize}
+
+The {\tt \$mem} cell has the following ports:
+
+\begin{itemize}
+\item \B{RD\_CLK} \\
+This input is \B{RD\_PORTS} bits wide, containing all clock signals for the read ports.
+
+\item \B{RD\_EN} \\
+This input is \B{RD\_PORTS} bits wide, containing all enable signals for the read ports.
+
+\item \B{RD\_ADDR} \\
+This input is \B{RD\_PORTS}*\B{ABITS} bits wide, containing all address signals for the read ports.
+
+\item \B{RD\_DATA} \\
+This input is \B{RD\_PORTS}*\B{WIDTH} bits wide, containing all data signals for the read ports.
+
+\item \B{WR\_CLK} \\
+This input is \B{WR\_PORTS} bits wide, containing all clock signals for the write ports.
+
+\item \B{WR\_EN} \\
+This input is \B{WR\_PORTS}*\B{WIDTH} bits wide, containing all enable signals for the write ports.
+
+\item \B{WR\_ADDR} \\
+This input is \B{WR\_PORTS}*\B{ABITS} bits wide, containing all address signals for the write ports.
+
+\item \B{WR\_DATA} \\
+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.
+
+\subsection{Finite State Machines}
+
+\begin{fixme}
+Add a brief description of the {\tt \$fsm} cell type.
+\end{fixme}
+
+\section{Gates}
+\label{sec:celllib_gates}
+
+For gate level logic networks, fixed function single bit cells are used that do
+not provide any parameters.
+
+Simulation models for these cells can be found in the file {\tt techlibs/common/simcells.v} in the Yosys
+source tree.
+
+\begin{table}[t]
+\hfil
+\begin{tabular}[t]{ll}
+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 \$\_OR\_} \\
+\lstinline[language=Verilog]; Y = A ^ B; & {\tt \$\_XOR\_} \\
+\lstinline[language=Verilog]; Y = S ? B : A; & {\tt \$\_MUX\_} \\
+\hline
+\lstinline[language=Verilog]; always @(negedge C) Q <= D; & {\tt \$\_DFF\_N\_} \\
+\lstinline[language=Verilog]; always @(posedge C) Q <= D; & {\tt \$\_DFF\_P\_} \\
+\end{tabular}
+\hfil
+\begin{tabular}[t]{llll}
+$ClkEdge$ & $RstLvl$ & $RstVal$ & Cell Type \\
+\hline
+\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFF\_NN0\_} \\
+\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFF\_NN1\_} \\
+\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFF\_NP0\_} \\
+\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFF\_NP1\_} \\
+\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFF\_PN0\_} \\
+\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFF\_PN1\_} \\
+\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFF\_PP0\_} \\
+\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFF\_PP1\_} \\
+\end{tabular}
+\caption{Cell types for gate level logic networks}
+\label{tab:CellLib_gates}
+\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.
+
+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
+d-type flip-flops with asynchronous resets. The values in the table for these cell types relate to the
+following Verilog code template, where \lstinline[mathescape,language=Verilog];$RstEdge$; is \lstinline[language=Verilog];posedge;
+if \lstinline[mathescape,language=Verilog];$RstLvl$; if \lstinline[language=Verilog];1;, and \lstinline[language=Verilog];negedge;
+otherwise.
+
+\begin{lstlisting}[mathescape,language=Verilog]
+ always @($ClkEdge$ C, $RstEdge$ R)
+ if (R == $RstLvl$)
+ Q <= $RstVa$l;
+ else
+ Q <= D;
+\end{lstlisting}
+
+In most cases gate level logic networks are created from RTL networks using the {\tt techmap} pass. The flip-flop cells
+from the gate level logic network can be mapped to physical flip-flop cells from a Liberty file using the {\tt dfflibmap}
+pass. The combinatorial logic cells can be mapped to physical cells from a Liberty file via ABC \citeweblink{ABC}
+using the {\tt abc} pass.
+
+\begin{fixme}
+Add information about {\tt \$assert}, {\tt \$assume}, {\tt \$equiv}, {\tt \$initstate}, {\tt \$anyconst}, and {\tt \$anyseq} cells.
+\end{fixme}
+
+\begin{fixme}
+Add information about {\tt \$slice} and {\tt \$concat} cells.
+\end{fixme}
+
+\begin{fixme}
+Add information about {\tt \$lut} and {\tt \$sop} cells.
+\end{fixme}
+
+\begin{fixme}
+Add information about {\tt \$alu}, {\tt \$macc}, {\tt \$fa}, and {\tt \$lcu} cells.
+\end{fixme}
+
+\begin{fixme}
+Add information about {\tt \$ff} and {\tt \$\_FF\_} cells.
+\end{fixme}
+
+\begin{fixme}
+Add information about {\tt \$dffe}, {\tt \$dffsr}, {\tt \$dlatch}, and {\tt \$dlatchsr} cells.
+\end{fixme}
+
+\begin{fixme}
+Add information about {\tt \$\_DFFE\_??\_}, {\tt \$\_DFFSR\_???\_}, {\tt \$\_DLATCH\_?\_}, and {\tt \$\_DLATCHSR\_???\_} cells.
+\end{fixme}
+
+\begin{fixme}
+Add information about {\tt \$\_NAND\_}, {\tt \$\_NOR\_}, {\tt \$\_XNOR\_}, {\tt \$\_AOI3\_}, {\tt \$\_OAI3\_}, {\tt \$\_AOI4\_}, and {\tt \$\_OAI4\_} cells.
+\end{fixme}
+
diff --git a/manual/CHAPTER_Eval.tex b/manual/CHAPTER_Eval.tex
new file mode 100644
index 00000000..f719618d
--- /dev/null
+++ b/manual/CHAPTER_Eval.tex
@@ -0,0 +1,209 @@
+
+\chapter{Evaluation, Conclusion, Future Work}
+\label{chapter:eval}
+
+The Yosys source tree contains over 200 test cases\footnote{Most of this test
+cases are copied from HANA \citeweblink{HANA} or the ASIC-WORLD website
+\citeweblink{ASIC-WORLD}.} which are used in the {\tt make test} make-target.
+Besides these there is an external Yosys benchmark and test case package that
+contains a few larger designs \citeweblink{YosysTestsGit}. This package
+contains the designs listed in Tab.~\ref{tab:yosys-test-designs}.
+
+\begin{table}
+ \hfil
+ \begin{tabular}{lrrp{8.5cm}}
+ Test-Design & Source & Gates\footnotemark & Description / Comments \\
+ \hline
+ {\tt aes\_core} & IWLS2005 & $ 41{,}837 $ & \footnotesize AES Cipher written by Rudolf Usselmann \\
+ {\tt i2c} & IWLS2005 & $ 1{,}072 $ & \footnotesize WISHBONE compliant I2C Master by Richard Herveille \\
+ {\tt openmsp430} & OpenCores & $ 7{,}173 $ & \footnotesize MSP430 compatible CPU by Olivier Girard \\
+ {\tt or1200} & OpenCores & $ 42{,}675 $ & \footnotesize The OpenRISC 1200 CPU by Damjan Lampret \\
+ {\tt sasc} & IWLS2005 & $ 456 $ & \footnotesize Simple Async. Serial Comm. Device by Rudolf Usselmann \\
+ {\tt simple\_spi} & IWLS2005 & $ 690 $ & \footnotesize MC68HC11E based SPI interface by Richard Herveille \\
+ {\tt spi} & IWLS2005 & $ 2{,}478 $ & \footnotesize SPI IP core by Simon Srot \\
+ {\tt ss\_pcm} & IWLS2005 & $ 279 $ & \footnotesize PCM IO Slave by Rudolf Usselmann \\
+ {\tt systemcaes} & IWLS2005 & $ 6{,}893 $ & \footnotesize AES core (using SystemC to Verilog) by Javier Castillo \\
+ {\tt usb\_phy} & IWLS2005 & $ 515 $ & \footnotesize USB 1.1 PHY by Rudolf Usselmann \\
+ \end{tabular}
+ \caption{Tests included in the yosys-tests package.}
+ \label{tab:yosys-test-designs}
+\end{table}
+
+\footnotetext{
+Number of gates determined using the Yosys synthesis script ``{\tt hierarchy -top \$top; proc; opt; memory; opt; techmap; opt; abc; opt; flatten \$top; hierarchy -top \$top; abc; opt; select -count */c:*}''.
+}
+
+\section{Correctness of Synthesis Results}
+
+The following measures were taken to increase the confidence in the correctness of the Yosys synthesis results:
+
+\begin{itemize}
+\item Yosys comes with a large selection\footnote{At the time of this writing
+269 test cases.} of small test cases that are evaluated when the command {\tt
+make test} is executed. During development of Yosys it was shown that this
+collection of test cases is sufficient to catch most bugs. The following more
+sophisticated test procedures only caught a few additional bugs. Whenever this
+happened, an appropriate test case was added to the collection of small test
+cases for {\tt make test} to ensure better testability of the feature in
+question in the future.
+
+\item The designs listed in Tab.~\ref{tab:yosys-test-designs} where validated
+using the formal verification tool Synopsys Formality\citeweblink{Formality}.
+The Yosys synthesis scripts used to synthesize the individual designs for this
+test are slightly different per design in order to broaden the coverage of
+Yosys features. The large majority of all errors encountered using these tests
+are false-negatives, mostly related to FSM encoding or signal naming in large
+array logic (such as in memory blocks). Therefore the {\tt fsm\_recode} pass
+was extended so it can be used to generate TCL commands for Synopsys Formality
+that describe the relationship between old and new state encodings. Also the
+method used to generate signal and cell names in the Verilog backend was
+slightly modified in order to improve the automatic matching of net names in
+Synopsys Formality. With these changes in place all designs in Tab.~\ref{tab:yosys-test-designs}
+validate successfully using Formality.
+
+\item VlogHammer \citeweblink{VlogHammer} is a set of scripts that
+auto-generate a large collection of test cases\footnote{At the time of this
+writing over 6600 test cases.} and synthesize them using Yosys and the
+following freely available proprietary synthesis tools.
+\begin{itemize}
+\item Xilinx Vivado WebPack (2013.2) \citeweblink{XilinxWebPACK}
+\item Xilinx ISE (XST) WebPack (14.5) \citeweblink{XilinxWebPACK}
+\item Altera Quartus II Web Edition (13.0) \citeweblink{QuartusWeb}
+\end{itemize}
+The built-in SAT solver of Yosys is used to formally
+verify the Yosys RTL- and Gate-Level netlists against the netlists generated by
+this other tools.\footnote{A SAT solver is a program that can solve the boolean
+satisfiability problem. The built-in SAT solver in Yosys can be used for formal
+equivalence checking, amongst other things. See Sec.~\ref{cmd:sat} for details.}
+When differences are found, the input pattern that result in
+different outputs are used for simulating the original Verilog code as well as
+the synthesis results using the following Verilog simulators.
+\begin{itemize}
+\item Xilinx ISIM (from Xilinx ISE 14.5 \citeweblink{XilinxWebPACK})
+\item Modelsim 10.1d (from Quartus II 13.0 \citeweblink{QuartusWeb})
+\item Icarus Verilog (no specific version)
+\end{itemize}
+The set of tests performed by VlogHammer systematically verify the correct
+behaviour of
+\begin{itemize}
+\item Yosys Verilog Frontend and RTL generation
+\item Yosys Gate-Level Technology Mapping
+\item Yosys SAT Models for RTL- and Gate-Level cells
+\item Yosys Constant Evaluator Models for RTL- and Gate-Level cells
+\end{itemize}
+against the reference provided by the other tools. A few bugs related to sign
+extensions and bit-width extensions where found (and have been fixed meanwhile)
+using this approach. This test also revealed a small number of bugs in the
+other tools (i.e.~Vivado, XST, Quartus, ISIM and Icarus Verilog; no bugs where
+found in Modelsim using vlogHammer so far).
+\end{itemize}
+
+Although complex software can never be expected to be fully bug-free
+\cite{MURPHY}, it has been shown that Yosys is mature and feature-complete
+enough to handle most real-world cases correctly.
+
+\section{Quality of synthesis results}
+
+In this section an attempt to evaluate the quality of Yosys synthesis results is made. To this end the
+synthesis results of a commercial FPGA synthesis tool when presented with the original HDL code vs.~when
+presented with the Yosys synthesis result are compared.
+
+The OpenMSP430 and the OpenRISC 1200 test cases were synthesized using the following Yosys synthesis script:
+
+\begin{lstlisting}[numbers=left,frame=single,mathescape]
+hierarchy -check
+proc; opt; fsm; opt; memory; opt
+techmap; opt; abc; opt
+\end{lstlisting}
+
+The original RTL and the Yosys output where both passed to the Xilinx XST 14.5
+FPGA synthesis tool. The following setting where used for XST:
+
+\begin{lstlisting}[numbers=left,frame=single,mathescape]
+-p artix7
+-use_dsp48 NO
+-iobuf NO
+-ram_extract NO
+-rom_extract NO
+-fsm_extract YES
+-fsm_encoding Auto
+\end{lstlisting}
+
+The results of this comparison is summarized in Tab.~\ref{tab:synth-test}. The
+used FPGA resources (registers and LUTs) and performance (maximum frequency as
+reported by XST) are given per module (indentation indicates module hierarchy,
+the numbers are including all contained modules).
+
+For most modules the results are very similar between XST and Yosys. XST is
+used in both cases for the final mapping of logic to LUTs. So this comparison
+only compares the high-level synthesis functions (such as FSM extraction and
+encoding) of Yosys and XST.
+
+\begin{table}
+ \def\nomhz{--- \phantom{MHz}}
+ \def\P#1 {(#1\hbox to 0px{)\hss}}
+ \hfil
+ \begin{tabular}{l|rrr|rrr}
+ & \multicolumn{3}{c|}{Without Yosys} & \multicolumn{3}{c}{With Yosys} \\
+ Module & Regs & LUTs & Max. Freq. & Regs & LUTs & Max. Freq. \\
+ \hline
+ {\tt openMSP430} & 689 & 2210 & 71 MHz & 719 & 2779 & 53 MHz \\
+ {\tt \hskip1em omsp\_clock\_module} & 21 & 30 & 645 MHz & 21 & 30 & 644 MHz \\
+ {\tt \hskip1em \hskip1em omsp\_sync\_cell} & 2 & --- & 1542 MHz & 2 & --- & 1542 MHz \\
+ {\tt \hskip1em \hskip1em omsp\_sync\_reset} & 2 & --- & 1542 MHz & 2 & --- & 1542 MHz \\
+ {\tt \hskip1em omsp\_dbg} & 143 & 344 & 292 MHz & 149 & 430 & 353 MHz \\
+ {\tt \hskip1em \hskip1em omsp\_dbg\_uart} & 76 & 135 & 377 MHz & 79 & 139 & 389 MHz \\
+ {\tt \hskip1em omsp\_execution\_unit} & 266 & 911 & 80 MHz & 266 & 1034 & 137 MHz \\
+ {\tt \hskip1em \hskip1em omsp\_alu} & --- & 202 & \nomhz & --- & 263 & \nomhz \\
+ {\tt \hskip1em \hskip1em omsp\_register\_file} & 231 & 478 & 285 MHz & 231 & 506 & 293 MHz \\
+ {\tt \hskip1em omsp\_frontend} & 115 & 340 & 178 MHz & 118 & 527 & 206 MHz \\
+ {\tt \hskip1em omsp\_mem\_backbone} & 38 & 141 & 1087 MHz & 38 & 144 & 1087 MHz \\
+ {\tt \hskip1em omsp\_multiplier} & 73 & 397 & 129 MHz & 102 & 1053 & 55 MHz \\
+ {\tt \hskip1em omsp\_sfr} & 6 & 18 & 1023 MHz & 6 & 20 & 1023 MHz \\
+ {\tt \hskip1em omsp\_watchdog} & 24 & 53 & 362 MHz & 24 & 70 & 360 MHz \\
+ \hline
+ {\tt or1200\_top} & 7148 & 9969 & 135 MHz & 7173 & 10238 & 108 MHz \\
+ {\tt \hskip1em or1200\_alu} & --- & 681 & \nomhz & --- & 641 & \nomhz \\
+ {\tt \hskip1em or1200\_cfgr} & --- & 11 & \nomhz & --- & 11 & \nomhz \\
+ {\tt \hskip1em or1200\_ctrl} & 175 & 186 & 464 MHz & 174 & 279 & 377 MHz \\
+ {\tt \hskip1em or1200\_except} & 241 & 451 & 313 MHz & 241 & 353 & 301 MHz \\
+ {\tt \hskip1em or1200\_freeze} & 6 & 18 & 507 MHz & 6 & 16 & 515 MHz \\
+ {\tt \hskip1em or1200\_if} & 68 & 143 & 806 MHz & 68 & 139 & 790 MHz \\
+ {\tt \hskip1em or1200\_lsu} & 8 & 138 & \nomhz & 12 & 205 & 1306 MHz \\
+ {\tt \hskip1em \hskip1em or1200\_mem2reg} & --- & 60 & \nomhz & --- & 66 & \nomhz \\
+ {\tt \hskip1em \hskip1em or1200\_reg2mem} & --- & 29 & \nomhz & --- & 29 & \nomhz \\
+ {\tt \hskip1em or1200\_mult\_mac} & 394 & 2209 & 240 MHz & 394 & 2230 & 241 MHz \\
+ {\tt \hskip1em \hskip1em or1200\_amultp2\_32x32} & 256 & 1783 & 240 MHz & 256 & 1770 & 241 MHz \\
+ {\tt \hskip1em or1200\_operandmuxes} & 65 & 129 & 1145 MHz & 65 & 129 & 1145 MHz \\
+ {\tt \hskip1em or1200\_rf} & 1041 & 1722 & 822 MHz & 1042 & 1722 & 581 MHz \\
+ {\tt \hskip1em or1200\_sprs} & 18 & 432 & 724 MHz & 18 & 469 & 722 MHz \\
+ {\tt \hskip1em or1200\_wbmux} & 33 & 93 & \nomhz & 33 & 78 & \nomhz \\
+ {\tt \hskip1em or1200\_dc\_top} & --- & 5 & \nomhz & --- & 5 & \nomhz \\
+ {\tt \hskip1em or1200\_dmmu\_top} & 2445 & 1004 & \nomhz & 2445 & 1043 & \nomhz \\
+ {\tt \hskip1em \hskip1em or1200\_dmmu\_tlb} & 2444 & 975 & \nomhz & 2444 & 1013 & \nomhz \\
+ {\tt \hskip1em or1200\_du} & 67 & 56 & 859 MHz & 67 & 56 & 859 MHz \\
+ {\tt \hskip1em or1200\_ic\_top} & 39 & 100 & 527 MHz & 41 & 136 & 514 MHz \\
+ {\tt \hskip1em \hskip1em or1200\_ic\_fsm} & 40 & 42 & 408 MHz & 40 & 75 & 484 MHz \\
+ {\tt \hskip1em or1200\_pic} & 38 & 50 & 1169 MHz & 38 & 50 & 1177 MHz \\
+ {\tt \hskip1em or1200\_tt} & 64 & 112 & 370 MHz & 64 & 186 & 437 MHz \\
+ \end{tabular}
+ \caption{Synthesis results (as reported by XST) for OpenMSP430 and OpenRISC 1200}
+ \label{tab:synth-test}
+\end{table}
+
+\section{Conclusion and Future Work}
+
+Yosys is capable of correctly synthesizing real-world Verilog designs. The
+generated netlists are of a decent quality. However, in cases where dedicated
+hardware resources should be used for certain functions it is of course
+necessary to implement proper technology mapping for these functions in
+Yosys. This can be as easy as calling the {\tt techmap} pass with an
+architecture-specific mapping file in the synthesis script. As no such thing
+has been done in the above tests, it is only natural that the resulting designs
+cannot benefit from these dedicated hardware resources.
+
+Therefore future work includes the implementation of architecture-specific
+technology mappings besides additional frontends (VHDL), backends (EDIF),
+and above all else, application specific passes. After all, this was
+the main motivation for the development of Yosys in the first place.
+
diff --git a/manual/CHAPTER_Eval/grep-it.sh b/manual/CHAPTER_Eval/grep-it.sh
new file mode 100644
index 00000000..0f4f95ae
--- /dev/null
+++ b/manual/CHAPTER_Eval/grep-it.sh
@@ -0,0 +1,84 @@
+#!/bin/bash
+
+openmsp430_mods="
+omsp_alu
+omsp_clock_module
+omsp_dbg
+omsp_dbg_uart
+omsp_execution_unit
+omsp_frontend
+omsp_mem_backbone
+omsp_multiplier
+omsp_register_file
+omsp_sfr
+omsp_sync_cell
+omsp_sync_reset
+omsp_watchdog
+openMSP430"
+
+or1200_mods="
+or1200_alu
+or1200_amultp2_32x32
+or1200_cfgr
+or1200_ctrl
+or1200_dc_top
+or1200_dmmu_tlb
+or1200_dmmu_top
+or1200_du
+or1200_except
+or1200_fpu
+or1200_freeze
+or1200_ic_fsm
+or1200_ic_ram
+or1200_ic_tag
+or1200_ic_top
+or1200_if
+or1200_immu_tlb
+or1200_lsu
+or1200_mem2reg
+or1200_mult_mac
+or1200_operandmuxes
+or1200_pic
+or1200_pm
+or1200_qmem_top
+or1200_reg2mem
+or1200_rf
+or1200_sb
+or1200_sprs
+or1200_top
+or1200_tt
+or1200_wbmux"
+
+grep_regs() {
+ x=$(grep '^ Number of Slice Registers:' $1.syr | sed 's/.*: *//;' | cut -f1 -d' ')
+ echo $x | sed 's,^ *$,-1,'
+}
+
+grep_luts() {
+ x=$(grep '^ Number of Slice LUTs:' $1.syr | sed 's/.*: *//;' | cut -f1 -d' ')
+ echo $x | sed 's,^ *$,-1,'
+}
+
+grep_freq() {
+ x=$(grep 'Minimum period.*Maximum Frequency' $1.syr | sed 's/\.[0-9]*MHz.*//;' | cut -f3 -d:)
+ echo $x | sed 's,^ *$,-1,'
+}
+
+for mod in $openmsp430_mods $or1200_mods; do
+ printf '%-30s s,$, \\& %6d \\& %6d \\& %4d MHz \\& %6d \\& %6d \\& %4d MHz \\\\\\\\,;\n' "/${mod//_/\\\\_}}/" \
+ $(grep_regs ${mod}) $(grep_luts ${mod}) $(grep_freq ${mod}) \
+ $(grep_regs ${mod}_ys) $(grep_luts ${mod}_ys) $(grep_freq ${mod}_ys)
+done
+
+# for mod in $openmsp430_mods $or1200_mods; do
+# [ $mod = "or1200_top" -o $mod = "or1200_dmmu_top" -o $mod = or1200_dmmu_tlb -o $mod = or1200_immu_tlb ] && continue
+# regs=$(grep_regs ${mod}) regs_ys=$(grep_regs ${mod}_ys)
+# luts=$(grep_luts ${mod}) luts_ys=$(grep_luts ${mod}_ys)
+# freq=$(grep_freq ${mod}) freq_ys=$(grep_freq ${mod}_ys)
+# if [ $regs -gt 0 -a $regs_ys -gt 0 ]; then regs_p=$(( 100*regs_ys / regs )); else regs_p=NaN; fi
+# if [ $luts -gt 0 -a $luts_ys -gt 0 ]; then luts_p=$(( 100*luts_ys / luts )); else luts_p=NaN; fi
+# if [ $freq -gt 0 -a $freq_ys -gt 0 ]; then freq_p=$(( 100*freq_ys / freq )); else freq_p=NaN; fi
+# printf '%-30s %3s %3s %3s\n' $mod $regs_p $luts_p $freq_p
+#
+# done
+
diff --git a/manual/CHAPTER_Eval/openmsp430.prj b/manual/CHAPTER_Eval/openmsp430.prj
new file mode 100644
index 00000000..cb8cd271
--- /dev/null
+++ b/manual/CHAPTER_Eval/openmsp430.prj
@@ -0,0 +1,14 @@
+verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_sync_cell.v"
+verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_sync_reset.v"
+verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_register_file.v"
+verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_dbg_uart.v"
+verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_alu.v"
+verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_watchdog.v"
+verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_sfr.v"
+verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_multiplier.v"
+verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_mem_backbone.v"
+verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_frontend.v"
+verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_execution_unit.v"
+verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_dbg.v"
+verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_clock_module.v"
+verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/openMSP430.v"
diff --git a/manual/CHAPTER_Eval/openmsp430_ys.prj b/manual/CHAPTER_Eval/openmsp430_ys.prj
new file mode 100644
index 00000000..0009c99d
--- /dev/null
+++ b/manual/CHAPTER_Eval/openmsp430_ys.prj
@@ -0,0 +1 @@
+verilog work "openmsp430_ys.v"
diff --git a/manual/CHAPTER_Eval/or1200.prj b/manual/CHAPTER_Eval/or1200.prj
new file mode 100644
index 00000000..9496874e
--- /dev/null
+++ b/manual/CHAPTER_Eval/or1200.prj
@@ -0,0 +1,37 @@
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_spram.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_reg2mem.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_mem2reg.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_dpram.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_amultp2_32x32.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_wbmux.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_sprs.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_rf.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_operandmuxes.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_mult_mac.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_lsu.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_immu_tlb.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_if.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_ic_tag.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_ic_ram.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_ic_fsm.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_genpc.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_freeze.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_fpu.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_except.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_dmmu_tlb.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_ctrl.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_cfgr.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_alu.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_wb_biu.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_tt.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_sb.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_qmem_top.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_pm.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_pic.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_immu_top.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_ic_top.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_du.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_dmmu_top.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_dc_top.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_cpu.v"
+verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_top.v"
diff --git a/manual/CHAPTER_Eval/or1200_ys.prj b/manual/CHAPTER_Eval/or1200_ys.prj
new file mode 100644
index 00000000..4dd5f41a
--- /dev/null
+++ b/manual/CHAPTER_Eval/or1200_ys.prj
@@ -0,0 +1 @@
+verilog work "or1200_ys.v"
diff --git a/manual/CHAPTER_Eval/run-it.sh b/manual/CHAPTER_Eval/run-it.sh
new file mode 100644
index 00000000..b4a67ceb
--- /dev/null
+++ b/manual/CHAPTER_Eval/run-it.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+
+openmsp430_mods="
+omsp_alu
+omsp_clock_module
+omsp_dbg
+omsp_dbg_uart
+omsp_execution_unit
+omsp_frontend
+omsp_mem_backbone
+omsp_multiplier
+omsp_register_file
+omsp_sfr
+omsp_sync_cell
+omsp_sync_reset
+omsp_watchdog
+openMSP430"
+
+or1200_mods="
+or1200_alu
+or1200_amultp2_32x32
+or1200_cfgr
+or1200_ctrl
+or1200_dc_top
+or1200_dmmu_tlb
+or1200_dmmu_top
+or1200_du
+or1200_except
+or1200_fpu
+or1200_freeze
+or1200_ic_fsm
+or1200_ic_ram
+or1200_ic_tag
+or1200_ic_top
+or1200_if
+or1200_immu_tlb
+or1200_lsu
+or1200_mem2reg
+or1200_mult_mac
+or1200_operandmuxes
+or1200_pic
+or1200_pm
+or1200_qmem_top
+or1200_reg2mem
+or1200_rf
+or1200_sb
+or1200_sprs
+or1200_top
+or1200_tt
+or1200_wbmux"
+
+yosys_cmds="hierarchy -check; proc; opt; fsm; opt; memory; opt; techmap; opt; abc; opt"
+
+yosys -p "$yosys_cmds" -o openmsp430_ys.v $( cut -f2 -d'"' openmsp430.prj )
+yosys -p "$yosys_cmds" -o or1200_ys.v $( cut -f2 -d'"' or1200.prj )
+
+. /opt/Xilinx/14.5/ISE_DS/settings64.sh
+
+run_single() {
+ prj_file=$1 top_module=$2 out_file=$3
+ sed "s/@prj_file@/$prj_file/g; s/@out_file@/$out_file/g; s/@top_module@/$top_module/g;" < settings.xst > ${out_file}.xst
+ xst -ifn ${out_file}.xst -ofn ${out_file}.syr
+}
+
+for mod in $openmsp430_mods; do
+ run_single openmsp430.prj ${mod} ${mod}
+ run_single openmsp430_ys.prj ${mod} ${mod}_ys
+done
+
+for mod in $or1200_mods; do
+ run_single or1200.prj ${mod} ${mod}
+ run_single or1200_ys.prj ${mod} ${mod}_ys
+done
+
diff --git a/manual/CHAPTER_Eval/settings.xst b/manual/CHAPTER_Eval/settings.xst
new file mode 100644
index 00000000..2f381d09
--- /dev/null
+++ b/manual/CHAPTER_Eval/settings.xst
@@ -0,0 +1,2 @@
+run -ifn @prj_file@ -ofn @out_file@ -ofmt NGC -top @top_module@ -p artix7
+-use_dsp48 NO -iobuf NO -ram_extract NO -rom_extract NO -fsm_extract YES -fsm_encoding Auto
diff --git a/manual/CHAPTER_Intro.tex b/manual/CHAPTER_Intro.tex
new file mode 100644
index 00000000..76e5d847
--- /dev/null
+++ b/manual/CHAPTER_Intro.tex
@@ -0,0 +1,98 @@
+
+\chapter{Introduction}
+\label{chapter:intro}
+
+This document presents the Free and Open Source (FOSS) Verilog HDL synthesis tool ``Yosys''.
+Its design and implementation as well as its performance on real-world designs
+is discussed in this document.
+
+\section{History of Yosys}
+
+A Hardware Description Language (HDL) is a computer language used to describe
+circuits. A HDL synthesis tool is a computer program that takes a formal
+description of a circuit written in an HDL as input and generates a netlist
+that implements the given circuit as output.
+
+Currently the most widely used and supported HDLs for digital circuits are
+Verilog \cite{Verilog2005}\cite{VerilogSynth} and
+VHDL\footnote{VHDL is an acronym for ``VHSIC hardware description language''
+and VHSIC is an acronym for ``Very-High-Speed Integrated
+Circuits''.} \cite{VHDL}\cite{VHDLSynth}.
+Both HDLs are used for test and verification purposes as well as logic
+synthesis, resulting in a set of synthesizable and a set of non-synthesizable
+language features. In this document we only look at the synthesizable subset
+of the language features.
+
+In recent work on heterogeneous coarse-grain reconfigurable
+logic \cite{intersynth} the need for a custom application-specific HDL synthesis
+tool emerged. It was soon realised that a synthesis tool that understood Verilog
+or VHDL would be preferred over a synthesis tool for a custom HDL. Given an
+existing Verilog or VHDL front end, the work for writing the necessary
+additional features and integrating them in an existing tool can be estimated to be
+about the same as writing a new tool with support for a minimalistic custom HDL.
+
+The proposed custom HDL synthesis tool should be licensed under a Free
+and Open Source Software (FOSS) licence. So an existing FOSS Verilog or VHDL
+synthesis tool would have been needed as basis to build upon. The main advantages
+of choosing Verilog or VHDL is the ability to synthesize existing HDL code and
+to mitigate the requirement for circuit-designers to learn a new language. In order to take full advantage of any existing FOSS Verilog or VHDL tool,
+such a tool would have to provide a feature-complete implementation of the
+synthesizable HDL subset.
+
+Basic RTL synthesis is a well understood field \cite{LogicSynthesis}. Lexing,
+parsing and processing of computer languages \cite{Dragonbook} is a thoroughly
+researched field. All the information required to write such tools has been openly
+available for a long time, and it is therefore likely that a FOSS HDL synthesis tool
+with a feature-complete Verilog or VHDL front end must exist which can be used as a basis for a custom RTL synthesis tool.
+
+Due to the author's preference for Verilog over VHDL it was decided early
+on to go for Verilog instead of VHDL\footnote{A quick investigation into FOSS
+VHDL tools yielded similar grim results for FOSS VHDL synthesis tools.}.
+So the existing FOSS Verilog synthesis tools were evaluated (see
+App.~\ref{chapter:sota}). The results of this evaluation are utterly
+devastating. Therefore a completely new Verilog synthesis tool was implemented
+and is recommended as basis for custom synthesis tools. This is the tool that
+is discussed in this document.
+
+\section{Structure of this Document}
+
+The structure of this document is as follows:
+
+Chapter~\ref{chapter:intro} is this introduction.
+
+Chapter~\ref{chapter:basics} covers a short introduction to the world of HDL
+synthesis. Basic principles and the terminology are outlined in this chapter.
+
+Chapter~\ref{chapter:approach} gives the quickest possible outline to how the
+problem of implementing a HDL synthesis tool is approached in the case of
+Yosys.
+
+Chapter~\ref{chapter:overview} contains a more detailed overview of the
+implementation of Yosys. This chapter covers the data structures used in
+Yosys to represent a design in detail and is therefore recommended reading
+for everyone who is interested in understanding the Yosys internals.
+
+Chapter~\ref{chapter:celllib} covers the internal cell library used by Yosys.
+This is especially important knowledge for anyone who wants to understand the
+intermediate netlists used internally by Yosys.
+
+Chapter~ \ref{chapter:prog} gives a tour to the internal APIs of Yosys. This
+is recommended reading for everyone who actually wants to read or write
+Yosys source code. The chapter concludes with an example loadable module
+for Yosys.
+
+Chapters~\ref{chapter:verilog}, \ref{chapter:opt}, and \ref{chapter:techmap}
+cover three important pieces of the synthesis pipeline: The Verilog frontend,
+the optimization passes and the technology mapping to the target architecture,
+respectively.
+
+Chapter~\ref{chapter:eval} covers the evaluation of the performance
+(correctness and quality) of Yosys on real-world input data.
+The chapter concludes the main part of this document with conclusions and
+outlook to future work.
+
+Various appendices, including a command reference manual
+(App.~\ref{commandref}) and an evaluation of pre-existing FOSS Verilog
+synthesis tools (App.~\ref{chapter:sota}) complete this document.
+
+
diff --git a/manual/CHAPTER_Optimize.tex b/manual/CHAPTER_Optimize.tex
new file mode 100644
index 00000000..eee92ef5
--- /dev/null
+++ b/manual/CHAPTER_Optimize.tex
@@ -0,0 +1,324 @@
+
+\chapter{Optimizations}
+\label{chapter:opt}
+
+Yosys employs a number of optimizations to generate better and cleaner results.
+This chapter outlines these optimizations.
+
+\section{Simple Optimizations}
+
+The Yosys pass {\tt opt} runs a number of simple optimizations. This includes removing unused
+signals and cells and const folding. It is recommended to run this pass after each major step
+in the synthesis script. At the time of this writing the {\tt opt} pass executes the following
+passes that each perform a simple optimization:
+
+\begin{itemize}
+\item Once at the beginning of {\tt opt}:
+\begin{itemize}
+\item {\tt opt\_expr}
+\item {\tt opt\_merge -nomux}
+\end{itemize}
+\item Repeat until result is stable:
+\begin{itemize}
+\item {\tt opt\_muxtree}
+\item {\tt opt\_reduce}
+\item {\tt opt\_merge}
+\item {\tt opt\_rmdff}
+\item {\tt opt\_clean}
+\item {\tt opt\_expr}
+\end{itemize}
+\end{itemize}
+
+The following section describes each of the {\tt opt\_*} passes.
+
+\subsection{The opt\_expr pass}
+
+This pass performs const folding on the internal combinational cell types
+described in Chap.~\ref{chapter:celllib}. This means a cell with all constant
+inputs is replaced with the constant value this cell drives. In some cases
+this pass can also optimize cells with some constant inputs.
+
+\begin{table}
+ \hfil
+ \begin{tabular}{cc|c}
+ A-Input & B-Input & Replacement \\
+ \hline
+ any & 0 & 0 \\
+ 0 & any & 0 \\
+ 1 & 1 & 1 \\
+ \hline
+ X/Z & X/Z & X \\
+ 1 & X/Z & X \\
+ X/Z & 1 & X \\
+ \hline
+ any & X/Z & 0 \\
+ X/Z & any & 0 \\
+ \hline
+ $a$ & 1 & $a$ \\
+ 1 & $b$ & $b$ \\
+ \end{tabular}
+ \caption{Const folding rules for {\tt\$\_AND\_} cells as used in {\tt opt\_expr}.}
+ \label{tab:opt_expr_and}
+\end{table}
+
+Table~\ref{tab:opt_expr_and} shows the replacement rules used for optimizing
+an {\tt\$\_AND\_} gate. The first three rules implement the obvious const folding
+rules. Note that `any' might include dynamic values calculated by other parts
+of the circuit. The following three lines propagate undef (X) states.
+These are the only three cases in which it is allowed to propagate an undef
+according to Sec.~5.1.10 of IEEE Std. 1364-2005 \cite{Verilog2005}.
+
+The next two lines assume the value 0 for undef states. These two rules are only
+used if no other substitutions are possible in the current module. If other substitutions
+are possible they are performed first, in the hope that the `any' will change to
+an undef value or a 1 and therefore the output can be set to undef.
+
+The last two lines simply replace an {\tt\$\_AND\_} gate with one constant-1
+input with a buffer.
+
+Besides this basic const folding the {\tt opt\_expr} pass can replace 1-bit wide
+{\tt \$eq} and {\tt \$ne} cells with buffers or not-gates if one input is constant.
+
+The {\tt opt\_expr} pass is very conservative regarding optimizing {\tt \$mux} cells,
+as these cells are often used to model decision-trees and breaking these trees can
+interfere with other optimizations.
+
+\subsection{The opt\_muxtree pass}
+
+This pass optimizes trees of multiplexer cells by analyzing the select inputs.
+Consider the following simple example:
+
+\begin{lstlisting}[numbers=left,frame=single,language=Verilog]
+module uut(a, y);
+input a;
+output [1:0] y = a ? (a ? 1 : 2) : 3;
+endmodule
+\end{lstlisting}
+
+The output can never be 2, as this would require \lstinline[language=Verilog];a;
+to be 1 for the outer multiplexer and 0 for the inner multiplexer. The {\tt
+opt\_muxtree} pass detects this contradiction and replaces the inner multiplexer
+with a constant 1, yielding the logic for \lstinline[language=Verilog];y = a ? 1 : 3;.
+
+\subsection{The opt\_reduce pass}
+
+\begin{sloppypar}
+This is a simple optimization pass that identifies and consolidates identical input
+bits to {\tt \$reduce\_and} and {\tt \$reduce\_or} cells. It also sorts the input
+bits to ease identification of shareable {\tt \$reduce\_and} and {\tt \$reduce\_or} cells
+in other passes.
+\end{sloppypar}
+
+This pass also identifies and consolidates identical inputs to multiplexer cells. In this
+case the new shared select bit is driven using a {\tt \$reduce\_or} cell that combines
+the original select bits.
+
+Lastly this pass consolidates trees of {\tt \$reduce\_and} cells and trees of
+{\tt \$reduce\_or} cells to single large {\tt \$reduce\_and} or {\tt \$reduce\_or} cells.
+
+These three simple optimizations are performed in a loop until a stable result is
+produced.
+
+\subsection{The opt\_rmdff pass}
+
+This pass identifies single-bit d-type flip-flops ({\tt \$\_DFF\_*}, {\tt \$dff}, and {\tt
+\$adff} cells) with a constant data input and replaces them with a constant driver.
+
+\subsection{The opt\_clean pass}
+
+This pass identifies unused signals and cells and removes them from the design. It also
+creates an \B{unused\_bits} attribute on wires with unused bits. This attribute can be
+used for debugging or by other optimization passes.
+
+\subsection{The opt\_merge pass}
+
+This pass performs trivial resource sharing. This means that this pass identifies cells
+with identical inputs and replaces them with a single instance of the cell.
+
+The option {\tt -nomux} can be used to disable resource sharing for multiplexer
+cells ({\tt \$mux} and {\tt \$pmux}. This can be useful as
+it prevents multiplexer trees to be merged, which might prevent {\tt opt\_muxtree}
+to identify possible optimizations.
+
+\section{FSM Extraction and Encoding}
+
+The {\tt fsm} pass performs finite-state-machine (FSM) extraction and recoding. The {\tt fsm}
+pass simply executes the following other passes:
+
+\begin{itemize}
+\item Identify and extract FSMs:
+\begin{itemize}
+\item {\tt fsm\_detect}
+\item {\tt fsm\_extract}
+\end{itemize}
+
+\item Basic optimizations:
+\begin{itemize}
+\item {\tt fsm\_opt}
+\item {\tt opt\_clean}
+\item {\tt fsm\_opt}
+\end{itemize}
+
+\item Expanding to nearby gate-logic (if called with {\tt -expand}):
+\begin{itemize}
+\item {\tt fsm\_expand}
+\item {\tt opt\_clean}
+\item {\tt fsm\_opt}
+\end{itemize}
+
+\item Re-code FSM states (unless called with {\tt -norecode}):
+\begin{itemize}
+\item {\tt fsm\_recode}
+\end{itemize}
+
+\item Print information about FSMs:
+\begin{itemize}
+\item {\tt fsm\_info}
+\end{itemize}
+
+\item Export FSMs in KISS2 file format (if called with {\tt -export}):
+\begin{itemize}
+\item {\tt fsm\_export}
+\end{itemize}
+
+\item Map FSMs to RTL cells (unless called with {\tt -nomap}):
+\begin{itemize}
+\item {\tt fsm\_map}
+\end{itemize}
+\end{itemize}
+
+The {\tt fsm\_detect} pass identifies FSM state registers and marks them using the
+\B{fsm\_encoding}{\tt = "auto"} attribute. The {\tt fsm\_extract} extracts all
+FSMs marked using the \B{fsm\_encoding} attribute (unless \B{fsm\_encoding} is
+set to {\tt "none"}) and replaces the corresponding RTL cells with a {\tt \$fsm}
+cell. All other {\tt fsm\_*} passes operate on these {\tt \$fsm} cells. The
+{\tt fsm\_map} call finally replaces the {\tt \$fsm} cells with RTL cells.
+
+Note that these optimizations operate on an RTL netlist. I.e.~the {\tt fsm} pass
+should be executed after the {\tt proc} pass has transformed all
+{\tt RTLIL::Process} objects to RTL cells.
+
+The algorithms used for FSM detection and extraction are influenced by a more
+general reported technique \cite{fsmextract}.
+
+\subsection{FSM Detection}
+
+The {\tt fsm\_detect} pass identifies FSM state registers. It sets the
+\B{fsm\_encoding}{\tt = "auto"} attribute on any (multi-bit) wire that matches
+the following description:
+
+\begin{itemize}
+\item Does not already have the \B{fsm\_encoding} attribute.
+\item Is not an output of the containing module.
+\item Is driven by single {\tt \$dff} or {\tt \$adff} cell.
+\item The \B{D}-Input of this {\tt \$dff} or {\tt \$adff} cell is driven by a multiplexer
+tree that only has constants or the old state value on its leaves.
+\item The state value is only used in the said multiplexer tree or by simple relational
+cells that compare the state value to a constant (usually {\tt \$eq} cells).
+\end{itemize}
+
+This heuristic has proven to work very well. It is possible to overwrite it by setting
+\B{fsm\_encoding}{\tt = "auto"} on registers that should be considered FSM state registers
+and setting \B{fsm\_encoding}{\tt = "none"} on registers that match the above criteria
+but should not be considered FSM state registers.
+
+Note however that marking state registers with \B{fsm\_encoding} that are not
+suitable for FSM recoding can cause synthesis to fail or produce invalid
+results.
+
+\subsection{FSM Extraction}
+
+The {\tt fsm\_extract} pass operates on all state signals marked with the
+\B{fsm\_encoding} ({\tt != "none"}) attribute. For each state signal the following
+information is determined:
+
+\begin{itemize}
+\item The state registers
+\item The asynchronous reset state if the state registers use asynchronous reset
+\item All states and the control input signals used in the state transition functions
+\item The control output signals calculated from the state signals and control inputs
+\item A table of all state transitions and corresponding control inputs- and outputs
+\end{itemize}
+
+The state registers (and asynchronous reset state, if applicable) is simply determined
+by identifying the driver for the state signal.
+
+From there the {\tt \$mux}-tree driving the state register inputs is
+recursively traversed. All select inputs are control signals and the leaves of the
+{\tt \$mux}-tree are the states. The algorithm fails if a non-constant leaf
+that is not the state signal itself is found.
+
+The list of control outputs is initialized with the bits from the state signal.
+It is then extended by adding all values that are calculated by cells that
+compare the state signal with a constant value.
+
+In most cases this will cover all uses of the state register, thus rendering the
+state encoding arbitrary. If however a design uses e.g.~a single bit of the state
+value to drive a control output directly, this bit of the state signal will be
+transformed to a control output of the same value.
+
+Finally, a transition table for the FSM is generated. This is done by using the
+{\tt ConstEval} C++ helper class (defined in {\tt kernel/consteval.h}) that can
+be used to evaluate parts of the design. The {\tt ConstEval} class can be asked
+to calculate a given set of result signals using a set of signal-value
+assignments. It can also be passed a list of stop-signals that abort the {\tt
+ConstEval} algorithm if the value of a stop-signal is needed in order to
+calculate the result signals.
+
+The {\tt fsm\_extract} pass uses the {\tt ConstEval} class in the following way
+to create a transition table. For each state:
+
+\begin{enumerate}
+\item Create a {\tt ConstEval} object for the module containing the FSM
+\item Add all control inputs to the list of stop signals
+\item Set the state signal to the current state
+\item Try to evaluate the next state and control output \label{enum:fsm_extract_cealg_try}
+\item If step~\ref{enum:fsm_extract_cealg_try} was not successful:
+\begin{itemize}
+\item Recursively goto step~\ref{enum:fsm_extract_cealg_try} with the offending stop-signal set to 0.
+\item Recursively goto step~\ref{enum:fsm_extract_cealg_try} with the offending stop-signal set to 1.
+\end{itemize}
+\item If step~\ref{enum:fsm_extract_cealg_try} was successful: Emit transition
+\end{enumerate}
+
+Finally a {\tt \$fsm} cell is created with the generated transition table and added to the
+module. This new cell is connected to the control signals and the old drivers for the
+control outputs are disconnected.
+
+\subsection{FSM Optimization}
+
+The {\tt fsm\_opt} pass performs basic optimizations on {\tt \$fsm} cells (not including state
+recoding). The following optimizations are performed (in this order):
+
+\begin{itemize}
+\item Unused control outputs are removed from the {\tt \$fsm} cell. The attribute \B{unused\_bits}
+(that is usually set by the {\tt opt\_clean} pass) is used to determine which control
+outputs are unused.
+\item Control inputs that are connected to the same driver are merged.
+\item When a control input is driven by a control output, the control input is removed and the transition
+table altered to give the same performance without the external feedback path.
+\item Entries in the transition table that yield the same output and only
+differ in the value of a single control input bit are merged and the different bit is removed
+from the sensitivity list (turned into a don't-care bit).
+\item Constant inputs are removed and the transition table is altered to give an unchanged behaviour.
+\item Unused inputs are removed.
+\end{itemize}
+
+\subsection{FSM Recoding}
+
+The {\tt fsm\_recode} pass assigns new bit pattern to the states. Usually this
+also implies a change in the width of the state signal. At the moment of this
+writing only one-hot encoding with all-zero for the reset state is supported.
+
+The {\tt fsm\_recode} pass can also write a text file with the changes performed
+by it that can be used when verifying designs synthesized by Yosys using Synopsys
+Formality \citeweblink{Formality}.
+
+\section{Logic Optimization}
+
+Yosys can perform multi-level combinational logic optimization on gate-level netlists using the
+external program ABC \citeweblink{ABC}. The {\tt abc} pass extracts the combinational gate-level
+parts of the design, passes it through ABC, and re-integrates the results. The {\tt abc} pass
+can also be used to perform other operations using ABC, such as technology mapping (see
+Sec.~\ref{sec:techmap_extern} for details).
+
diff --git a/manual/CHAPTER_Overview.tex b/manual/CHAPTER_Overview.tex
new file mode 100644
index 00000000..964875d5
--- /dev/null
+++ b/manual/CHAPTER_Overview.tex
@@ -0,0 +1,526 @@
+
+\chapter{Implementation Overview}
+\label{chapter:overview}
+
+Yosys is an extensible open source hardware synthesis tool. It is aimed at
+designers who are looking for an easily accessible, universal, and
+vendor-independent synthesis tool, as well as scientists who do research in
+electronic design automation (EDA) and are looking for an open synthesis
+framework that can be used to test algorithms on complex real-world designs.
+
+Yosys can synthesize a large subset of Verilog 2005 and has been tested with a
+wide range of real-world designs, including the OpenRISC 1200 CPU
+\citeweblink{OR1200}, the openMSP430 CPU \citeweblink{openMSP430}, the
+OpenCores I$^2$C master \citeweblink{i2cmaster} and the k68 CPU \citeweblink{k68}.
+
+As of this writing a Yosys VHDL frontend is in development.
+
+Yosys is written in C++ (using some features from the new C++11 standard). This
+chapter describes some of the fundamental Yosys data structures. For the sake
+of simplicity the C++ type names used in the Yosys implementation are used in
+this chapter, even though the chapter only explains the conceptual idea behind
+it and can be used as reference to implement a similar system in any language.
+
+\section{Simplified Data Flow}
+
+Figure~\ref{fig:Overview_flow} shows the simplified data flow within Yosys.
+Rectangles in the figure represent program modules and ellipses internal
+data structures that are used to exchange design data between the program
+modules.
+
+Design data is read in using one of the frontend modules. The high-level HDL
+frontends for Verilog and VHDL code generate an abstract syntax tree (AST) that
+is then passed to the AST frontend. Note that both HDL frontends use the same
+AST representation that is powerful enough to cover the Verilog HDL and VHDL
+language.
+
+The AST Frontend then compiles the AST to Yosys's main internal data format,
+the RTL Intermediate Language (RTLIL). A more detailed description of this format
+is given in the next section.
+
+There is also a text representation of the RTLIL data structure that can be
+parsed using the ILANG Frontend.
+
+The design data may then be transformed using a series of passes that all
+operate on the RTLIL representation of the design.
+
+Finally the design in RTLIL representation is converted back to text by one
+of the backends, namely the Verilog Backend for generating Verilog netlists
+and the ILANG Backend for writing the RTLIL data in the same format that is
+understood by the ILANG Frontend.
+
+With the exception of the AST Frontend, which is called by the high-level HDL
+frontends and can't be called directly by the user, all program modules are
+called by the user (usually using a synthesis script that contains text
+commands for Yosys).
+
+By combining passes in different ways and/or adding additional passes to Yosys
+it is possible to adapt Yosys to a wide range of applications. For this to be
+possible it is key that (1) all passes operate on the same data structure
+(RTLIL) and (2) that this data structure is powerful enough to represent the design
+in different stages of the synthesis.
+
+\begin{figure}[t]
+ \hfil
+ \begin{tikzpicture}
+ \tikzstyle{process} = [draw, fill=green!10, rectangle, minimum height=3em, minimum width=10em, node distance=15em]
+ \tikzstyle{data} = [draw, fill=blue!10, ellipse, minimum height=3em, minimum width=7em, node distance=15em]
+ \node[process] (vlog) {Verilog Frontend};
+ \node[process, dashed, fill=green!5] (vhdl) [right of=vlog] {VHDL Frontend};
+ \node[process] (ilang) [right of=vhdl] {ILANG Frontend};
+ \node[data] (ast) [below of=vlog, node distance=5em, xshift=7.5em] {AST};
+ \node[process] (astfe) [below of=ast, node distance=5em] {AST Frontend};
+ \node[data] (rtlil) [below of=astfe, node distance=5em, xshift=7.5em] {RTLIL};
+ \node[process] (pass) [right of=rtlil, node distance=5em, xshift=7.5em] {Passes};
+ \node[process] (vlbe) [below of=rtlil, node distance=7em, xshift=-13em] {Verilog Backend};
+ \node[process] (ilangbe) [below of=rtlil, node distance=7em, xshift=0em] {ILANG Backend};
+ \node[process, dashed, fill=green!5] (otherbe) [below of=rtlil, node distance=7em, xshift=+13em] {Other Backends};
+
+ \draw[-latex] (vlog) -- (ast);
+ \draw[-latex] (vhdl) -- (ast);
+ \draw[-latex] (ast) -- (astfe);
+ \draw[-latex] (astfe) -- (rtlil);
+ \draw[-latex] (ilang) -- (rtlil);
+ \draw[latex-latex] (rtlil) -- (pass);
+ \draw[-latex] (rtlil) -- (vlbe);
+ \draw[-latex] (rtlil) -- (ilangbe);
+ \draw[-latex] (rtlil) -- (otherbe);
+ \end{tikzpicture}
+ \caption{Yosys simplified data flow (ellipses: data structures, rectangles: program modules)}
+ \label{fig:Overview_flow}
+\end{figure}
+
+\section{The RTL Intermediate Language}
+
+All frontends, passes and backends in Yosys operate on a design in RTLIL\footnote{The {\it Language} in {\it RTL Intermediate Language}
+refers to the fact, that RTLIL also has a text representation, usually referred to as {\it Intermediate Language} (ILANG).} representation.
+The only exception are the high-level frontends that use the AST representation as an intermediate step before generating RTLIL
+data.
+
+In order to avoid reinventing names for the RTLIL classes, they are simply referred to by their full C++ name, i.e.~including
+the {\tt RTLIL::} namespace prefix, in this document.
+
+Figure~\ref{fig:Overview_RTLIL} shows a simplified Entity-Relationship Diagram (ER Diagram) of RTLIL. In $1:N$ relationships the arrow
+points from the $N$ side to the $1$. For example one RTLIL::Design contains $N$ (zero to many) instances of RTLIL::Module.
+A two-pointed arrow indicates a $1:1$ relationship.
+
+The RTLIL::Design is the root object of the RTLIL data structure. There is always one ``current design'' in memory
+which passes operate on, frontends add data to and backends convert to exportable formats. But in some cases passes
+internally generate additional RTLIL::Design objects. For example when a pass is reading an auxiliary Verilog file such
+as a cell library, it might create an additional RTLIL::Design object and call the Verilog frontend with this
+other object to parse the cell library.
+
+\begin{figure}[t]
+ \hfil
+ \begin{tikzpicture}
+ \tikzstyle{entity} = [draw, fill=gray!10, rectangle, minimum height=3em, minimum width=7em, node distance=5em, font={\ttfamily}]
+ \node[entity] (design) {RTLIL::Design};
+ \node[entity] (module) [right of=design, node distance=11em] {RTLIL::Module} edge [-latex] node[above] {\tiny 1 \hskip3em N} (design);
+
+ \node[entity] (process) [fill=green!10, right of=module, node distance=10em] {RTLIL::Process} (process.west) edge [-latex] (module);
+ \node[entity] (memory) [fill=red!10, below of=process] {RTLIL::Memory} edge [-latex] (module);
+ \node[entity] (wire) [fill=blue!10, above of=process] {RTLIL::Wire} (wire.west) edge [-latex] (module);
+ \node[entity] (cell) [fill=blue!10, above of=wire] {RTLIL::Cell} (cell.west) edge [-latex] (module);
+
+ \node[entity] (case) [fill=green!10, right of=process, node distance=10em] {RTLIL::CaseRule} edge [latex-latex] (process);
+ \node[entity] (sync) [fill=green!10, above of=case] {RTLIL::SyncRule} edge [-latex] (process);
+ \node[entity] (switch) [fill=green!10, below of=case] {RTLIL::SwitchRule} edge [-latex] (case);
+ \draw[latex-] (switch.east) -- ++(1em,0) |- (case.east);
+ \end{tikzpicture}
+ \caption{Simplified RTLIL Entity-Relationship Diagram}
+ \label{fig:Overview_RTLIL}
+\end{figure}
+
+There is only one active RTLIL::Design object that is used by all frontends,
+passes and backends called by the user, e.g.~using a synthesis script. The RTLIL::Design then contains
+zero to many RTLIL::Module objects. This corresponds to modules in Verilog or entities in VHDL. Each
+module in turn contains objects from three different categories:
+
+\begin{itemize}
+\item RTLIL::Cell and RTLIL::Wire objects represent classical netlist data.
+\item RTLIL::Process objects represent the decision trees (if-then-else statements, etc.) and synchronization
+declarations (clock signals and sensitivity) from Verilog {\tt always} and VHDL {\tt process} blocks.
+\item RTLIL::Memory objects represent addressable memories (arrays).
+\end{itemize}
+
+\begin{sloppypar}
+Usually the output of the synthesis procedure is a netlist, i.e. all
+RTLIL::Process and RTLIL::Memory objects must be replaced by RTLIL::Cell and
+RTLIL::Wire objects by synthesis passes.
+\end{sloppypar}
+
+All features of the HDL that cannot be mapped directly to these RTLIL classes must be
+transformed to an RTLIL-compatible representation by the HDL frontend. This includes
+Verilog-features such as generate-blocks, loops and parameters.
+
+The following sections contain a more detailed description of the different
+parts of RTLIL and rationale behind some of the design decisions.
+
+\subsection{RTLIL Identifiers}
+
+All identifiers in RTLIL (such as module names, port names, signal names, cell
+types, etc.) follow the following naming convention: they must either start with
+a backslash (\textbackslash) or a dollar sign (\$).
+
+Identifiers starting with a backslash are public visible identifiers. Usually
+they originate from one of the HDL input files. For example the signal name ``{\tt \textbackslash sig42}''
+is most likely a signal that was declared using the name ``{\tt sig42}'' in an HDL input file.
+On the other hand the signal name ``{\tt \$sig42}'' is an auto-generated signal name. The backends
+convert all identifiers that start with a dollar sign to identifiers that do not collide with
+identifiers that start with a backslash.
+
+This has three advantages:
+
+\begin{itemize}
+\item First, it is impossible that an auto-generated identifier collides with
+an identifier that was provided by the user.
+\item Second, the information about which identifiers were originally
+provided by the user is always available which can help guide some optimizations. For example the ``opt\_rmunused''
+tries to preserve signals with a user-provided name but doesn't hesitate to delete signals that have
+auto-generated names when they just duplicate other signals.
+\item Third, the delicate job of finding suitable auto-generated public visible
+names is deferred to one central location. Internally auto-generated names that
+may hold important information for Yosys developers can be used without
+disturbing external tools. For example the Verilog backend assigns names in the form {\tt \_{\it integer}\_}.
+\end{itemize}
+
+In order to avoid programming errors, the RTLIL data structures check if all
+identifiers start with either a backslash or a dollar sign and generate a
+runtime error if this rule is violated.
+
+All RTLIL identifiers are case sensitive.
+
+\subsection{RTLIL::Design and RTLIL::Module}
+
+The RTLIL::Design object is basically just a container for RTLIL::Module objects. In addition to
+a list of RTLIL::Module objects the RTLIL::Design also keeps a list of {\it selected objects}, i.e.
+the objects that passes should operate on. In most cases the whole design is selected and therefore
+passes operate on the whole design. But this mechanism can be useful for more complex synthesis jobs
+in which only parts of the design should be affected by certain passes.
+
+Besides the objects shown in the ER diagram in Fig.~\ref{fig:Overview_RTLIL} an RTLIL::Module object
+contains the following additional properties:
+
+\begin{itemize}
+\item The module name
+\item A list of attributes
+\item A list of connections between wires
+\item An optional frontend callback used to derive parametrized variations of the module
+\end{itemize}
+
+The attributes can be Verilog attributes imported by the Verilog frontend or attributes assigned
+by passes. They can be used to store additional metadata about modules or just mark them to be
+used by certain part of the synthesis script but not by others.
+
+Verilog and VHDL both support parametric modules (known as ``generic entities'' in VHDL). The RTLIL
+format does not support parametric modules itself. Instead each module contains a callback function
+into the AST frontend to generate a parametrized variation of the RTLIL::Module as needed. This
+callback then returns the auto-generated name of the parametrized variation of the module. (A hash
+over the parameters and the module name is used to prohibit the same parametrized variation from being
+generated twice. For modules with only a few parameters, a name directly containing all parameters
+is generated instead of a hash string.)
+
+\subsection{RTLIL::Cell and RTLIL::Wire}
+
+A module contains zero to many RTLIL::Cell and RTLIL::Wire objects. Objects of
+these types are used to model netlists. Usually the goal of all synthesis efforts is to convert
+all modules to a state where the functionality of the module is implemented only by cells
+from a given cell library and wires to connect these cells with each other. Note that module
+ports are just wires with a special property.
+
+An RTLIL::Wire object has the following properties:
+
+\begin{itemize}
+\item The wire name
+\item A list of attributes
+\item A width (buses are just wires with a width > 1)
+\item If the wire is a port: port number and direction (input/output/inout)
+\end{itemize}
+
+As with modules, the attributes can be Verilog attributes imported by the
+Verilog frontend or attributes assigned by passes.
+
+In Yosys, busses (signal vectors) are represented using a single wire object
+with a width > 1. So Yosys does not convert signal vectors to individual signals.
+This makes some aspects of RTLIL more complex but enables Yosys to be used for
+coarse grain synthesis where the cells of the target architecture operate on
+entire signal vectors instead of single bit wires.
+
+An RTLIL::Cell object has the following properties:
+
+\begin{itemize}
+\item The cell name and type
+\item A list of attributes
+\item A list of parameters (for parametric cells)
+\item Cell ports and the connections of ports to wires and constants
+\end{itemize}
+
+The connections of ports to wires are coded by assigning an RTLIL::SigSpec
+to each cell port. The RTLIL::SigSpec data type is described in the next section.
+
+\subsection{RTLIL::SigSpec}
+
+A ``signal'' is everything that can be applied to a cell port. I.e.
+
+\begin{itemize}
+\item Any constant value of arbitrary bit-width \\
+\null\hskip1em For example: \lstinline[language=Verilog]{1337, 16'b0000010100111001, 1'b1, 1'bx}
+\item All bits of a wire or a selection of bits from a wire \\
+\null\hskip1em For example: \lstinline[language=Verilog]{mywire, mywire[24], mywire[15:8]}
+\item Concatenations of the above \\
+\null\hskip1em For example: \lstinline[language=Verilog]|{16'd1337, mywire[15:8]}|
+\end{itemize}
+
+The RTLIL::SigSpec data type is used to represent signals. The RTLIL::Cell
+object contains one RTLIL::SigSpec for each cell port.
+
+In addition, connections between wires are represented using a pair of
+RTLIL::SigSpec objects. Such pairs are needed in different locations. Therefore
+the type name RTLIL::SigSig was defined for such a pair.
+
+\subsection{RTLIL::Process}
+
+When a high-level HDL frontend processes behavioural code it splits it up into
+data path logic (e.g.~the expression {\tt a + b} is replaced by the output of an
+adder that takes {\tt a} and {\tt b} as inputs) and an RTLIL::Process that models
+the control logic of the behavioural code. Let's consider a simple example:
+
+\begin{lstlisting}[numbers=left,frame=single,language=Verilog]
+module ff_with_en_and_async_reset(clock, reset, enable, d, q);
+input clock, reset, enable, d;
+output reg q;
+always @(posedge clock, posedge reset)
+ if (reset)
+ q <= 0;
+ else if (enable)
+ q <= d;
+endmodule
+\end{lstlisting}
+
+In this example there is no data path and therefore the RTLIL::Module generated by
+the frontend only contains a few RTLIL::Wire objects and an RTLIL::Process.
+The RTLIL::Process in ILANG syntax:
+
+\begin{lstlisting}[numbers=left,frame=single,language=rtlil]
+process $proc$ff_with_en_and_async_reset.v:4$1
+ assign $0\q[0:0] \q
+ switch \reset
+ case 1'1
+ assign $0\q[0:0] 1'0
+ case
+ switch \enable
+ case 1'1
+ assign $0\q[0:0] \d
+ case
+ end
+ end
+ sync posedge \clock
+ update \q $0\q[0:0]
+ sync posedge \reset
+ update \q $0\q[0:0]
+end
+\end{lstlisting}
+
+This RTLIL::Process contains two RTLIL::SyncRule objects, two RTLIL::SwitchRule
+objects and five RTLIL::CaseRule objects. The wire {\tt \$0\textbackslash{}q[0:0]}
+is an automatically created wire that holds the next value of {\tt \textbackslash{}q}. The lines
+$2 \dots 12$ describe how {\tt \$0\textbackslash{}q[0:0]} should be calculated. The
+lines $13 \dots 16$ describe how the value of {\tt \$0\textbackslash{}q[0:0]} is used
+to update {\tt \textbackslash{}q}.
+
+An RTLIL::Process is a container for zero or more RTLIL::SyncRule objects and
+exactly one RTLIL::CaseRule object, which is called the {\it root case}.
+
+An RTLIL::SyncRule object contains an (optional) synchronization condition
+(signal and edge-type) and zero or more assignments (RTLIL::SigSig).
+
+An RTLIL::CaseRule is a container for zero or more assignments (RTLIL::SigSig)
+and zero or more RTLIL::SwitchRule objects. An RTLIL::SwitchRule objects is a
+container for zero or more RTLIL::CaseRule objects.
+
+In the above example the lines $2 \dots 12$ are the root case. Here {\tt \$0\textbackslash{}q[0:0]} is first
+assigned the old value {\tt \textbackslash{}q} as default value (line 2). The root case
+also contains an RTLIL::SwitchRule object (lines $3 \dots 12$). Such an object is very similar to the C {\tt switch}
+statement as it uses a control signal ({\tt \textbackslash{}reset} in this case) to determine
+which of its cases should be active. The RTLIL::SwitchRule object then contains one RTLIL::CaseRule
+object per case. In this example there is a case\footnote{The
+syntax {\tt 1'1} in the ILANG code specifies a constant with a length of one bit (the first ``1''),
+and this bit is a one (the second ``1'').} for {\tt \textbackslash{}reset == 1} that causes
+{\tt \$0\textbackslash{}q[0:0]} to be set (lines 4 and 5) and a default case that in turn contains a switch that
+sets {\tt \$0\textbackslash{}q[0:0]} to the value of {\tt \textbackslash{}d} if {\tt
+\textbackslash{}enable} is active (lines $6 \dots 11$).
+
+The lines $13 \dots 16$ then cause {\tt \textbackslash{}q} to be updated whenever there is
+a positive clock edge on {\tt \textbackslash{}clock} or {\tt \textbackslash{}reset}.
+
+In order to generate such a representation, the language frontend must be able to handle blocking
+and nonblocking assignments correctly. However, the language frontend does not need to identify
+the correct type of storage element for the output signal or generate multiplexers for the
+decision tree. This is done by passes that work on the RTLIL representation. Therefore it is
+relatively easy to substitute these steps with other algorithms that target different target
+architectures or perform optimizations or other transformations on the decision trees before
+further processing them.
+
+One of the first actions performed on a design in RTLIL representation in most
+synthesis scripts is identifying asynchronous resets. This is usually done using the {\tt proc\_arst}
+pass. This pass transforms the above example to the following RTLIL::Process:
+
+\begin{lstlisting}[numbers=left,frame=single,language=rtlil]
+process $proc$ff_with_en_and_async_reset.v:4$1
+ assign $0\q[0:0] \q
+ switch \enable
+ case 1'1
+ assign $0\q[0:0] \d
+ case
+ end
+ sync posedge \clock
+ update \q $0\q[0:0]
+ sync high \reset
+ update \q 1'0
+end
+\end{lstlisting}
+
+This pass has transformed the outer RTLIL::SwitchRule into a modified RTLIL::SyncRule object
+for the {\tt \textbackslash{}reset} signal. Further processing converts the RTLIL::Process
+into e.g.~a d-type flip-flop with asynchronous reset and a multiplexer for the enable signal:
+
+\begin{lstlisting}[numbers=left,frame=single,language=rtlil]
+cell $adff $procdff$6
+ parameter \ARST_POLARITY 1'1
+ parameter \ARST_VALUE 1'0
+ parameter \CLK_POLARITY 1'1
+ parameter \WIDTH 1
+ connect \ARST \reset
+ connect \CLK \clock
+ connect \D $0\q[0:0]
+ connect \Q \q
+end
+cell $mux $procmux$3
+ parameter \WIDTH 1
+ connect \A \q
+ connect \B \d
+ connect \S \enable
+ connect \Y $0\q[0:0]
+end
+\end{lstlisting}
+
+Different combinations of passes may yield different results. Note that {\tt \$adff} and {\tt
+\$mux} are internal cell types that still need to be mapped to cell types from the
+target cell library.
+
+Some passes refuse to operate on modules that still contain RTLIL::Process objects as the
+presence of these objects in a module increases the complexity. Therefore the passes to translate
+processes to a netlist of cells are usually called early in a synthesis script. The {\tt proc}
+pass calls a series of other passes that together perform this conversion in a way that is suitable
+for most synthesis tasks.
+
+\subsection{RTLIL::Memory}
+
+For every array (memory) in the HDL code an RTLIL::Memory object is created. A
+memory object has the following properties:
+
+\begin{itemize}
+\item The memory name
+\item A list of attributes
+\item The width of an addressable word
+\item The size of the memory in number of words
+\end{itemize}
+
+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.
+
+The rationale behind using separate cells for the individual ports versus
+creating a large multiport memory cell right in the language frontend is that
+the separate {\tt \$memrd} and {\tt \$memwr} cells can be consolidated using resource sharing.
+As resource sharing is a non-trivial optimization problem where different synthesis tasks
+can have different requirements it lends itself to do the optimisation in separate passes and merge
+the RTLIL::Memory objects and {\tt \$memrd} and {\tt \$memwr} cells to multiport memory blocks after resource sharing is completed.
+
+The {\tt memory} pass performs this conversion and can (depending on the options passed
+to it) transform the memories directly to d-type flip-flops and address logic or yield
+multiport memory blocks (represented using {\tt \$mem} cells).
+
+See Sec.~\ref{sec:memcells} for details about the memory cell types.
+
+\section{Command Interface and Synthesis Scripts}
+
+Yosys reads and processes commands from synthesis scripts, command line arguments and
+an interactive command prompt. Yosys commands consist of a command name and an optional
+whitespace separated list of arguments. Commands are terminated using the newline character
+or a semicolon ({\tt ;}). Empty lines and lines starting with the hash sign ({\tt \#}) are ignored.
+See Sec.~\ref{sec:typusecase} for an example synthesis script.
+
+The command {\tt help} can be used to access the command reference manual.
+
+Most commands can operate not only on the entire design but also specifically on {\it selected}
+parts of the design. For example the command {\tt dump} will print all selected objects
+in the current design while {\tt dump foobar} will only print the module {\tt foobar}
+and {\tt dump *} will print the entire design regardless of the current selection.
+
+The selection mechanism is very powerful. For example the command {\tt dump */t:\$add
+\%x:+[A] */w:* \%i} will print all wires that are connected to the \B{A} port of
+a {\tt \$add} cell. Detailed documentation of the select framework can be
+found in the command reference for the {\tt select} command.
+
+\section{Source Tree and Build System}
+
+The Yosys source tree is organized into the following top-level directories:
+
+\begin{itemize}
+
+\item {\tt backends/} \\
+This directory contains a subdirectory for each of the backend modules.
+
+\item {\tt frontends/} \\
+This directory contains a subdirectory for each of the frontend modules.
+
+\item {\tt kernel/} \\
+This directory contains all the core functionality of Yosys. This includes the
+functions and definitions for working with the RTLIL data structures ({\tt
+rtlil.h} and {\tt rtlil.cc}), the main() function ({\tt driver.cc}), the
+internal framework for generating log messages ({\tt log.h} and {\tt log.cc}),
+the internal framework for registering and calling passes ({\tt register.h} and
+{\tt register.cc}), some core commands that are not really passes ({\tt
+select.cc}, {\tt show.cc}, \dots) and a couple of other small utility libraries.
+
+\item {\tt passes/} \\
+This directory contains a subdirectory for each pass or group of passes. For example as
+of this writing the directory {\tt passes/opt/} contains the code for seven
+passes: {\tt opt}, {\tt opt\_expr}, {\tt opt\_muxtree}, {\tt opt\_reduce},
+{\tt opt\_rmdff}, {\tt opt\_rmunused} and {\tt opt\_merge}.
+
+\item {\tt techlibs/} \\
+This directory contains simulation models and standard implementations for the
+cells from the internal cell library.
+
+\item {\tt tests/} \\
+This directory contains a couple of test cases. Most of the smaller tests are executed
+automatically when {\tt make test} is called. The larger tests must be executed
+manually. Most of the larger tests require downloading external HDL source code
+and/or external tools. The tests range from comparing simulation results of the synthesized
+design to the original sources to logic equivalence checking of entire CPU cores.
+
+\end{itemize}
+
+\begin{sloppypar}
+The top-level Makefile includes {\tt frontends/*/Makefile.inc}, {\tt passes/*/Makefile.inc}
+and {\tt backends/*/Makefile.inc}. So when extending Yosys it is enough to create
+a new directory in {\tt frontends/}, {\tt passes/} or {\tt backends/} with your sources
+and a {\tt Makefile.inc}. The Yosys kernel automatically detects all commands linked with
+Yosys. So it is not needed to add additional commands to a central list of commands.
+\end{sloppypar}
+
+Good starting points for reading example source code to learn how to write passes
+are {\tt passes/opt/opt\_rmdff.cc} and {\tt passes/opt/opt\_merge.cc}.
+
+See the top-level README file for a quick {\it Getting Started} guide and build
+instructions. The Yosys build is based solely on Makefiles.
+
+Users of the Qt Creator IDE can generate a QT Creator project file using {\tt
+make qtcreator}. Users of the Eclipse IDE can use the ``Makefile Project with
+Existing Code'' project type in the Eclipse ``New Project'' dialog (only
+available after the CDT plugin has been installed) to create an Eclipse project
+in order to programming extensions to Yosys or just browse the Yosys code base.
+
diff --git a/manual/CHAPTER_Prog.tex b/manual/CHAPTER_Prog.tex
new file mode 100644
index 00000000..3cbc95a1
--- /dev/null
+++ b/manual/CHAPTER_Prog.tex
@@ -0,0 +1,26 @@
+
+\chapter{Programming Yosys Extensions}
+\label{chapter:prog}
+
+This chapter contains some bits and pieces of information about programming
+yosys extensions. Also consult the section on programming in the ``Yosys
+Presentation'' (can be downloaded from the Yosys website as PDF) and don't
+be afraid to ask questions on the Yosys Subreddit.
+
+\section{The ``CodingReadme'' File}
+
+The following is an excerpt of the {\tt CodingReadme} file from the Yosys source tree.
+
+\lstinputlisting[title=CodingReadme,rangeprefix=--,rangesuffix=--,includerangemarker=false,linerange=snip-snap,numbers=left,frame=single]{../CodingReadme}
+
+\section{The ``stubsnets'' Example Module}
+
+The following is the complete code of the ``stubsnets'' example module. It is included in the Yosys source distribution as {\tt manual/CHAPTER\_Prog/stubnets.cc}.
+
+
+\lstinputlisting[title=stubnets.cc,numbers=left,frame=single,language=C++]{CHAPTER_Prog/stubnets.cc}
+
+\lstinputlisting[title=Makefile,numbers=left,frame=single,language=make]{CHAPTER_Prog/Makefile}
+
+\lstinputlisting[title=test.v,numbers=left,frame=single,language=Verilog]{CHAPTER_Prog/test.v}
+
diff --git a/manual/CHAPTER_Prog/.gitignore b/manual/CHAPTER_Prog/.gitignore
new file mode 100644
index 00000000..fa83c321
--- /dev/null
+++ b/manual/CHAPTER_Prog/.gitignore
@@ -0,0 +1,3 @@
+stubnets.so
+stubnets.d
+*.log
diff --git a/manual/CHAPTER_Prog/Makefile b/manual/CHAPTER_Prog/Makefile
new file mode 100644
index 00000000..8e326bdc
--- /dev/null
+++ b/manual/CHAPTER_Prog/Makefile
@@ -0,0 +1,12 @@
+test: stubnets.so
+ yosys -ql test1.log -m ./stubnets.so test.v -p "stubnets"
+ yosys -ql test2.log -m ./stubnets.so test.v -p "opt; stubnets"
+ yosys -ql test3.log -m ./stubnets.so test.v -p "techmap; opt; stubnets -report_bits"
+ tail test1.log test2.log test3.log
+
+stubnets.so: stubnets.cc
+ yosys-config --exec --cxx --cxxflags --ldflags -o $@ -shared $^ --ldlibs
+
+clean:
+ rm -f test1.log test2.log test3.log
+ rm -f stubnets.so stubnets.d
diff --git a/manual/CHAPTER_Prog/stubnets.cc b/manual/CHAPTER_Prog/stubnets.cc
new file mode 100644
index 00000000..eb77bd40
--- /dev/null
+++ b/manual/CHAPTER_Prog/stubnets.cc
@@ -0,0 +1,130 @@
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+#include <string>
+#include <map>
+#include <set>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+// this function is called for each module in the design
+static void find_stub_nets(RTLIL::Design *design, RTLIL::Module *module, bool report_bits)
+{
+ // use a SigMap to convert nets to a unique representation
+ SigMap sigmap(module);
+
+ // count how many times a single-bit signal is used
+ std::map<RTLIL::SigBit, int> bit_usage_count;
+
+ // count output lines for this module (needed only for summary output at the end)
+ int line_count = 0;
+
+ log("Looking for stub wires in module %s:\n", RTLIL::id2cstr(module->name));
+
+ // For all ports on all cells
+ for (auto &cell_iter : module->cells_)
+ for (auto &conn : cell_iter.second->connections())
+ {
+ // Get the signals on the port
+ // (use sigmap to get a uniqe signal name)
+ RTLIL::SigSpec sig = sigmap(conn.second);
+
+ // add each bit to bit_usage_count, unless it is a constant
+ for (auto &bit : sig)
+ if (bit.wire != NULL)
+ bit_usage_count[bit]++;
+ }
+
+ // for each wire in the module
+ for (auto &wire_iter : module->wires_)
+ {
+ RTLIL::Wire *wire = wire_iter.second;
+
+ // .. but only selected wires
+ if (!design->selected(module, wire))
+ continue;
+
+ // add +1 usage if this wire actually is a port
+ int usage_offset = wire->port_id > 0 ? 1 : 0;
+
+ // we will record which bits of the (possibly multi-bit) wire are stub signals
+ std::set<int> stub_bits;
+
+ // get a signal description for this wire and split it into separate bits
+ RTLIL::SigSpec sig = sigmap(wire);
+
+ // for each bit (unless it is a constant):
+ // check if it is used at least two times and add to stub_bits otherwise
+ for (int i = 0; i < GetSize(sig); i++)
+ if (sig[i].wire != NULL && (bit_usage_count[sig[i]] + usage_offset) < 2)
+ stub_bits.insert(i);
+
+ // continue if no stub bits found
+ if (stub_bits.size() == 0)
+ continue;
+
+ // report stub bits and/or stub wires, don't report single bits
+ // if called with report_bits set to false.
+ if (GetSize(stub_bits) == GetSize(sig)) {
+ log(" found stub wire: %s\n", RTLIL::id2cstr(wire->name));
+ } else {
+ if (!report_bits)
+ continue;
+ log(" found wire with stub bits: %s [", RTLIL::id2cstr(wire->name));
+ for (int bit : stub_bits)
+ log("%s%d", bit == *stub_bits.begin() ? "" : ", ", bit);
+ log("]\n");
+ }
+
+ // we have outputted a line, increment summary counter
+ line_count++;
+ }
+
+ // report summary
+ if (report_bits)
+ log(" found %d stub wires or wires with stub bits.\n", line_count);
+ else
+ log(" found %d stub wires.\n", line_count);
+}
+
+// each pass contains a singleton object that is derived from Pass
+struct StubnetsPass : public Pass {
+ StubnetsPass() : Pass("stubnets") { }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ // variables to mirror information from passed options
+ bool report_bits = 0;
+
+ log_header(design, "Executing STUBNETS pass (find stub nets).\n");
+
+ // parse options
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-report_bits") {
+ report_bits = true;
+ continue;
+ }
+ break;
+ }
+
+ // handle extra options (e.g. selection)
+ extra_args(args, argidx, design);
+
+ // call find_stub_nets() for each module that is either
+ // selected as a whole or contains selected objects.
+ for (auto &it : design->modules_)
+ if (design->selected_module(it.first))
+ find_stub_nets(design, it.second, report_bits);
+ }
+} StubnetsPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/manual/CHAPTER_Prog/test.v b/manual/CHAPTER_Prog/test.v
new file mode 100644
index 00000000..201f7500
--- /dev/null
+++ b/manual/CHAPTER_Prog/test.v
@@ -0,0 +1,8 @@
+module uut(in1, in2, in3, out1, out2);
+
+input [8:0] in1, in2, in3;
+output [8:0] out1, out2;
+
+assign out1 = in1 + in2 + (in3 >> 4);
+
+endmodule
diff --git a/manual/CHAPTER_StateOfTheArt.tex b/manual/CHAPTER_StateOfTheArt.tex
new file mode 100644
index 00000000..2d0c77a0
--- /dev/null
+++ b/manual/CHAPTER_StateOfTheArt.tex
@@ -0,0 +1,289 @@
+
+\chapter{Evaluation of other OSS Verilog Synthesis Tools}
+\label{chapter:sota}
+
+In this appendix\footnote{This appendix is an updated version of an
+unpublished student research paper. \cite{VerilogFossEval}}
+the existing FOSS Verilog synthesis tools\footnote{To the
+author's best knowledge, all relevant tools that existed at the time of this
+writing are included. But as there is no formal channel through which such
+tools are published it is hard to give any guarantees in that matter.} are
+evaluated. Extremely limited or application specific tools (e.g.~pure Verilog
+Netlist parsers) as well as Verilog simulators are not included. These existing
+solutions are tested using a set of representative Verilog code snippets. It is
+shown that no existing FOSS tool implements even close to a sufficient subset
+of Verilog to be usable as synthesis tool for a wide range existing Verilog code.
+
+The packages evaluated are:
+
+\begin{itemize}
+\item Icarus Verilog \citeweblink{Icarus}\footnote{Icarus Verilog is mainly a simulation
+tool but also supported synthesis up to version 0.8. Therefore version 0.8.7 is used
+for this evaluation.)}
+\item Verilog-to-Routing (VTR) / Odin-II \cite{vtr2012}\cite{Odin}\citeweblink{VTR}
+\item HDL Analyzer and Netlist Architect (HANA) \citeweblink{HANA}
+\item Verilog front-end to VIS (vl2mv) \cite{Cheng93vl2mv:a}\citeweblink{VIS}
+\end{itemize}
+
+In each of the following sections Verilog modules that test a certain Verilog
+language feature are presented and the support for these features is tested in all
+the tools mentioned above. It is evaluated whether the tools under test
+successfully generate netlists for the Verilog input and whether these netlists
+match the simulation behavior of the designs using testbenches.
+
+All test cases are verified to be synthesizeable using Xilinx XST from the Xilinx
+WebPACK \citeweblink{XilinxWebPACK} suite.
+
+Trivial features such as support for simple structural Verilog are not explicitly tested.
+
+Vl2mv and Odin-II generate output in the BLIF (Berkeley Logic Interchange
+Format) and BLIF-MV (an extended version of BLIF) formats respectively.
+ABC \citeweblink{ABC} is used to convert this output to Verilog for verification
+using testbenches.
+
+Icarus Verilog generates EDIF (Electronic Design Interchange Format) output
+utilizing LPM (Library of Parameterized Modules) cells. The EDIF files are
+converted to Verilog using edif2ngd and netgen from Xilinx WebPACK. A
+hand-written implementation of the LPM cells utilized by the generated netlists
+is used for verification.
+
+Following these functional tests, a quick analysis of the extensibility of the tools
+under test is provided in a separate section.
+
+The last section of this chapter finally concludes these series of evaluations
+with a summary of the results.
+
+\begin{figure}[t!]
+ \begin{minipage}{7.7cm}
+ \lstinputlisting[numbers=left,frame=single,language=Verilog]{CHAPTER_StateOfTheArt/always01_pub.v}
+ \end{minipage}
+ \hfill
+ \begin{minipage}{7.7cm}
+ \lstinputlisting[frame=single,language=Verilog]{CHAPTER_StateOfTheArt/always02_pub.v}
+ \end{minipage}
+ \caption{1st and 2nd Verilog always examples}
+ \label{fig:StateOfTheArt_always12}
+\end{figure}
+
+\begin{figure}[!]
+ \lstinputlisting[numbers=left,frame=single,language=Verilog]{CHAPTER_StateOfTheArt/always03.v}
+ \caption{3rd Verilog always example}
+ \label{fig:StateOfTheArt_always3}
+\end{figure}
+
+\section{Always blocks and blocking vs.~nonblocking assignments}
+\label{sec:blocking_nonblocking}
+
+The ``always''-block is one of the most fundamental non-trivial Verilog
+language features. It can be used to model a combinatorial path (with optional
+registers on the outputs) in a way that mimics a regular programming language.
+
+Within an always block, if- and case-statements can be used to model multiplexers.
+Blocking assignments ($=$) and nonblocking assignments ($<=$) are used to populate the
+leaf-nodes of these multiplexer trees. Unassigned leaf-nodes default to feedback
+paths that cause the output register to hold the previous value. More advanced
+synthesis tools often convert these feedback paths to register enable signals or
+even generate circuits with clock gating.
+
+Registers assigned with nonblocking assignments ($<=$) behave differently from
+variables in regular programming languages: In a simulation they are not
+updated immediately after being assigned. Instead the right-hand sides are
+evaluated and the results stored in temporary memory locations. After all
+pending updates have been prepared in this way they are executed, thus yielding
+semi-parallel execution of all nonblocking assignments.
+
+For synthesis this means that every occurrence of that register in an expression
+addresses the output port of the corresponding register regardless of the question whether the register
+has been assigned a new value in an earlier command in the same always block.
+Therefore with nonblocking assignments the order of the assignments has no effect
+on the resulting circuit as long as the left-hand sides of the assignments are
+unique.
+
+The three example codes in Fig.~\ref{fig:StateOfTheArt_always12} and
+Fig.~\ref{fig:StateOfTheArt_always3} use all these features and can thus be used
+to test the synthesis tools capabilities to synthesize always blocks correctly.
+
+The first example is only using the most fundamental Verilog features. All
+tools under test were able to successfully synthesize this design.
+
+\begin{figure}[b!]
+ \lstinputlisting[numbers=left,frame=single,language=Verilog]{CHAPTER_StateOfTheArt/arrays01.v}
+ \caption{Verilog array example}
+ \label{fig:StateOfTheArt_arrays}
+\end{figure}
+
+The 2nd example is functionally identical to the 1st one but is using an
+if-statement inside the always block. Odin-II fails to synthesize it and
+instead produces the following error message:
+
+\begin{verbatim}
+ERROR: (File: always02.v) (Line number: 13)
+You've defined the driver "count~0" twice
+\end{verbatim}
+
+Vl2mv does not produce an error message but outputs an invalid synthesis result
+that is not using the reset input at all.
+
+Icarus Verilog also doesn't produce an error message but generates an invalid output
+for this 2nd example. The code generated by Icarus Verilog only implements the reset
+path for the count register, effectively setting the output to constant 0.
+
+So of all tools under test only HANA was able to create correct synthesis results
+for the 2nd example.
+
+The 3rd example is using blocking and nonblocking assignments and many if statements.
+Odin also fails to synthesize this example:
+
+\begin{verbatim}
+ERROR: (File: always03.v) (Line number: 8)
+ODIN doesn't handle blocking statements in Sequential blocks
+\end{verbatim}
+
+HANA, Icarus Verilog and vl2mv create invalid synthesis results for the 3rd example.
+
+So unfortunately none of the tools under test provide a complete and correct
+implementation of blocking and nonblocking assignments.
+
+\section{Arrays for memory modelling}
+
+Verilog arrays are part of the synthesizeable subset of Verilog and are
+commonly used to model addressable memory. The Verilog code in
+Fig.~\ref{fig:StateOfTheArt_arrays} demonstrates this by implementing a single
+port memory.
+
+For this design HANA, vl2m and ODIN-II generate error messages indicating that
+arrays are not supported.
+
+\begin{figure}[t!]
+ \lstinputlisting[numbers=left,frame=single,language=Verilog]{CHAPTER_StateOfTheArt/forgen01.v}
+ \caption{Verilog for loop example}
+ \label{fig:StateOfTheArt_for}
+\end{figure}
+
+Icarus Verilog produces an invalid output that is using the address only for
+reads. Instead of using the address input for writes, the generated design
+simply loads the data to all memory locations whenever the write-enable input
+is active, effectively turning the design into a single 4-bit D-Flip-Flop with
+enable input.
+
+As all tools under test already fail this simple test, there is nothing to gain
+by continuing tests on this aspect of Verilog synthesis such as synthesis of dual port
+memories, correct handling of write collisions, and so forth.
+
+\begin{figure}[t!]
+ \lstinputlisting[numbers=left,frame=single,language=Verilog]{CHAPTER_StateOfTheArt/forgen02.v}
+ \caption{Verilog generate example}
+ \label{fig:StateOfTheArt_gen}
+\end{figure}
+
+\section{For-loops and generate blocks}
+
+For-loops and generate blocks are more advanced Verilog features. These features
+allow the circuit designer to add program code to her design that is evaluated
+during synthesis to generate (parts of) the circuits description; something that
+could only be done using a code generator otherwise.
+
+For-loops are only allowed in synthesizeable Verilog if they can be completely
+unrolled. Then they can be a powerful tool to generate array logic or static
+lookup tables. The code in Fig.~\ref{fig:StateOfTheArt_for} generates a circuit that
+tests a 5 bit value for being a prime number using a static lookup table.
+
+Generate blocks can be used to model array logic in complex parametric designs. The
+code in Fig.~\ref{fig:StateOfTheArt_gen} implements a ripple-carry adder with
+parametric width from simple assign-statements and logic operations using a Verilog
+generate block.
+
+All tools under test failed to synthesize both test cases. HANA creates invalid
+output in both cases. Icarus Verilog creates invalid output for the first
+test and fails with an error for the second case. The other two tools fail with
+error messages for both tests.
+
+\section{Extensibility}
+
+This section briefly discusses the extensibility of the tools under test and
+their internal data- and control-flow. As all tools under test already failed
+to synthesize simple Verilog always-blocks correctly, not much resources have
+been spent on evaluating the extensibility of these tools and therefore only a
+very brief discussion of the topic is provided here.
+
+HANA synthesizes for a built-in library of standard cells using two passes over
+an AST representation of the Verilog input. This approach executes fast but
+limits the extensibility as everything happens in only two comparable complex
+AST walks and there is no universal intermediate representation that is flexible
+enough to be used in arbitrary optimizations.
+
+Odin-II and vl2m are both front ends to existing synthesis flows. As such they
+only try to quickly convert the Verilog input into the internal representation
+of their respective flows (BLIF). So extensibility is less of an issue here as
+potential extensions would likely be implemented in other components of the
+flow.
+
+Icarus Verilog is clearly designed to be a simulation tool rather than a
+synthesis tool. The synthesis part of Icarus Verilog is an ad-hoc add-on to
+Icarus Verilog that aims at converting an internal representation that is meant
+for generation of a virtual machine based simulation code to netlists.
+
+\section{Summary and Outlook}
+
+Table~\ref{tab:StateOfTheArt_sum} summarizes the tests performed. Clearly none
+of the tools under test make a serious attempt at providing a feature-complete
+implementation of Verilog. It can be argued that Odin-II performed best in the
+test as it never generated incorrect code but instead produced error messages
+indicating that unsupported Verilog features where used in the Verilog input.
+
+In conclusion, to the best knowledge of the author, there is no FOSS Verilog
+synthesis tool other than Yosys that is anywhere near feature completeness and
+therefore there is no other candidate for a generic Verilog front end and/or
+synthesis framework to be used as a basis for custom synthesis tools.
+
+Yosys could also replace vl2m and/or Odin-II in their respective flows or
+function as a pre-compiler that can translate full-featured Verilog code to the
+simple subset of Verilog that is understood by vl2m and Odin-II.
+
+Yosys is designed for extensibility. It can be used as-is to synthesize Verilog
+code to netlists, but its main purpose is to be used as basis for custom tools.
+Yosys is structured in a language dependent Verilog front end and language
+independent synthesis code (which is in itself structured in independent
+passes). This architecture will simplify implementing additional HDL front
+ends and/or additional synthesis passes.
+
+Chapter~\ref{chapter:eval} contains a more detailed evaluation of Yosys using real-world
+designs that are far out of reach for any of the other tools discussed in this appendix.
+
+\vskip2cm
+\begin{table}[h]
+ % yosys hana vis icarus odin
+ % always01 ok ok ok ok ok
+ % always02 ok ok failed failed error
+ % always03 ok failed failed missing error
+ % arrays01 ok error error failed error
+ % forgen01 ok failed error failed error
+ % forgen02 ok failed error error error
+ \def\ok{\ding{52}}
+ \def\error{\ding{56}}
+ \def\failed{$\skull$}
+ \def\missing{$\skull$}
+ \rowcolors{2}{gray!25}{white}
+ \centerline{
+ \begin{tabular}{|l|cccc|c|}
+ \hline
+ & \bf HANA & \bf VIS / vl2m & \bf Icarus Verilog & \bf Odin-II & \bf Yosys \\
+ \hline
+ \tt always01 & \ok & \ok & \ok & \ok & \ok \\
+ \tt always02 & \ok & \failed & \failed & \error & \ok \\
+ \tt always03 & \failed & \failed & \missing & \error & \ok \\
+ \tt arrays01 & \error & \error & \failed & \error & \ok \\
+ \tt forgen01 & \failed & \error & \failed & \error & \ok \\
+ \tt forgen02 & \failed & \error & \error & \error & \ok \\
+ \hline
+ \end{tabular}
+ }
+ \centerline{
+ \ding{52} \dots passed \hskip2em
+ \ding{56} \dots produced error \hskip2em
+ $\skull$ \dots incorrect output
+ }
+ \caption{Summary of all test results}
+ \label{tab:StateOfTheArt_sum}
+\end{table}
+
diff --git a/manual/CHAPTER_StateOfTheArt/always01.v b/manual/CHAPTER_StateOfTheArt/always01.v
new file mode 100644
index 00000000..4719ed47
--- /dev/null
+++ b/manual/CHAPTER_StateOfTheArt/always01.v
@@ -0,0 +1,12 @@
+module uut_always01(clock, reset, c3, c2, c1, c0);
+
+input clock, reset;
+output c3, c2, c1, c0;
+reg [3:0] count;
+
+assign {c3, c2, c1, c0} = count;
+
+always @(posedge clock)
+ count <= reset ? 0 : count + 1;
+
+endmodule
diff --git a/manual/CHAPTER_StateOfTheArt/always01_pub.v b/manual/CHAPTER_StateOfTheArt/always01_pub.v
new file mode 100644
index 00000000..6a6a4b23
--- /dev/null
+++ b/manual/CHAPTER_StateOfTheArt/always01_pub.v
@@ -0,0 +1,14 @@
+module uut_always01(clock,
+ reset, count);
+
+input clock, reset;
+output [3:0] count;
+reg [3:0] count;
+
+always @(posedge clock)
+ count <= reset ?
+ 0 : count + 1;
+
+
+
+endmodule
diff --git a/manual/CHAPTER_StateOfTheArt/always02.v b/manual/CHAPTER_StateOfTheArt/always02.v
new file mode 100644
index 00000000..63f1ce31
--- /dev/null
+++ b/manual/CHAPTER_StateOfTheArt/always02.v
@@ -0,0 +1,15 @@
+module uut_always02(clock, reset, c3, c2, c1, c0);
+
+input clock, reset;
+output c3, c2, c1, c0;
+reg [3:0] count;
+
+assign {c3, c2, c1, c0} = count;
+
+always @(posedge clock) begin
+ count <= count + 1;
+ if (reset)
+ count <= 0;
+end
+
+endmodule
diff --git a/manual/CHAPTER_StateOfTheArt/always02_pub.v b/manual/CHAPTER_StateOfTheArt/always02_pub.v
new file mode 100644
index 00000000..91f1ca16
--- /dev/null
+++ b/manual/CHAPTER_StateOfTheArt/always02_pub.v
@@ -0,0 +1,14 @@
+module uut_always02(clock,
+ reset, count);
+
+input clock, reset;
+output [3:0] count;
+reg [3:0] count;
+
+always @(posedge clock) begin
+ count <= count + 1;
+ if (reset)
+ count <= 0;
+end
+
+endmodule
diff --git a/manual/CHAPTER_StateOfTheArt/always03.v b/manual/CHAPTER_StateOfTheArt/always03.v
new file mode 100644
index 00000000..53386acd
--- /dev/null
+++ b/manual/CHAPTER_StateOfTheArt/always03.v
@@ -0,0 +1,23 @@
+module uut_always03(clock, in1, in2, in3, in4, in5, in6, in7,
+ out1, out2, out3);
+
+input clock, in1, in2, in3, in4, in5, in6, in7;
+output out1, out2, out3;
+reg out1, out2, out3;
+
+always @(posedge clock) begin
+ out1 = in1;
+ if (in2)
+ out1 = !out1;
+ out2 <= out1;
+ if (in3)
+ out2 <= out2;
+ if (in4)
+ if (in5)
+ out3 <= in6;
+ else
+ out3 <= in7;
+ out1 = out1 ^ out2;
+end
+
+endmodule
diff --git a/manual/CHAPTER_StateOfTheArt/arrays01.v b/manual/CHAPTER_StateOfTheArt/arrays01.v
new file mode 100644
index 00000000..bd0eda29
--- /dev/null
+++ b/manual/CHAPTER_StateOfTheArt/arrays01.v
@@ -0,0 +1,16 @@
+module uut_arrays01(clock, we, addr, wr_data, rd_data);
+
+input clock, we;
+input [3:0] addr, wr_data;
+output [3:0] rd_data;
+reg [3:0] rd_data;
+
+reg [3:0] memory [15:0];
+
+always @(posedge clock) begin
+ if (we)
+ memory[addr] <= wr_data;
+ rd_data <= memory[addr];
+end
+
+endmodule
diff --git a/manual/CHAPTER_StateOfTheArt/cmp_tbdata.c b/manual/CHAPTER_StateOfTheArt/cmp_tbdata.c
new file mode 100644
index 00000000..b188144d
--- /dev/null
+++ b/manual/CHAPTER_StateOfTheArt/cmp_tbdata.c
@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+int line = 0;
+char buffer1[1024];
+char buffer2[1024];
+
+void check(bool ok)
+{
+ if (ok)
+ return;
+ // fprintf(stderr, "Error in testbench output compare (line=%d):\n-%s\n+%s\n", line, buffer1, buffer2);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ FILE *f1, *f2;
+ bool eof1, eof2;
+ int i;
+
+ check(argc == 3);
+
+ f1 = fopen(argv[1], "r");
+ f2 = fopen(argv[2], "r");
+
+ check(f1 && f2);
+
+ while (!feof(f1) && !feof(f2))
+ {
+ line++;
+ buffer1[0] = 0;
+ buffer2[0] = 0;
+
+ eof1 = fgets(buffer1, 1024, f1) == NULL;
+ eof2 = fgets(buffer2, 1024, f2) == NULL;
+
+ if (*buffer1 && buffer1[strlen(buffer1)-1] == '\n')
+ buffer1[strlen(buffer1)-1] = 0;
+
+ if (*buffer2 && buffer2[strlen(buffer2)-1] == '\n')
+ buffer2[strlen(buffer2)-1] = 0;
+
+ check(eof1 == eof2);
+
+ for (i = 0; buffer1[i] || buffer2[i]; i++)
+ {
+ check(buffer1[i] != 0 && buffer2[i] != 0);
+
+ // first argument is the reference. An 'z' or 'x'
+ // here means we don't care about the result.
+ if (buffer1[i] == 'z' || buffer1[i] == 'x')
+ continue;
+
+ check(buffer1[i] == buffer2[i]);
+ }
+ }
+
+ check(feof(f1) && feof(f2));
+
+ fclose(f1);
+ fclose(f2);
+ return 0;
+}
+
diff --git a/manual/CHAPTER_StateOfTheArt/forgen01.v b/manual/CHAPTER_StateOfTheArt/forgen01.v
new file mode 100644
index 00000000..70ee7e66
--- /dev/null
+++ b/manual/CHAPTER_StateOfTheArt/forgen01.v
@@ -0,0 +1,20 @@
+module uut_forgen01(a, y);
+
+input [4:0] a;
+output y;
+
+integer i, j;
+reg [31:0] lut;
+
+initial begin
+ for (i = 0; i < 32; i = i+1) begin
+ lut[i] = i > 1;
+ for (j = 2; j*j <= i; j = j+1)
+ if (i % j == 0)
+ lut[i] = 0;
+ end
+end
+
+assign y = lut[a];
+
+endmodule
diff --git a/manual/CHAPTER_StateOfTheArt/forgen02.v b/manual/CHAPTER_StateOfTheArt/forgen02.v
new file mode 100644
index 00000000..14af070c
--- /dev/null
+++ b/manual/CHAPTER_StateOfTheArt/forgen02.v
@@ -0,0 +1,30 @@
+module uut_forgen02(a, b, cin, y, cout);
+
+parameter WIDTH = 8;
+
+input [WIDTH-1:0] a, b;
+input cin;
+
+output [WIDTH-1:0] y;
+output cout;
+
+genvar i;
+wire [WIDTH-1:0] carry;
+
+generate
+ for (i = 0; i < WIDTH; i=i+1) begin:adder
+ wire [2:0] D;
+ assign D[1:0] = { a[i], b[i] };
+ if (i == 0) begin:chain
+ assign D[2] = cin;
+ end else begin:chain
+ assign D[2] = carry[i-1];
+ end
+ assign y[i] = ^D;
+ assign carry[i] = &D[1:0] | (^D[1:0] & D[2]);
+ end
+endgenerate
+
+assign cout = carry[WIDTH-1];
+
+endmodule
diff --git a/manual/CHAPTER_StateOfTheArt/iverilog-0.8.7-buildfixes.patch b/manual/CHAPTER_StateOfTheArt/iverilog-0.8.7-buildfixes.patch
new file mode 100644
index 00000000..63a03e59
--- /dev/null
+++ b/manual/CHAPTER_StateOfTheArt/iverilog-0.8.7-buildfixes.patch
@@ -0,0 +1,20 @@
+--- ./elab_net.cc.orig 2012-10-27 22:11:05.345688820 +0200
++++ ./elab_net.cc 2012-10-27 22:12:23.398075860 +0200
+@@ -29,6 +29,7 @@
+
+ # include <iostream>
+ # include <cstring>
++# include <memory>
+
+ /*
+ * This is a state flag that determines whether an elaborate_net must
+--- ./syn-rules.y.orig 2012-10-27 22:25:38.890020489 +0200
++++ ./syn-rules.y 2012-10-27 22:25:49.146071350 +0200
+@@ -25,6 +25,7 @@
+ # include "config.h"
+
+ # include <iostream>
++# include <stdio.h>
+
+ /*
+ * This file implements synthesis based on matching threads and
diff --git a/manual/CHAPTER_StateOfTheArt/mvsis-1.3.6-buildfixes.patch b/manual/CHAPTER_StateOfTheArt/mvsis-1.3.6-buildfixes.patch
new file mode 100644
index 00000000..4b44320f
--- /dev/null
+++ b/manual/CHAPTER_StateOfTheArt/mvsis-1.3.6-buildfixes.patch
@@ -0,0 +1,36 @@
+--- ./helpers/config.sub.orig 2012-10-27 22:09:04.429089223 +0200
++++ ./helpers/config.sub 2012-10-27 22:09:11.501124295 +0200
+@@ -158,6 +158,7 @@
+ | sparc | sparclet | sparclite | sparc64)
+ basic_machine=$basic_machine-unknown
+ ;;
++ x86_64-pc) ;;
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+--- ./src/base/ntki/ntkiFrames.c.orig 2012-10-27 22:09:26.961200963 +0200
++++ ./src/base/ntki/ntkiFrames.c 2012-10-27 22:09:32.901230409 +0200
+@@ -23,7 +23,7 @@
+ ////////////////////////////////////////////////////////////////////////
+
+ static void Ntk_NetworkAddFrame( Ntk_Network_t * pNetNew, Ntk_Network_t * pNet, int iFrame );
+-static void Ntk_NetworkReorderCiCo( Ntk_Network_t * pNet );
++// static void Ntk_NetworkReorderCiCo( Ntk_Network_t * pNet );
+
+ extern int Ntk_NetworkVerifyVariables( Ntk_Network_t * pNet1, Ntk_Network_t * pNet2, int fVerbose );
+
+--- ./src/graph/wn/wnStrashBin.c.orig 2012-10-27 22:27:29.966571294 +0200
++++ ./src/graph/wn/wnStrashBin.c 2012-10-27 22:27:55.898699881 +0200
+@@ -76,8 +76,10 @@
+ // assert( RetValue );
+
+ // clean the data of the nodes in the window
+- Ntk_NetworkForEachNodeSpecial( pWnd->pNet, pNode )
+- pNode->pCopy = (Ntk_Node_t *)pNode->pData = NULL;
++ Ntk_NetworkForEachNodeSpecial( pWnd->pNet, pNode ) {
++ pNode->pData = NULL;
++ pNode->pCopy = NULL;
++ }
+
+ // set the leaves
+ pgInputs = Sh_ManagerReadVars( pMan );
diff --git a/manual/CHAPTER_StateOfTheArt/simlib_hana.v b/manual/CHAPTER_StateOfTheArt/simlib_hana.v
new file mode 100644
index 00000000..7fb54fa4
--- /dev/null
+++ b/manual/CHAPTER_StateOfTheArt/simlib_hana.v
@@ -0,0 +1,1139 @@
+/*
+Copyright (C) 2009-2010 Parvez Ahmad
+Written by Parvez Ahmad <parvez_ahmad@yahoo.co.uk>.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+
+module BUF (input in, output out);
+
+assign out = in;
+
+endmodule
+
+module TRIBUF(input in, enable, output out);
+
+assign out = enable ? in : 1'bz;
+
+endmodule
+
+module INV(input in, output out);
+
+assign out = ~in;
+
+endmodule
+
+module AND2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out);
+
+assign out = &in;
+
+endmodule
+
+module AND3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out);
+
+assign out = &in;
+
+endmodule
+
+module AND4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out);
+
+assign out = &in;
+
+endmodule
+
+module OR2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out);
+
+assign out = |in;
+
+endmodule
+
+module OR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out);
+
+assign out = |in;
+
+endmodule
+
+module OR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out);
+
+assign out = |in;
+
+endmodule
+
+
+module NAND2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out);
+
+assign out = ~&in;
+
+endmodule
+
+module NAND3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out);
+
+assign out = ~&in;
+
+endmodule
+
+module NAND4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out);
+
+assign out = ~&in;
+
+endmodule
+
+module NOR2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out);
+
+assign out = ~|in;
+
+endmodule
+
+module NOR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out);
+
+assign out = ~|in;
+
+endmodule
+
+module NOR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out);
+
+assign out = ~|in;
+
+endmodule
+
+
+module XOR2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out);
+
+assign out = ^in;
+
+endmodule
+
+module XOR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out);
+
+assign out = ^in;
+
+endmodule
+
+module XOR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out);
+
+assign out = ^in;
+
+endmodule
+
+
+module XNOR2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out);
+
+assign out = ~^in;
+
+endmodule
+
+module XNOR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out);
+
+assign out = ~^in;
+
+endmodule
+
+module XNOR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out);
+
+assign out = ~^in;
+
+endmodule
+
+module DEC1 (input in, enable, output reg [1:0] out);
+
+always @(in or enable)
+ if(!enable)
+ out = 2'b00;
+ else begin
+ case (in)
+ 1'b0 : out = 2'b01;
+ 1'b1 : out = 2'b10;
+ endcase
+ end
+endmodule
+
+module DEC2 (input [1:0] in, input enable, output reg [3:0] out);
+
+always @(in or enable)
+ if(!enable)
+ out = 4'b0000;
+ else begin
+ case (in)
+ 2'b00 : out = 4'b0001;
+ 2'b01 : out = 4'b0010;
+ 2'b10 : out = 4'b0100;
+ 2'b11 : out = 4'b1000;
+ endcase
+ end
+endmodule
+
+module DEC3 (input [2:0] in, input enable, output reg [7:0] out);
+
+always @(in or enable)
+ if(!enable)
+ out = 8'b00000000;
+ else begin
+ case (in)
+ 3'b000 : out = 8'b00000001;
+ 3'b001 : out = 8'b00000010;
+ 3'b010 : out = 8'b00000100;
+ 3'b011 : out = 8'b00001000;
+ 3'b100 : out = 8'b00010000;
+ 3'b101 : out = 8'b00100000;
+ 3'b110 : out = 8'b01000000;
+ 3'b111 : out = 8'b10000000;
+ endcase
+ end
+endmodule
+
+module DEC4 (input [3:0] in, input enable, output reg [15:0] out);
+
+always @(in or enable)
+ if(!enable)
+ out = 16'b0000000000000000;
+ else begin
+ case (in)
+ 4'b0000 : out = 16'b0000000000000001;
+ 4'b0001 : out = 16'b0000000000000010;
+ 4'b0010 : out = 16'b0000000000000100;
+ 4'b0011 : out = 16'b0000000000001000;
+ 4'b0100 : out = 16'b0000000000010000;
+ 4'b0101 : out = 16'b0000000000100000;
+ 4'b0110 : out = 16'b0000000001000000;
+ 4'b0111 : out = 16'b0000000010000000;
+ 4'b1000 : out = 16'b0000000100000000;
+ 4'b1001 : out = 16'b0000001000000000;
+ 4'b1010 : out = 16'b0000010000000000;
+ 4'b1011 : out = 16'b0000100000000000;
+ 4'b1100 : out = 16'b0001000000000000;
+ 4'b1101 : out = 16'b0010000000000000;
+ 4'b1110 : out = 16'b0100000000000000;
+ 4'b1111 : out = 16'b1000000000000000;
+ endcase
+ end
+endmodule
+module DEC5 (input [4:0] in, input enable, output reg [31:0] out);
+
+always @(in or enable)
+ if(!enable)
+ out = 32'b00000000000000000000000000000000;
+ else begin
+ case (in)
+ 5'b00000 : out = 32'b00000000000000000000000000000001;
+ 5'b00001 : out = 32'b00000000000000000000000000000010;
+ 5'b00010 : out = 32'b00000000000000000000000000000100;
+ 5'b00011 : out = 32'b00000000000000000000000000001000;
+ 5'b00100 : out = 32'b00000000000000000000000000010000;
+ 5'b00101 : out = 32'b00000000000000000000000000100000;
+ 5'b00110 : out = 32'b00000000000000000000000001000000;
+ 5'b00111 : out = 32'b00000000000000000000000010000000;
+ 5'b01000 : out = 32'b00000000000000000000000100000000;
+ 5'b01001 : out = 32'b00000000000000000000001000000000;
+ 5'b01010 : out = 32'b00000000000000000000010000000000;
+ 5'b01011 : out = 32'b00000000000000000000100000000000;
+ 5'b01100 : out = 32'b00000000000000000001000000000000;
+ 5'b01101 : out = 32'b00000000000000000010000000000000;
+ 5'b01110 : out = 32'b00000000000000000100000000000000;
+ 5'b01111 : out = 32'b00000000000000001000000000000000;
+ 5'b10000 : out = 32'b00000000000000010000000000000000;
+ 5'b10001 : out = 32'b00000000000000100000000000000000;
+ 5'b10010 : out = 32'b00000000000001000000000000000000;
+ 5'b10011 : out = 32'b00000000000010000000000000000000;
+ 5'b10100 : out = 32'b00000000000100000000000000000000;
+ 5'b10101 : out = 32'b00000000001000000000000000000000;
+ 5'b10110 : out = 32'b00000000010000000000000000000000;
+ 5'b10111 : out = 32'b00000000100000000000000000000000;
+ 5'b11000 : out = 32'b00000001000000000000000000000000;
+ 5'b11001 : out = 32'b00000010000000000000000000000000;
+ 5'b11010 : out = 32'b00000100000000000000000000000000;
+ 5'b11011 : out = 32'b00001000000000000000000000000000;
+ 5'b11100 : out = 32'b00010000000000000000000000000000;
+ 5'b11101 : out = 32'b00100000000000000000000000000000;
+ 5'b11110 : out = 32'b01000000000000000000000000000000;
+ 5'b11111 : out = 32'b10000000000000000000000000000000;
+ endcase
+ end
+endmodule
+
+module DEC6 (input [5:0] in, input enable, output reg [63:0] out);
+
+always @(in or enable)
+ if(!enable)
+ out = 64'b0000000000000000000000000000000000000000000000000000000000000000;
+ else begin
+ case (in)
+ 6'b000000 : out = 64'b0000000000000000000000000000000000000000000000000000000000000001;
+ 6'b000001 : out = 64'b0000000000000000000000000000000000000000000000000000000000000010;
+ 6'b000010 : out = 64'b0000000000000000000000000000000000000000000000000000000000000100;
+ 6'b000011 : out = 64'b0000000000000000000000000000000000000000000000000000000000001000;
+ 6'b000100 : out = 64'b0000000000000000000000000000000000000000000000000000000000010000;
+ 6'b000101 : out = 64'b0000000000000000000000000000000000000000000000000000000000100000;
+ 6'b000110 : out = 64'b0000000000000000000000000000000000000000000000000000000001000000;
+ 6'b000111 : out = 64'b0000000000000000000000000000000000000000000000000000000010000000;
+ 6'b001000 : out = 64'b0000000000000000000000000000000000000000000000000000000100000000;
+ 6'b001001 : out = 64'b0000000000000000000000000000000000000000000000000000001000000000;
+ 6'b001010 : out = 64'b0000000000000000000000000000000000000000000000000000010000000000;
+ 6'b001011 : out = 64'b0000000000000000000000000000000000000000000000000000100000000000;
+ 6'b001100 : out = 64'b0000000000000000000000000000000000000000000000000001000000000000;
+ 6'b001101 : out = 64'b0000000000000000000000000000000000000000000000000010000000000000;
+ 6'b001110 : out = 64'b0000000000000000000000000000000000000000000000000100000000000000;
+ 6'b001111 : out = 64'b0000000000000000000000000000000000000000000000001000000000000000;
+ 6'b010000 : out = 64'b0000000000000000000000000000000000000000000000010000000000000000;
+ 6'b010001 : out = 64'b0000000000000000000000000000000000000000000000100000000000000000;
+ 6'b010010 : out = 64'b0000000000000000000000000000000000000000000001000000000000000000;
+ 6'b010011 : out = 64'b0000000000000000000000000000000000000000000010000000000000000000;
+ 6'b010100 : out = 64'b0000000000000000000000000000000000000000000100000000000000000000;
+ 6'b010101 : out = 64'b0000000000000000000000000000000000000000001000000000000000000000;
+ 6'b010110 : out = 64'b0000000000000000000000000000000000000000010000000000000000000000;
+ 6'b010111 : out = 64'b0000000000000000000000000000000000000000100000000000000000000000;
+ 6'b011000 : out = 64'b0000000000000000000000000000000000000001000000000000000000000000;
+ 6'b011001 : out = 64'b0000000000000000000000000000000000000010000000000000000000000000;
+ 6'b011010 : out = 64'b0000000000000000000000000000000000000100000000000000000000000000;
+ 6'b011011 : out = 64'b0000000000000000000000000000000000001000000000000000000000000000;
+ 6'b011100 : out = 64'b0000000000000000000000000000000000010000000000000000000000000000;
+ 6'b011101 : out = 64'b0000000000000000000000000000000000100000000000000000000000000000;
+ 6'b011110 : out = 64'b0000000000000000000000000000000001000000000000000000000000000000;
+ 6'b011111 : out = 64'b0000000000000000000000000000000010000000000000000000000000000000;
+
+ 6'b100000 : out = 64'b0000000000000000000000000000000100000000000000000000000000000000;
+ 6'b100001 : out = 64'b0000000000000000000000000000001000000000000000000000000000000000;
+ 6'b100010 : out = 64'b0000000000000000000000000000010000000000000000000000000000000000;
+ 6'b100011 : out = 64'b0000000000000000000000000000100000000000000000000000000000000000;
+ 6'b100100 : out = 64'b0000000000000000000000000001000000000000000000000000000000000000;
+ 6'b100101 : out = 64'b0000000000000000000000000010000000000000000000000000000000000000;
+ 6'b100110 : out = 64'b0000000000000000000000000100000000000000000000000000000000000000;
+ 6'b100111 : out = 64'b0000000000000000000000001000000000000000000000000000000000000000;
+ 6'b101000 : out = 64'b0000000000000000000000010000000000000000000000000000000000000000;
+ 6'b101001 : out = 64'b0000000000000000000000100000000000000000000000000000000000000000;
+ 6'b101010 : out = 64'b0000000000000000000001000000000000000000000000000000000000000000;
+ 6'b101011 : out = 64'b0000000000000000000010000000000000000000000000000000000000000000;
+ 6'b101100 : out = 64'b0000000000000000000100000000000000000000000000000000000000000000;
+ 6'b101101 : out = 64'b0000000000000000001000000000000000000000000000000000000000000000;
+ 6'b101110 : out = 64'b0000000000000000010000000000000000000000000000000000000000000000;
+ 6'b101111 : out = 64'b0000000000000000100000000000000000000000000000000000000000000000;
+ 6'b110000 : out = 64'b0000000000000001000000000000000000000000000000000000000000000000;
+ 6'b110001 : out = 64'b0000000000000010000000000000000000000000000000000000000000000000;
+ 6'b110010 : out = 64'b0000000000000100000000000000000000000000000000000000000000000000;
+ 6'b110011 : out = 64'b0000000000001000000000000000000000000000000000000000000000000000;
+ 6'b110100 : out = 64'b0000000000010000000000000000000000000000000000000000000000000000;
+ 6'b110101 : out = 64'b0000000000100000000000000000000000000000000000000000000000000000;
+ 6'b110110 : out = 64'b0000000001000000000000000000000000000000000000000000000000000000;
+ 6'b110111 : out = 64'b0000000010000000000000000000000000000000000000000000000000000000;
+ 6'b111000 : out = 64'b0000000100000000000000000000000000000000000000000000000000000000;
+ 6'b111001 : out = 64'b0000001000000000000000000000000000000000000000000000000000000000;
+ 6'b111010 : out = 64'b0000010000000000000000000000000000000000000000000000000000000000;
+ 6'b111011 : out = 64'b0000100000000000000000000000000000000000000000000000000000000000;
+ 6'b111100 : out = 64'b0001000000000000000000000000000000000000000000000000000000000000;
+ 6'b111101 : out = 64'b0010000000000000000000000000000000000000000000000000000000000000;
+ 6'b111110 : out = 64'b0100000000000000000000000000000000000000000000000000000000000000;
+ 6'b111111 : out = 64'b1000000000000000000000000000000000000000000000000000000000000000;
+ endcase
+ end
+endmodule
+
+
+module MUX2(input [1:0] in, input select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ endcase
+endmodule
+
+
+module MUX4(input [3:0] in, input [1:0] select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ 2: out = in[2];
+ 3: out = in[3];
+ endcase
+endmodule
+
+
+module MUX8(input [7:0] in, input [2:0] select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ 2: out = in[2];
+ 3: out = in[3];
+ 4: out = in[4];
+ 5: out = in[5];
+ 6: out = in[6];
+ 7: out = in[7];
+ endcase
+endmodule
+
+module MUX16(input [15:0] in, input [3:0] select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ 2: out = in[2];
+ 3: out = in[3];
+ 4: out = in[4];
+ 5: out = in[5];
+ 6: out = in[6];
+ 7: out = in[7];
+ 8: out = in[8];
+ 9: out = in[9];
+ 10: out = in[10];
+ 11: out = in[11];
+ 12: out = in[12];
+ 13: out = in[13];
+ 14: out = in[14];
+ 15: out = in[15];
+ endcase
+endmodule
+
+module MUX32(input [31:0] in, input [4:0] select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ 2: out = in[2];
+ 3: out = in[3];
+ 4: out = in[4];
+ 5: out = in[5];
+ 6: out = in[6];
+ 7: out = in[7];
+ 8: out = in[8];
+ 9: out = in[9];
+ 10: out = in[10];
+ 11: out = in[11];
+ 12: out = in[12];
+ 13: out = in[13];
+ 14: out = in[14];
+ 15: out = in[15];
+ 16: out = in[16];
+ 17: out = in[17];
+ 18: out = in[18];
+ 19: out = in[19];
+ 20: out = in[20];
+ 21: out = in[21];
+ 22: out = in[22];
+ 23: out = in[23];
+ 24: out = in[24];
+ 25: out = in[25];
+ 26: out = in[26];
+ 27: out = in[27];
+ 28: out = in[28];
+ 29: out = in[29];
+ 30: out = in[30];
+ 31: out = in[31];
+ endcase
+endmodule
+
+module MUX64(input [63:0] in, input [5:0] select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ 2: out = in[2];
+ 3: out = in[3];
+ 4: out = in[4];
+ 5: out = in[5];
+ 6: out = in[6];
+ 7: out = in[7];
+ 8: out = in[8];
+ 9: out = in[9];
+ 10: out = in[10];
+ 11: out = in[11];
+ 12: out = in[12];
+ 13: out = in[13];
+ 14: out = in[14];
+ 15: out = in[15];
+ 16: out = in[16];
+ 17: out = in[17];
+ 18: out = in[18];
+ 19: out = in[19];
+ 20: out = in[20];
+ 21: out = in[21];
+ 22: out = in[22];
+ 23: out = in[23];
+ 24: out = in[24];
+ 25: out = in[25];
+ 26: out = in[26];
+ 27: out = in[27];
+ 28: out = in[28];
+ 29: out = in[29];
+ 30: out = in[30];
+ 31: out = in[31];
+ 32: out = in[32];
+ 33: out = in[33];
+ 34: out = in[34];
+ 35: out = in[35];
+ 36: out = in[36];
+ 37: out = in[37];
+ 38: out = in[38];
+ 39: out = in[39];
+ 40: out = in[40];
+ 41: out = in[41];
+ 42: out = in[42];
+ 43: out = in[43];
+ 44: out = in[44];
+ 45: out = in[45];
+ 46: out = in[46];
+ 47: out = in[47];
+ 48: out = in[48];
+ 49: out = in[49];
+ 50: out = in[50];
+ 51: out = in[51];
+ 52: out = in[52];
+ 53: out = in[53];
+ 54: out = in[54];
+ 55: out = in[55];
+ 56: out = in[56];
+ 57: out = in[57];
+ 58: out = in[58];
+ 59: out = in[59];
+ 60: out = in[60];
+ 61: out = in[61];
+ 62: out = in[62];
+ 63: out = in[63];
+ endcase
+endmodule
+
+module ADD1(input in1, in2, cin, output out, cout);
+
+assign {cout, out} = in1 + in2 + cin;
+
+endmodule
+
+module ADD2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 + in2 + cin;
+
+endmodule
+
+module ADD4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 + in2 + cin;
+
+endmodule
+
+module ADD8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 + in2 + cin;
+
+endmodule
+
+module ADD16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 + in2 + cin;
+
+endmodule
+
+module ADD32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 + in2 + cin;
+
+endmodule
+module ADD64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 + in2 + cin;
+
+endmodule
+
+module SUB1(input in1, in2, cin, output out, cout);
+
+assign {cout, out} = in1 - in2 - cin;
+
+endmodule
+
+module SUB2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 - in2 - cin;
+
+endmodule
+
+module SUB4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 - in2 - cin;
+
+endmodule
+
+module SUB8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 - in2 - cin;
+
+endmodule
+
+module SUB16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 - in2 - cin;
+
+endmodule
+
+module SUB32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 - in2 - cin;
+
+endmodule
+module SUB64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 - in2 - cin;
+
+endmodule
+
+module MUL1 #(parameter SIZE = 1)(input in1, in2, output [2*SIZE-1:0] out);
+
+assign out = in1*in2;
+
+endmodule
+
+module MUL2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out);
+
+assign out = in1*in2;
+
+endmodule
+
+module MUL4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out);
+
+assign out = in1*in2;
+
+endmodule
+
+module MUL8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out);
+
+assign out = in1*in2;
+
+endmodule
+
+module MUL16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out);
+
+assign out = in1*in2;
+
+endmodule
+
+module MUL32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out);
+
+assign out = in1*in2;
+
+endmodule
+
+module MUL64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out);
+
+assign out = in1*in2;
+
+endmodule
+
+module DIV1 #(parameter SIZE = 1)(input in1, in2, output out, rem);
+
+assign out = in1/in2;
+assign rem = in1%in2;
+
+endmodule
+
+module DIV2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2,
+ output [SIZE-1:0] out, rem);
+
+assign out = in1/in2;
+assign rem = in1%in2;
+
+endmodule
+
+module DIV4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2,
+ output [SIZE-1:0] out, rem);
+
+assign out = in1/in2;
+assign rem = in1%in2;
+
+endmodule
+
+module DIV8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2,
+ output [SIZE-1:0] out, rem);
+
+assign out = in1/in2;
+assign rem = in1%in2;
+
+endmodule
+
+module DIV16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2,
+ output [SIZE-1:0] out, rem);
+
+assign out = in1/in2;
+assign rem = in1%in2;
+
+endmodule
+
+module DIV32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2,
+ output [SIZE-1:0] out, rem);
+
+assign out = in1/in2;
+assign rem = in1%in2;
+
+endmodule
+
+module DIV64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2,
+ output [SIZE-1:0] out, rem);
+
+assign out = in1/in2;
+assign rem = in1%in2;
+
+endmodule
+
+module FF (input d, clk, output reg q);
+always @( posedge clk)
+ q <= d;
+endmodule
+
+
+module RFF(input d, clk, reset, output reg q);
+always @(posedge clk or posedge reset)
+ if(reset)
+ q <= 0;
+ else
+ q <= d;
+endmodule
+
+module SFF(input d, clk, set, output reg q);
+always @(posedge clk or posedge set)
+ if(set)
+ q <= 1;
+ else
+ q <= d;
+endmodule
+
+module RSFF(input d, clk, set, reset, output reg q);
+always @(posedge clk or posedge reset or posedge set)
+ if(reset)
+ q <= 0;
+ else if(set)
+ q <= 1;
+ else
+ q <= d;
+endmodule
+
+module SRFF(input d, clk, set, reset, output reg q);
+always @(posedge clk or posedge set or posedge reset)
+ if(set)
+ q <= 1;
+ else if(reset)
+ q <= 0;
+ else
+ q <= d;
+endmodule
+
+module LATCH(input d, enable, output reg q);
+always @( d or enable)
+ if(enable)
+ q <= d;
+endmodule
+
+module RLATCH(input d, reset, enable, output reg q);
+always @( d or enable or reset)
+ if(enable)
+ if(reset)
+ q <= 0;
+ else
+ q <= d;
+endmodule
+
+module LSHIFT1 #(parameter SIZE = 1)(input in, shift, val, output reg out);
+
+always @ (in, shift, val) begin
+ if(shift)
+ out = val;
+ else
+ out = in;
+end
+
+endmodule
+
+
+module LSHIFT2 #(parameter SIZE = 2)(input [SIZE-1:0] in,
+ input [SIZE-1:0] shift, input val,
+ output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in << shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift));
+end
+endmodule
+
+module LSHIFT4 #(parameter SIZE = 4)(input [SIZE-1:0] in,
+ input [2:0] shift, input val, output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in << shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift));
+end
+endmodule
+
+
+module LSHIFT8 #(parameter SIZE = 8)(input [SIZE-1:0] in,
+ input [3:0] shift, input val, output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in << shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift));
+end
+endmodule
+
+module LSHIFT16 #(parameter SIZE = 16)(input [SIZE-1:0] in,
+ input [4:0] shift, input val, output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in << shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift));
+end
+endmodule
+
+module LSHIFT32 #(parameter SIZE = 32)(input [SIZE-1:0] in,
+ input [5:0] shift, input val, output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in << shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift));
+end
+endmodule
+
+module LSHIFT64 #(parameter SIZE = 64)(input [SIZE-1:0] in,
+ input [6:0] shift, input val, output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in << shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift));
+end
+endmodule
+
+module RSHIFT1 #(parameter SIZE = 1)(input in, shift, val, output reg out);
+
+always @ (in, shift, val) begin
+ if(shift)
+ out = val;
+ else
+ out = in;
+end
+
+endmodule
+
+module RSHIFT2 #(parameter SIZE = 2)(input [SIZE-1:0] in,
+ input [SIZE-1:0] shift, input val,
+ output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in >> shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift));
+end
+
+endmodule
+
+
+module RSHIFT4 #(parameter SIZE = 4)(input [SIZE-1:0] in,
+ input [2:0] shift, input val,
+ output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in >> shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift));
+end
+endmodule
+
+module RSHIFT8 #(parameter SIZE = 8)(input [SIZE-1:0] in,
+ input [3:0] shift, input val,
+ output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in >> shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift));
+end
+
+endmodule
+
+module RSHIFT16 #(parameter SIZE = 16)(input [SIZE-1:0] in,
+ input [4:0] shift, input val,
+ output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in >> shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift));
+end
+endmodule
+
+
+module RSHIFT32 #(parameter SIZE = 32)(input [SIZE-1:0] in,
+ input [5:0] shift, input val,
+ output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in >> shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift));
+end
+endmodule
+
+module RSHIFT64 #(parameter SIZE = 64)(input [SIZE-1:0] in,
+ input [6:0] shift, input val,
+ output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in >> shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift));
+end
+endmodule
+
+module CMP1 #(parameter SIZE = 1) (input in1, in2,
+ output reg equal, unequal, greater, lesser);
+
+always @ (in1 or in2) begin
+ if(in1 == in2) begin
+ equal = 1;
+ unequal = 0;
+ greater = 0;
+ lesser = 0;
+ end
+ else begin
+ equal = 0;
+ unequal = 1;
+
+ if(in1 < in2) begin
+ greater = 0;
+ lesser = 1;
+ end
+ else begin
+ greater = 1;
+ lesser = 0;
+ end
+ end
+end
+endmodule
+
+
+module CMP2 #(parameter SIZE = 2) (input [SIZE-1:0] in1, in2,
+ output reg equal, unequal, greater, lesser);
+
+always @ (in1 or in2) begin
+ if(in1 == in2) begin
+ equal = 1;
+ unequal = 0;
+ greater = 0;
+ lesser = 0;
+ end
+ else begin
+ equal = 0;
+ unequal = 1;
+
+ if(in1 < in2) begin
+ greater = 0;
+ lesser = 1;
+ end
+ else begin
+ greater = 1;
+ lesser = 0;
+ end
+ end
+end
+endmodule
+
+module CMP4 #(parameter SIZE = 4) (input [SIZE-1:0] in1, in2,
+ output reg equal, unequal, greater, lesser);
+
+always @ (in1 or in2) begin
+ if(in1 == in2) begin
+ equal = 1;
+ unequal = 0;
+ greater = 0;
+ lesser = 0;
+ end
+ else begin
+ equal = 0;
+ unequal = 1;
+
+ if(in1 < in2) begin
+ greater = 0;
+ lesser = 1;
+ end
+ else begin
+ greater = 1;
+ lesser = 0;
+ end
+ end
+end
+endmodule
+
+module CMP8 #(parameter SIZE = 8) (input [SIZE-1:0] in1, in2,
+ output reg equal, unequal, greater, lesser);
+
+always @ (in1 or in2) begin
+ if(in1 == in2) begin
+ equal = 1;
+ unequal = 0;
+ greater = 0;
+ lesser = 0;
+ end
+ else begin
+ equal = 0;
+ unequal = 1;
+
+ if(in1 < in2) begin
+ greater = 0;
+ lesser = 1;
+ end
+ else begin
+ greater = 1;
+ lesser = 0;
+ end
+ end
+end
+endmodule
+
+module CMP16 #(parameter SIZE = 16) (input [SIZE-1:0] in1, in2,
+ output reg equal, unequal, greater, lesser);
+
+always @ (in1 or in2) begin
+ if(in1 == in2) begin
+ equal = 1;
+ unequal = 0;
+ greater = 0;
+ lesser = 0;
+ end
+ else begin
+ equal = 0;
+ unequal = 1;
+
+ if(in1 < in2) begin
+ greater = 0;
+ lesser = 1;
+ end
+ else begin
+ greater = 1;
+ lesser = 0;
+ end
+ end
+end
+endmodule
+
+module CMP32 #(parameter SIZE = 32) (input [SIZE-1:0] in1, in2,
+ output reg equal, unequal, greater, lesser);
+
+always @ (in1 or in2) begin
+ if(in1 == in2) begin
+ equal = 1;
+ unequal = 0;
+ greater = 0;
+ lesser = 0;
+ end
+ else begin
+ equal = 0;
+ unequal = 1;
+
+ if(in1 < in2) begin
+ greater = 0;
+ lesser = 1;
+ end
+ else begin
+ greater = 1;
+ lesser = 0;
+ end
+ end
+end
+endmodule
+
+module CMP64 #(parameter SIZE = 64) (input [SIZE-1:0] in1, in2,
+ output reg equal, unequal, greater, lesser);
+
+always @ (in1 or in2) begin
+ if(in1 == in2) begin
+ equal = 1;
+ unequal = 0;
+ greater = 0;
+ lesser = 0;
+ end
+ else begin
+ equal = 0;
+ unequal = 1;
+
+ if(in1 < in2) begin
+ greater = 0;
+ lesser = 1;
+ end
+ else begin
+ greater = 1;
+ lesser = 0;
+ end
+ end
+end
+endmodule
+
+module VCC (output supply1 out);
+endmodule
+
+module GND (output supply0 out);
+endmodule
+
+
+module INC1 #(parameter SIZE = 1) (input in, output [SIZE:0] out);
+
+assign out = in + 1;
+
+endmodule
+
+module INC2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output [SIZE:0] out);
+
+assign out = in + 1;
+
+endmodule
+
+module INC4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output [SIZE:0] out);
+assign out = in + 1;
+
+endmodule
+
+module INC8 #(parameter SIZE = 8) (input [SIZE-1:0] in, output [SIZE:0] out);
+assign out = in + 1;
+
+endmodule
+
+module INC16 #(parameter SIZE = 16) (input [SIZE-1:0] in, output [SIZE:0] out);
+assign out = in + 1;
+
+endmodule
+
+module INC32 #(parameter SIZE = 32) (input [SIZE-1:0] in, output [SIZE:0] out);
+assign out = in + 1;
+
+endmodule
+module INC64 #(parameter SIZE = 64) (input [SIZE-1:0] in, output [SIZE:0] out);
+assign out = in + 1;
+
+endmodule
+
diff --git a/manual/CHAPTER_StateOfTheArt/simlib_icarus.v b/manual/CHAPTER_StateOfTheArt/simlib_icarus.v
new file mode 100644
index 00000000..fdd7ef61
--- /dev/null
+++ b/manual/CHAPTER_StateOfTheArt/simlib_icarus.v
@@ -0,0 +1,224 @@
+
+module cell0(Result0);
+output Result0;
+assign Result0 = 0;
+endmodule
+
+module cell1(Result0);
+output Result0;
+assign Result0 = 1;
+endmodule
+
+module ADD4(
+ DataA0, DataA1, DataA2, DataA3,
+ DataB0, DataB1, DataB2, DataB3,
+ Result0, Result1, Result2, Result3, Cout
+);
+input DataA0, DataA1, DataA2, DataA3;
+input DataB0, DataB1, DataB2, DataB3;
+output Result0, Result1, Result2, Result3, Cout;
+assign {Cout, Result3, Result2, Result1, Result0} = {DataA3, DataA2, DataA1, DataA0} + {DataB3, DataB2, DataB1, DataB0};
+endmodule
+
+module BUF(DATA, RESULT);
+input DATA;
+output RESULT;
+assign RESULT = DATA;
+endmodule
+
+module INV(DATA, RESULT);
+input DATA;
+output RESULT;
+assign RESULT = ~DATA;
+endmodule
+
+module fd4(
+ Clock,
+ Data0, Data1, Data2, Data3,
+ Q0, Q1, Q2, Q3
+);
+input Clock;
+input Data0, Data1, Data2, Data3;
+output reg Q0, Q1, Q2, Q3;
+always @(posedge Clock)
+ {Q0, Q1, Q2, Q3} <= {Data0, Data1, Data2, Data3};
+endmodule
+
+module fdce1(
+ Clock, Enable,
+ Data0,
+ Q0
+);
+input Clock, Enable;
+input Data0;
+output reg Q0;
+always @(posedge Clock)
+ if (Enable)
+ Q0 <= Data0;
+endmodule
+
+module fdce4(
+ Clock, Enable,
+ Data0, Data1, Data2, Data3,
+ Q0, Q1, Q2, Q3
+);
+input Clock, Enable;
+input Data0, Data1, Data2, Data3;
+output reg Q0, Q1, Q2, Q3;
+always @(posedge Clock)
+ if (Enable)
+ {Q0, Q1, Q2, Q3} <= {Data0, Data1, Data2, Data3};
+endmodule
+
+module mux4_1_2(
+ Sel0,
+ Data0x0, Data0x1, Data0x2, Data0x3,
+ Data1x0, Data1x1, Data1x2, Data1x3,
+ Result0, Result1, Result2, Result3
+);
+input Sel0;
+input Data0x0, Data0x1, Data0x2, Data0x3;
+input Data1x0, Data1x1, Data1x2, Data1x3;
+output Result0, Result1, Result2, Result3;
+assign {Result0, Result1, Result2, Result3} = Sel0 ? {Data1x0, Data1x1, Data1x2, Data1x3} : {Data0x0, Data0x1, Data0x2, Data0x3};
+endmodule
+
+module mux1_1_2(
+ Sel0,
+ Data0x0,
+ Data1x0,
+ Result0
+);
+input Sel0;
+input Data0x0;
+input Data1x0;
+output Result0;
+assign Result0 = Sel0 ? Data1x0 : Data0x0;
+endmodule
+
+module xor2(
+ DATA0X0,
+ DATA1X0,
+ RESULT0
+);
+input DATA0X0;
+input DATA1X0;
+output RESULT0;
+assign RESULT0 = DATA1X0 ^ DATA0X0;
+endmodule
+
+module fdce64(
+ Clock, Enable,
+ Data0, Data1, Data2, Data3, Data4, Data5, Data6, Data7, Data8, Data9, Data10, Data11, Data12, Data13, Data14, Data15, Data16, Data17, Data18, Data19, Data20, Data21, Data22, Data23, Data24, Data25, Data26, Data27, Data28, Data29, Data30, Data31, Data32, Data33, Data34, Data35, Data36, Data37, Data38, Data39, Data40, Data41, Data42, Data43, Data44, Data45, Data46, Data47, Data48, Data49, Data50, Data51, Data52, Data53, Data54, Data55, Data56, Data57, Data58, Data59, Data60, Data61, Data62, Data63,
+ Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15, Q16, Q17, Q18, Q19, Q20, Q21, Q22, Q23, Q24, Q25, Q26, Q27, Q28, Q29, Q30, Q31, Q32, Q33, Q34, Q35, Q36, Q37, Q38, Q39, Q40, Q41, Q42, Q43, Q44, Q45, Q46, Q47, Q48, Q49, Q50, Q51, Q52, Q53, Q54, Q55, Q56, Q57, Q58, Q59, Q60, Q61, Q62, Q63
+);
+input Clock, Enable;
+input Data0, Data1, Data2, Data3, Data4, Data5, Data6, Data7, Data8, Data9, Data10, Data11, Data12, Data13, Data14, Data15, Data16, Data17, Data18, Data19, Data20, Data21, Data22, Data23, Data24, Data25, Data26, Data27, Data28, Data29, Data30, Data31, Data32, Data33, Data34, Data35, Data36, Data37, Data38, Data39, Data40, Data41, Data42, Data43, Data44, Data45, Data46, Data47, Data48, Data49, Data50, Data51, Data52, Data53, Data54, Data55, Data56, Data57, Data58, Data59, Data60, Data61, Data62, Data63;
+output reg Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15, Q16, Q17, Q18, Q19, Q20, Q21, Q22, Q23, Q24, Q25, Q26, Q27, Q28, Q29, Q30, Q31, Q32, Q33, Q34, Q35, Q36, Q37, Q38, Q39, Q40, Q41, Q42, Q43, Q44, Q45, Q46, Q47, Q48, Q49, Q50, Q51, Q52, Q53, Q54, Q55, Q56, Q57, Q58, Q59, Q60, Q61, Q62, Q63;
+always @(posedge Clock)
+ if (Enable)
+ { Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15, Q16, Q17, Q18, Q19, Q20, Q21, Q22, Q23, Q24, Q25, Q26, Q27, Q28, Q29, Q30, Q31, Q32, Q33, Q34, Q35, Q36, Q37, Q38, Q39, Q40, Q41, Q42, Q43, Q44, Q45, Q46, Q47, Q48, Q49, Q50, Q51, Q52, Q53, Q54, Q55, Q56, Q57, Q58, Q59, Q60, Q61, Q62, Q63 } <= { Data0, Data1, Data2, Data3, Data4, Data5, Data6, Data7, Data8, Data9, Data10, Data11, Data12, Data13, Data14, Data15, Data16, Data17, Data18, Data19, Data20, Data21, Data22, Data23, Data24, Data25, Data26, Data27, Data28, Data29, Data30, Data31, Data32, Data33, Data34, Data35, Data36, Data37, Data38, Data39, Data40, Data41, Data42, Data43, Data44, Data45, Data46, Data47, Data48, Data49, Data50, Data51, Data52, Data53, Data54, Data55, Data56, Data57, Data58, Data59, Data60, Data61, Data62, Data63 };
+endmodule
+
+module mux4_4_16(
+ Sel0, Sel1, Sel2, Sel3,
+ Result0, Result1, Result2, Result3,
+ Data0x0, Data0x1, Data0x2, Data0x3,
+ Data1x0, Data1x1, Data1x2, Data1x3,
+ Data2x0, Data2x1, Data2x2, Data2x3,
+ Data3x0, Data3x1, Data3x2, Data3x3,
+ Data4x0, Data4x1, Data4x2, Data4x3,
+ Data5x0, Data5x1, Data5x2, Data5x3,
+ Data6x0, Data6x1, Data6x2, Data6x3,
+ Data7x0, Data7x1, Data7x2, Data7x3,
+ Data8x0, Data8x1, Data8x2, Data8x3,
+ Data9x0, Data9x1, Data9x2, Data9x3,
+ Data10x0, Data10x1, Data10x2, Data10x3,
+ Data11x0, Data11x1, Data11x2, Data11x3,
+ Data12x0, Data12x1, Data12x2, Data12x3,
+ Data13x0, Data13x1, Data13x2, Data13x3,
+ Data14x0, Data14x1, Data14x2, Data14x3,
+ Data15x0, Data15x1, Data15x2, Data15x3
+);
+input Sel0, Sel1, Sel2, Sel3;
+output Result0, Result1, Result2, Result3;
+input Data0x0, Data0x1, Data0x2, Data0x3;
+input Data1x0, Data1x1, Data1x2, Data1x3;
+input Data2x0, Data2x1, Data2x2, Data2x3;
+input Data3x0, Data3x1, Data3x2, Data3x3;
+input Data4x0, Data4x1, Data4x2, Data4x3;
+input Data5x0, Data5x1, Data5x2, Data5x3;
+input Data6x0, Data6x1, Data6x2, Data6x3;
+input Data7x0, Data7x1, Data7x2, Data7x3;
+input Data8x0, Data8x1, Data8x2, Data8x3;
+input Data9x0, Data9x1, Data9x2, Data9x3;
+input Data10x0, Data10x1, Data10x2, Data10x3;
+input Data11x0, Data11x1, Data11x2, Data11x3;
+input Data12x0, Data12x1, Data12x2, Data12x3;
+input Data13x0, Data13x1, Data13x2, Data13x3;
+input Data14x0, Data14x1, Data14x2, Data14x3;
+input Data15x0, Data15x1, Data15x2, Data15x3;
+assign {Result0, Result1, Result2, Result3} =
+ {Sel3, Sel2, Sel1, Sel0} == 0 ? { Data0x0, Data0x1, Data0x2, Data0x3 } :
+ {Sel3, Sel2, Sel1, Sel0} == 1 ? { Data1x0, Data1x1, Data1x2, Data1x3 } :
+ {Sel3, Sel2, Sel1, Sel0} == 2 ? { Data2x0, Data2x1, Data2x2, Data2x3 } :
+ {Sel3, Sel2, Sel1, Sel0} == 3 ? { Data3x0, Data3x1, Data3x2, Data3x3 } :
+ {Sel3, Sel2, Sel1, Sel0} == 4 ? { Data4x0, Data4x1, Data4x2, Data4x3 } :
+ {Sel3, Sel2, Sel1, Sel0} == 5 ? { Data5x0, Data5x1, Data5x2, Data5x3 } :
+ {Sel3, Sel2, Sel1, Sel0} == 6 ? { Data6x0, Data6x1, Data6x2, Data6x3 } :
+ {Sel3, Sel2, Sel1, Sel0} == 7 ? { Data7x0, Data7x1, Data7x2, Data7x3 } :
+ {Sel3, Sel2, Sel1, Sel0} == 8 ? { Data8x0, Data8x1, Data8x2, Data8x3 } :
+ {Sel3, Sel2, Sel1, Sel0} == 9 ? { Data9x0, Data9x1, Data9x2, Data9x3 } :
+ {Sel3, Sel2, Sel1, Sel0} == 10 ? { Data10x0, Data10x1, Data10x2, Data10x3 } :
+ {Sel3, Sel2, Sel1, Sel0} == 11 ? { Data11x0, Data11x1, Data11x2, Data11x3 } :
+ {Sel3, Sel2, Sel1, Sel0} == 12 ? { Data12x0, Data12x1, Data12x2, Data12x3 } :
+ {Sel3, Sel2, Sel1, Sel0} == 13 ? { Data13x0, Data13x1, Data13x2, Data13x3 } :
+ {Sel3, Sel2, Sel1, Sel0} == 14 ? { Data14x0, Data14x1, Data14x2, Data14x3 } :
+ {Sel3, Sel2, Sel1, Sel0} == 15 ? { Data15x0, Data15x1, Data15x2, Data15x3 } : 'bx;
+endmodule
+
+module mux1_5_32(
+ Sel0, Sel1, Sel2, Sel3, Sel4,
+ Data0x0, Data1x0, Data2x0, Data3x0, Data4x0, Data5x0, Data6x0, Data7x0, Data8x0, Data9x0, Data10x0, Data11x0, Data12x0, Data13x0, Data14x0, Data15x0,
+ Data16x0, Data17x0, Data18x0, Data19x0, Data20x0, Data21x0, Data22x0, Data23x0, Data24x0, Data25x0, Data26x0, Data27x0, Data28x0, Data29x0, Data30x0, Data31x0,
+ Result0
+);
+input Sel0, Sel1, Sel2, Sel3, Sel4;
+input Data0x0, Data1x0, Data2x0, Data3x0, Data4x0, Data5x0, Data6x0, Data7x0, Data8x0, Data9x0, Data10x0, Data11x0, Data12x0, Data13x0, Data14x0, Data15x0;
+input Data16x0, Data17x0, Data18x0, Data19x0, Data20x0, Data21x0, Data22x0, Data23x0, Data24x0, Data25x0, Data26x0, Data27x0, Data28x0, Data29x0, Data30x0, Data31x0;
+output Result0;
+assign Result0 =
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 0 ? Data0x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 1 ? Data1x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 2 ? Data2x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 3 ? Data3x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 4 ? Data4x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 5 ? Data5x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 6 ? Data6x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 7 ? Data7x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 8 ? Data8x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 9 ? Data9x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 10 ? Data10x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 11 ? Data11x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 12 ? Data12x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 13 ? Data13x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 14 ? Data14x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 15 ? Data15x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 16 ? Data16x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 17 ? Data17x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 18 ? Data18x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 19 ? Data19x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 20 ? Data20x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 21 ? Data21x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 22 ? Data22x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 23 ? Data23x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 24 ? Data24x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 25 ? Data25x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 26 ? Data26x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 27 ? Data27x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 28 ? Data28x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 29 ? Data29x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 30 ? Data30x0 :
+ {Sel4, Sel3, Sel2, Sel1, Sel0} == 31 ? Data31x0 : 'bx;
+endmodule
+
diff --git a/manual/CHAPTER_StateOfTheArt/simlib_yosys.v b/manual/CHAPTER_StateOfTheArt/simlib_yosys.v
new file mode 100644
index 00000000..454c9a83
--- /dev/null
+++ b/manual/CHAPTER_StateOfTheArt/simlib_yosys.v
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * The internal logic cell simulation library.
+ *
+ * This Verilog library contains simple simulation models for the internal
+ * logic cells (_NOT_, _AND_, ...) that are generated by the default technology
+ * mapper (see "stdcells.v" in this directory) and expected by the "abc" pass.
+ *
+ */
+
+module _NOT_(A, Y);
+input A;
+output Y;
+assign Y = ~A;
+endmodule
+
+module _AND_(A, B, Y);
+input A, B;
+output Y;
+assign Y = A & B;
+endmodule
+
+module _OR_(A, B, Y);
+input A, B;
+output Y;
+assign Y = A | B;
+endmodule
+
+module _XOR_(A, B, Y);
+input A, B;
+output Y;
+assign Y = A ^ B;
+endmodule
+
+module _MUX_(A, B, S, Y);
+input A, B, S;
+output reg Y;
+always @* begin
+ if (S)
+ Y = B;
+ else
+ Y = A;
+end
+endmodule
+
+module _DFF_N_(D, Q, C);
+input D, C;
+output reg Q;
+always @(negedge C) begin
+ Q <= D;
+end
+endmodule
+
+module _DFF_P_(D, Q, C);
+input D, C;
+output reg Q;
+always @(posedge C) begin
+ Q <= D;
+end
+endmodule
+
+module _DFF_NN0_(D, Q, C, R);
+input D, C, R;
+output reg Q;
+always @(negedge C or negedge R) begin
+ if (R == 0)
+ Q <= 0;
+ else
+ Q <= D;
+end
+endmodule
+
+module _DFF_NN1_(D, Q, C, R);
+input D, C, R;
+output reg Q;
+always @(negedge C or negedge R) begin
+ if (R == 0)
+ Q <= 1;
+ else
+ Q <= D;
+end
+endmodule
+
+module _DFF_NP0_(D, Q, C, R);
+input D, C, R;
+output reg Q;
+always @(negedge C or posedge R) begin
+ if (R == 1)
+ Q <= 0;
+ else
+ Q <= D;
+end
+endmodule
+
+module _DFF_NP1_(D, Q, C, R);
+input D, C, R;
+output reg Q;
+always @(negedge C or posedge R) begin
+ if (R == 1)
+ Q <= 1;
+ else
+ Q <= D;
+end
+endmodule
+
+module _DFF_PN0_(D, Q, C, R);
+input D, C, R;
+output reg Q;
+always @(posedge C or negedge R) begin
+ if (R == 0)
+ Q <= 0;
+ else
+ Q <= D;
+end
+endmodule
+
+module _DFF_PN1_(D, Q, C, R);
+input D, C, R;
+output reg Q;
+always @(posedge C or negedge R) begin
+ if (R == 0)
+ Q <= 1;
+ else
+ Q <= D;
+end
+endmodule
+
+module _DFF_PP0_(D, Q, C, R);
+input D, C, R;
+output reg Q;
+always @(posedge C or posedge R) begin
+ if (R == 1)
+ Q <= 0;
+ else
+ Q <= D;
+end
+endmodule
+
+module _DFF_PP1_(D, Q, C, R);
+input D, C, R;
+output reg Q;
+always @(posedge C or posedge R) begin
+ if (R == 1)
+ Q <= 1;
+ else
+ Q <= D;
+end
+endmodule
+
diff --git a/manual/CHAPTER_StateOfTheArt/sis-1.3.6-buildfixes.patch b/manual/CHAPTER_StateOfTheArt/sis-1.3.6-buildfixes.patch
new file mode 100644
index 00000000..ad957d6b
--- /dev/null
+++ b/manual/CHAPTER_StateOfTheArt/sis-1.3.6-buildfixes.patch
@@ -0,0 +1,113 @@
+Some minor build fixes for sis-1.3.6 as it can be downloaded from
+http://www-cad.eecs.berkeley.edu/~pchong/sis.html or
+http://embedded.eecs.berkeley.edu/Alumni/pchong/sis.html
+
+diff --git a/sis/io/read_kiss.c b/sis/io/read_kiss.c
+index 814e526..c862892 100644
+--- a/sis/io/read_kiss.c
++++ b/sis/io/read_kiss.c
+@@ -10,7 +10,6 @@
+ #ifdef SIS
+ #include "sis.h"
+
+-extern void read_error();
+ extern int read_lineno;
+ extern char *read_filename;
+
+diff --git a/sis/pld/act_bdd.c b/sis/pld/act_bdd.c
+index 4fb4415..a5cd74c 100644
+--- a/sis/pld/act_bdd.c
++++ b/sis/pld/act_bdd.c
+@@ -141,6 +141,8 @@ char *name;
+ return p_vertex;
+ }
+
++static int compare();
++
+ /* Or 2 ACT's*/
+ act_t *
+ my_or_act_F(array_b,cover, array)
+@@ -148,7 +150,6 @@ array_t *array_b;
+ array_t *array;
+ sm_row *cover;
+ {
+- static int compare();
+ int i;
+ act_t *up_vertex, *down_vertex, *vertex;
+ sm_element *p;
+diff --git a/sis/pld/act_ite.c b/sis/pld/act_ite.c
+index a35f2fb..7b824df 100644
+--- a/sis/pld/act_ite.c
++++ b/sis/pld/act_ite.c
+@@ -125,6 +125,8 @@ node_t *fanin;
+ and the minimum column cover variables in cover, generates an ite for the
+ original function. */
+
++static int compare();
++
+ ite_vertex *
+ my_or_ite_F(array_b, cover, array, network)
+ array_t *array_b;
+@@ -132,7 +134,6 @@ array_t *array;
+ sm_row *cover;
+ network_t *network;
+ {
+- static int compare();
+ int i;
+ ite_vertex *vertex;
+ sm_element *p;
+diff --git a/sis/pld/xln_merge.c b/sis/pld/xln_merge.c
+index 075e6c5..16f4d61 100644
+--- a/sis/pld/xln_merge.c
++++ b/sis/pld/xln_merge.c
+@@ -284,6 +284,7 @@ array_t *match1_array, *match2_array;
+
+ }
+
++static sm_row *xln_merge_find_neighbor_of_row1_with_minimum_neighbors();
+
+ /*----------------------------------------------------------------------------------------------------
+ An alternate to lindo option. Uses greedy merging. A node with minimum mergeable nodes is picked
+@@ -296,7 +297,6 @@ xln_merge_nodes_without_lindo(coeff, cand_node_array, match1_array, match2_array
+ {
+ node_t *n1, *n2;
+ sm_row *row1, *row2;
+- static sm_row *xln_merge_find_neighbor_of_row1_with_minimum_neighbors();
+
+ while (TRUE) {
+ row1 = sm_shortest_row(coeff);
+diff --git a/sis/pld/xln_part_dec.c b/sis/pld/xln_part_dec.c
+index 1c856bd..b78828a 100644
+--- a/sis/pld/xln_part_dec.c
++++ b/sis/pld/xln_part_dec.c
+@@ -49,13 +49,14 @@ int size;
+
+
+
++static int kernel_value();
++
+ int
+ split_node(network, node, size)
+ network_t *network;
+ node_t *node;
+ int size;
+ {
+- static int kernel_value();
+ int i, value = 1;
+ kern_node *sorted;
+ divisor_t *div, *best_div;
+diff --git a/xsis/Makefile.am b/xsis/Makefile.am
+index 196d98b..686fdf4 100644
+--- a/xsis/Makefile.am
++++ b/xsis/Makefile.am
+@@ -1,8 +1,8 @@
+ xsis_SOURCES_local = NetPlot.c NetPlot.h NetPlotP.h main.c xastg.c \
+ xblif.c xcmd.c xhelp.c xsis.c xsis.h xutil.c \
+ blif50.px ghost.px help50.px sis50.px
+-AM_CPPFLAGS = -I../sis/include -I@SIS_X_INCLUDES@
+-AM_LDFLAGS = -L@SIS_X_LIBRARIES@
++AM_CPPFLAGS = -I../sis/include
++AM_LDFLAGS =
+ LDADD = ../sis/libsis.a -lXaw -lXmu -lXt -lXext -lX11 -lm
+
+ if SIS_COND_X
diff --git a/manual/CHAPTER_StateOfTheArt/synth.sh b/manual/CHAPTER_StateOfTheArt/synth.sh
new file mode 100755
index 00000000..3a7524a2
--- /dev/null
+++ b/manual/CHAPTER_StateOfTheArt/synth.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+yosys_bin="/usr/local/synthesis/src/yosys/yosys"
+hana_bin="/usr/local/synthesis/src/hana/bin/hana"
+vl2mv_bin="/usr/local/synthesis/bin/vl2mv"
+vis_bin="/usr/local/synthesis/bin/vis"
+iverilog_bin="/usr/local/synthesis/bin/iverilog-0.8"
+odin_bin="/usr/local/synthesis/src/vtr_release/ODIN_II/odin_II.exe"
+abc_bin="/usr/local/synthesis/src/alanmi-abc-b5750272659f/abc"
+edif2ngd="/opt/Xilinx/14.3/ISE_DS/ISE/bin/lin64/edif2ngd"
+netgen="/opt/Xilinx/14.3/ISE_DS/ISE/bin/lin64/netgen"
+
+all_modes="yosys hana vis icarus odin"
+all_sources="always01 always02 always03 arrays01 forgen01 forgen02"
+
+if [ "$*" == "ALL" ]; then
+ for mode in $all_modes; do
+ for src in $all_sources; do
+ echo "synth.sh $mode $src.v ${src}_${mode}.v"
+ ( set -x; bash synth.sh $mode $src.v ${src}_${mode}.v || rm -f ${src}_${mode}.v; ) > ${src}_${mode}.log 2>&1
+ done
+ done
+ exit
+fi
+
+mode="$1"
+source="$2"
+output="$3"
+prefix="${output%.v}"
+
+help() {
+ echo "$0 ALL" >&2
+ echo "$0 {yosys|hana|vis|icarus|odin} <source-file> <output-file>" >&2
+ exit 1
+}
+
+if [ "$#" != 3 -o ! -f "$source" ]; then
+ help
+fi
+
+set -ex
+
+case "$mode" in
+ yosys)
+ $yosys_bin -o $output -b "verilog -noattr" -p proc -p opt -p memory -p opt -p techmap -p opt $source ;;
+ hana)
+ $hana_bin -s $output $source ;;
+ vis)
+ $vl2mv_bin -o $prefix.mv $source
+ { echo "read_blif_mv $prefix.mv"; echo "write_verilog $output"; } | $abc_bin ;;
+ icarus)
+ rm -f $prefix.ngo $prefix.v
+ $iverilog_bin -t fpga -o $prefix.edif $source
+ $edif2ngd $prefix.edif $prefix.ngo
+ $netgen -ofmt verilog $prefix.ngo $prefix.v
+ sed -re '/timescale/ s,^,//,;' -i $prefix.v ;;
+ odin)
+ $odin_bin -o $prefix.blif -V $source
+ sed -re 's,top\^,,g; s,clock,_clock,g;' -i $prefix.blif
+ { echo "read_blif $prefix.blif"; echo "write_verilog $output"; } | $abc_bin ;;
+ *)
+ help
+esac
+
diff --git a/manual/CHAPTER_StateOfTheArt/validate_tb.sh b/manual/CHAPTER_StateOfTheArt/validate_tb.sh
new file mode 100755
index 00000000..b6409eb1
--- /dev/null
+++ b/manual/CHAPTER_StateOfTheArt/validate_tb.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+set -ex
+
+yosys_bin="/usr/local/synthesis/src/yosys/yosys"
+iverilog_bin="iverilog"
+
+all_modes="yosys hana vis icarus odin"
+all_sources="always01 always02 always03 arrays01 forgen01 forgen02"
+
+gcc -o cmp_tbdata cmp_tbdata.c
+
+for src in $all_sources; do
+ echo; echo
+ $yosys_bin -o ${src}_tb.v -b autotest ${src}.v
+ $iverilog_bin -o ${src}_tb ${src}_tb.v ${src}.v
+ ./${src}_tb > ${src}_tb.out
+ for mode in $all_modes; do
+ simlib=""
+ [ -f ${src}_${mode}.v ] || continue
+ [ -f simlib_${mode}.v ] && simlib="simlib_${mode}.v"
+ if $iverilog_bin -o ${src}_${mode}_tb -s testbench ${src}_tb.v ${src}_${mode}.v $simlib; then
+ ./${src}_${mode}_tb > ${src}_${mode}_tb.out
+ else
+ rm -f ${src}_${mode}_tb.out
+ fi
+ done
+done
+
+set +x
+echo; echo; echo
+
+{
+ for mode in $all_modes; do
+ echo -en "\t$mode"
+ done; echo
+
+ for src in $all_sources; do
+ echo -n "$src"
+ for mode in $all_modes; do
+ if [ -f ${src}_${mode}.v ]; then
+ if [ ! -s ${src}_${mode}_tb.out ]; then
+ echo -en "\tmissing"
+ elif ./cmp_tbdata ${src}_tb.out ${src}_${mode}_tb.out; then
+ echo -en "\tok"
+ else
+ echo -en "\tfailed"
+ fi
+ else
+ echo -en "\terror"
+ fi
+ done; echo
+ done
+} | expand -t12
+
diff --git a/manual/CHAPTER_Techmap.tex b/manual/CHAPTER_Techmap.tex
new file mode 100644
index 00000000..13aa8e5a
--- /dev/null
+++ b/manual/CHAPTER_Techmap.tex
@@ -0,0 +1,102 @@
+
+\chapter{Technology Mapping}
+\label{chapter:techmap}
+
+Previous chapters outlined how HDL code is transformed into an RTL netlist. The
+RTL netlist is still based on abstract coarse-grain cell types like arbitrary
+width adders and even multipliers. This chapter covers how an RTL netlist is
+transformed into a functionally equivalent netlist utilizing the cell types
+available in the target architecture.
+
+Technology mapping is often performed in two phases. In the first phase RTL cells
+are mapped to an internal library of single-bit cells (see Sec.~\ref{sec:celllib_gates}).
+In the second phase this netlist of internal gate types is transformed to a netlist
+of gates from the target technology library.
+
+When the target architecture provides coarse-grain cells (such as block ram
+or ALUs), these must be mapped to directly form the RTL netlist, as information
+on the coarse-grain structure of the design is lost when it is mapped to
+bit-width gate types.
+
+\section{Cell Substitution}
+
+The simplest form of technology mapping is cell substitution, as performed by
+the {\tt techmap} pass. This pass, when provided with a Verilog file that
+implements the RTL cell types using simpler cells, simply replaces the RTL
+cells with the provided implementation.
+
+When no map file is provided, {\tt techmap} uses a built-in map file that
+maps the Yosys RTL cell types to the internal gate library used by Yosys.
+The curious reader may find this map file as {\tt techlibs/common/techmap.v} in
+the Yosys source tree.
+
+Additional features have been added to {\tt techmap} to allow for conditional
+mapping of cells (see {\tt help techmap} or Sec.~\ref{cmd:techmap}). This can
+for example be useful if the target architecture supports hardware multipliers for
+certain bit-widths but not for others.
+
+A usual synthesis flow would first use the {\tt techmap} pass to directly map
+some RTL cells to coarse-grain cells provided by the target architecture (if
+any) and then use techmap with the built-in default file to map the remaining
+RTL cells to gate logic.
+
+\section{Subcircuit Substitution}
+
+Sometimes the target architecture provides cells that are more powerful than
+the RTL cells used by Yosys. For example a cell in the target architecture that can
+calculate the absolute-difference of two numbers does not match any single
+RTL cell type but only combinations of cells.
+
+For these cases Yosys provides the {\tt extract} pass that can match a given set
+of modules against a design and identify the portions of the design that are
+identical (i.e.~isomorphic subcircuits) to any of the given modules. These
+matched subcircuits are then replaced by instances of the given modules.
+
+The {\tt extract} pass also finds basic variations of the given modules,
+such as swapped inputs on commutative cell types.
+
+In addition to this the {\tt extract} pass also has limited support for
+frequent subcircuit mining, i.e.~the process of finding recurring subcircuits
+in the design. This has a few applications, including the design of new
+coarse-grain architectures \cite{intersynthFdlBookChapter}.
+
+The hard algorithmic work done by the {\tt extract} pass (solving the
+isomorphic subcircuit problem and frequent subcircuit mining) is performed
+using the SubCircuit library that can also be used stand-alone without Yosys
+(see Sec.~\ref{sec:SubCircuit}).
+
+\section{Gate-Level Technology Mapping}
+\label{sec:techmap_extern}
+
+On the gate-level the target architecture is usually described by a ``Liberty
+file''. The Liberty file format is an industry standard format that can be
+used to describe the behaviour and other properties of standard library cells
+\citeweblink{LibertyFormat}.
+
+Mapping a design utilizing the Yosys internal gate library (e.g.~as a result
+of mapping it to this representation using the {\tt techmap} pass) is
+performed in two phases.
+
+First the register cells must be mapped to the registers that are available
+on the target architectures. The target architecture might not provide all
+variations of d-type flip-flops with positive and negative clock edge,
+high-active and low-active asynchronous set and/or reset, etc. Therefore the
+process of mapping the registers might add additional inverters to the design
+and thus it is important to map the register cells first.
+
+Mapping of the register cells may be performed by using the {\tt dfflibmap}
+pass. This pass expects a Liberty file as argument (using the {\tt -liberty}
+option) and only uses the register cells from the Liberty file.
+
+Secondly the combinational logic must be mapped to the target architecture.
+This is done using the external program ABC \citeweblink{ABC} via the
+{\tt abc} pass by using the {\tt -liberty} option to the pass. Note that
+in this case only the combinatorial cells are used from the cell library.
+
+Occasionally Liberty files contain trade secrets (such as sensitive timing
+information) that cannot be shared freely. This complicates processes such as
+reporting bugs in the tools involved. When the information in the Liberty file
+used by Yosys and ABC are not part of the sensitive information, the additional
+tool {\tt yosys-filterlib} (see Sec.~\ref{sec:filterlib}) can be used to strip
+the sensitive information from the Liberty file.
+
diff --git a/manual/CHAPTER_Verilog.tex b/manual/CHAPTER_Verilog.tex
new file mode 100644
index 00000000..e9ca6114
--- /dev/null
+++ b/manual/CHAPTER_Verilog.tex
@@ -0,0 +1,849 @@
+
+\chapter{The Verilog and AST Frontends}
+\label{chapter:verilog}
+
+This chapter provides an overview of the implementation of the Yosys Verilog
+and AST frontends. The Verilog frontend reads Verilog-2005 code and creates
+an abstract syntax tree (AST) representation of the input. This AST representation
+is then passed to the AST frontend that converts it to RTLIL data, as illustrated
+in Fig.~\ref{fig:Verilog_flow}.
+
+\begin{figure}[b!]
+ \hfil
+ \begin{tikzpicture}
+ \tikzstyle{process} = [draw, fill=green!10, rectangle, minimum height=3em, minimum width=10em, node distance=5em, font={\ttfamily}]
+ \tikzstyle{data} = [draw, fill=blue!10, ellipse, minimum height=3em, minimum width=7em, node distance=5em, font={\ttfamily}]
+
+ \node[data] (n1) {Verilog Source};
+ \node[process] (n2) [below of=n1] {Verilog Frontend};
+ \node[data] (n3) [below of=n2] {AST};
+ \node[process] (n4) [below of=n3] {AST Frontend};
+ \node[data] (n5) [below of=n4] {RTLIL};
+
+ \draw[-latex] (n1) -- (n2);
+ \draw[-latex] (n2) -- (n3);
+ \draw[-latex] (n3) -- (n4);
+ \draw[-latex] (n4) -- (n5);
+
+ \tikzstyle{details} = [draw, fill=yellow!5, rectangle, node distance=6cm, font={\ttfamily}]
+
+ \node[details] (d1) [right of=n2] {\begin{minipage}{5cm}
+ \hfil
+ \begin{tikzpicture}
+ \tikzstyle{subproc} = [draw, fill=green!10, rectangle, minimum height=2em, minimum width=10em, node distance=3em, font={\ttfamily}]
+ \node (s0) {};
+ \node[subproc] (s1) [below of=s0] {Preprocessor};
+ \node[subproc] (s2) [below of=s1] {Lexer};
+ \node[subproc] (s3) [below of=s2] {Parser};
+ \node[node distance=3em] (s4) [below of=s3] {};
+ \draw[-latex] (s0) -- (s1);
+ \draw[-latex] (s1) -- (s2);
+ \draw[-latex] (s2) -- (s3);
+ \draw[-latex] (s3) -- (s4);
+ \end{tikzpicture}
+ \end{minipage}};
+
+ \draw[dashed] (n2.north east) -- (d1.north west);
+ \draw[dashed] (n2.south east) -- (d1.south west);
+
+ \node[details] (d2) [right of=n4] {\begin{minipage}{5cm}
+ \hfil
+ \begin{tikzpicture}
+ \tikzstyle{subproc} = [draw, fill=green!10, rectangle, minimum height=2em, minimum width=10em, node distance=3em, font={\ttfamily}]
+ \node (s0) {};
+ \node[subproc] (s1) [below of=s0] {Simplifier};
+ \node[subproc] (s2) [below of=s1] {RTLIL Generator};
+ \node[node distance=3em] (s3) [below of=s2] {};
+ \draw[-latex] (s0) -- (s1);
+ \draw[-latex] (s1) -- (s2);
+ \draw[-latex] (s2) -- (s3);
+ \end{tikzpicture}
+ \end{minipage}};
+
+ \draw[dashed] (n4.north east) -- (d2.north west);
+ \draw[dashed] (n4.south east) -- (d2.south west);
+
+ \end{tikzpicture}
+ \caption{Simplified Verilog to RTLIL data flow}
+ \label{fig:Verilog_flow}
+\end{figure}
+
+
+\section{Transforming Verilog to AST}
+
+The {\it Verilog frontend} converts the Verilog sources to an internal AST representation that closely resembles
+the structure of the original Verilog code. The Verilog frontend consists of three components, the
+{\it Preprocessor}, the {\it Lexer} and the {\it Parser}.
+
+The source code to the Verilog frontend can be found in {\tt frontends/verilog/} in the Yosys source tree.
+
+\subsection{The Verilog Preprocessor}
+
+The Verilog preprocessor scans over the Verilog source code and interprets some of the Verilog compiler
+directives such as \lstinline[language=Verilog]{`include}, \lstinline[language=Verilog]{`define} and
+\lstinline[language=Verilog]{`ifdef}.
+
+It is implemented as a C++ function that is passed a file descriptor as input and returns the
+pre-processed Verilog code as a \lstinline[language=C++]{std::string}.
+
+The source code to the Verilog Preprocessor can be found in {\tt
+frontends/verilog/preproc.cc} in the Yosys source tree.
+
+\subsection{The Verilog Lexer}
+
+\begin{sloppypar}
+The Verilog Lexer is written using the lexer generator {\it flex} \citeweblink{flex}. Its source code
+can be found in {\tt frontends/verilog/lexer.l} in the Yosys source tree.
+The lexer does little more than identifying all keywords and literals
+recognised by the Yosys Verilog frontend.
+\end{sloppypar}
+
+The lexer keeps track of the current location in the Verilog source code using
+some global variables. These variables are used by the constructor of AST nodes
+to annotate each node with the source code location it originated from.
+
+\begin{sloppypar}
+Finally the lexer identifies and handles special comments such as
+``\lstinline[language=Verilog]{// synopsys translate_off}'' and
+``\lstinline[language=Verilog]{// synopsys full_case}''. (It is recommended to
+use \lstinline[language=Verilog]{`ifdef} constructs instead of the Synsopsys
+translate\_on/off comments and attributes such as
+\lstinline[language=Verilog]{(* full_case *)} over ``\lstinline[language=Verilog]{// synopsys full_case}''
+whenever possible.)
+\end{sloppypar}
+
+\subsection{The Verilog Parser}
+
+The Verilog Parser is written using the parser generator {\it bison} \citeweblink{bison}. Its source code
+can be found in {\tt frontends/verilog/parser.y} in the Yosys source tree.
+
+It generates an AST using the \lstinline[language=C++]{AST::AstNode} data structure
+defined in {\tt frontends/ast/ast.h}. An \lstinline[language=C++]{AST::AstNode} object has
+the following properties:
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{table}[b!]
+\hfil
+\begin{tabular}{>{\raggedright\arraybackslash}p{7cm}>{\raggedright\arraybackslash}p{8cm}}
+AST Node Type & Corresponding Verilog Construct \\
+\hline
+\hline
+\arrayrulecolor{gray}
+{\tt AST\_NONE} & This Node type should never be used. \\
+\hline
+%
+{\tt AST\_DESIGN} & This node type is used for the top node of the AST tree. It
+has no corresponding Verilog construct. \\
+\hline
+%
+{\tt AST\_MODULE},
+{\tt AST\_TASK},
+{\tt AST\_FUNCTION} &
+\lstinline[language=Verilog];module;,
+\lstinline[language=Verilog];task; and
+\lstinline[language=Verilog];function; \\
+\hline
+%
+{\tt AST\_WIRE} &
+\lstinline[language=Verilog];input;,
+\lstinline[language=Verilog];output;,
+\lstinline[language=Verilog];wire;,
+\lstinline[language=Verilog];reg; and
+\lstinline[language=Verilog];integer; \\
+\hline
+%
+{\tt AST\_MEMORY} &
+Verilog Arrays \\
+\hline
+%
+{\tt AST\_AUTOWIRE} &
+Created by the simplifier when an undeclared signal name is used. \\
+\hline
+%
+{\tt AST\_PARAMETER},
+{\tt AST\_LOCALPARAM} &
+\lstinline[language=Verilog];parameter; and
+\lstinline[language=Verilog];localparam; \\
+\hline
+%
+{\tt AST\_PARASET} &
+Parameter set in cell instantiation \\
+\hline
+%
+{\tt AST\_ARGUMENT} &
+Port connection in cell instantiation \\
+\hline
+%
+{\tt AST\_RANGE} &
+Bit-Index in a signal or element index in array \\
+\hline
+%
+{\tt AST\_CONSTANT} &
+A literal value \\
+\hline
+%
+{\tt AST\_CELLTYPE} &
+The type of cell in cell instantiation \\
+\hline
+%
+{\tt AST\_IDENTIFIER} &
+An Identifier (signal name in expression or cell/task/etc. name in other contexts) \\
+\hline
+%
+{\tt AST\_PREFIX} &
+Construct an identifier in the form {\tt <prefix>[<index>].<suffix>} (used only in
+advanced generate constructs) \\
+\hline
+%
+{\tt AST\_FCALL},
+{\tt AST\_TCALL} &
+Call to function or task \\
+\hline
+%
+{\tt AST\_TO\_SIGNED},
+{\tt AST\_TO\_UNSIGNED} &
+The \lstinline[language=Verilog];$signed(); and
+\lstinline[language=Verilog];$unsigned(); functions \\
+\hline
+\end{tabular}
+\caption{AST node types with their corresponding Verilog constructs. \\ (continued on next page)}
+\label{tab:Verilog_AstNodeType}
+\end{table}
+
+\begin{table}[t!]
+\ContinuedFloat
+\hfil
+\begin{tabular}{>{\raggedright\arraybackslash}p{7cm}>{\raggedright\arraybackslash}p{8cm}}
+AST Node Type & Corresponding Verilog Construct \\
+\hline
+\hline
+\arrayrulecolor{gray}
+{\tt AST\_CONCAT}
+{\tt AST\_REPLICATE} &
+The \lstinline[language=Verilog];{...}; and
+\lstinline[language=Verilog];{...{...}}; operators \\
+\hline
+%
+{\tt AST\_BIT\_NOT},
+{\tt AST\_BIT\_AND},
+{\tt AST\_BIT\_OR},
+{\tt AST\_BIT\_XOR},
+{\tt AST\_BIT\_XNOR} &
+The bitwise operators \break
+\lstinline[language=Verilog];~;,
+\lstinline[language=Verilog];&;,
+\lstinline[language=Verilog];|;,
+\lstinline[language=Verilog];^; and
+\lstinline[language=Verilog];~^; \\
+\hline
+%
+{\tt AST\_REDUCE\_AND},
+{\tt AST\_REDUCE\_OR},
+{\tt AST\_REDUCE\_XOR},
+{\tt AST\_REDUCE\_XNOR} &
+The unary reduction operators \break
+\lstinline[language=Verilog];~;,
+\lstinline[language=Verilog];&;,
+\lstinline[language=Verilog];|;,
+\lstinline[language=Verilog];^; and
+\lstinline[language=Verilog];~^; \\
+\hline
+%
+{\tt AST\_REDUCE\_BOOL} &
+Conversion from multi-bit value to boolean value
+(equivalent to {\tt AST\_REDUCE\_OR}) \\
+\hline
+%
+{\tt AST\_SHIFT\_LEFT},
+{\tt AST\_SHIFT\_RIGHT},
+{\tt AST\_SHIFT\_SLEFT},
+{\tt AST\_SHIFT\_SRIGHT} &
+The shift operators \break
+\lstinline[language=Verilog];<<;,
+\lstinline[language=Verilog];>>;,
+\lstinline[language=Verilog];<<<; and
+\lstinline[language=Verilog];>>>; \\
+\hline
+%
+{\tt AST\_LT},
+{\tt AST\_LE},
+{\tt AST\_EQ},
+{\tt AST\_NE},
+{\tt AST\_GE},
+{\tt AST\_GT} &
+The relational operators \break
+\lstinline[language=Verilog];<;,
+\lstinline[language=Verilog];<=;,
+\lstinline[language=Verilog];==;,
+\lstinline[language=Verilog];!=;,
+\lstinline[language=Verilog];>=; and
+\lstinline[language=Verilog];>; \\
+\hline
+%
+{\tt AST\_ADD},
+{\tt AST\_SUB},
+{\tt AST\_MUL},
+{\tt AST\_DIV},
+{\tt AST\_MOD},
+{\tt AST\_POW} &
+The binary operators \break
+\lstinline[language=Verilog];+;,
+\lstinline[language=Verilog];-;,
+\lstinline[language=Verilog];*;,
+\lstinline[language=Verilog];/;,
+\lstinline[language=Verilog];%; and
+\lstinline[language=Verilog];**; \\
+\hline
+%
+{\tt AST\_POS},
+{\tt AST\_NEG} &
+The prefix operators
+\lstinline[language=Verilog];+; and
+\lstinline[language=Verilog];-; \\
+\hline
+%
+{\tt AST\_LOGIC\_AND},
+{\tt AST\_LOGIC\_OR},
+{\tt AST\_LOGIC\_NOT} &
+The logic operators
+\lstinline[language=Verilog];&&;,
+\lstinline[language=Verilog];||; and
+\lstinline[language=Verilog];!; \\
+\hline
+%
+{\tt AST\_TERNARY} &
+The ternary \lstinline[language=Verilog];?:;-operator \\
+\hline
+%
+{\tt AST\_MEMRD}
+{\tt AST\_MEMWR} &
+Read and write memories. These nodes are generated by
+the AST simplifier for writes/reads to/from Verilog arrays. \\
+\hline
+%
+{\tt AST\_ASSIGN} &
+An \lstinline[language=Verilog];assign; statement \\
+\hline
+%
+{\tt AST\_CELL} &
+A cell instantiation \\
+\hline
+%
+{\tt AST\_PRIMITIVE} &
+A primitive cell (\lstinline[language=Verilog];and;,
+\lstinline[language=Verilog];nand;,
+\lstinline[language=Verilog];or;, etc.) \\
+\hline
+%
+{\tt AST\_ALWAYS},
+{\tt AST\_INITIAL} &
+Verilog \lstinline[language=Verilog];always;- and \lstinline[language=Verilog];initial;-blocks \\
+\hline
+%
+{\tt AST\_BLOCK} &
+A \lstinline[language=Verilog];begin;-\lstinline[language=Verilog];end;-block \\
+\hline
+%
+{\tt AST\_ASSIGN\_EQ}.
+{\tt AST\_ASSIGN\_LE} &
+Blocking (\lstinline[language=Verilog];=;) and nonblocking (\lstinline[language=Verilog];<=;)
+assignments within an \lstinline[language=Verilog];always;- or \lstinline[language=Verilog];initial;-block \\
+\hline
+%
+{\tt AST\_CASE}.
+{\tt AST\_COND},
+{\tt AST\_DEFAULT} &
+The \lstinline[language=Verilog];case; (\lstinline[language=Verilog];if;) statements, conditions within a case
+and the default case respectively \\
+\hline
+%
+{\tt AST\_FOR} &
+A \lstinline[language=Verilog];for;-loop with an
+\lstinline[language=Verilog];always;- or
+\lstinline[language=Verilog];initial;-block \\
+\hline
+%
+{\tt AST\_GENVAR},
+{\tt AST\_GENBLOCK},
+{\tt AST\_GENFOR},
+{\tt AST\_GENIF} &
+The \lstinline[language=Verilog];genvar; and
+\lstinline[language=Verilog];generate; keywords and
+\lstinline[language=Verilog];for; and \lstinline[language=Verilog];if; within a
+generate block. \\
+\hline
+%
+{\tt AST\_POSEDGE},
+{\tt AST\_NEGEDGE},
+{\tt AST\_EDGE} &
+Event conditions for \lstinline[language=Verilog];always; blocks. \\
+\hline
+\end{tabular}
+\caption{AST node types with their corresponding Verilog constructs. \\ (continuation from previous page)}
+\label{tab:Verilog_AstNodeTypeCont}
+\end{table}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{itemize}
+\item {\bf The node type} \\
+This enum (\lstinline[language=C++]{AST::AstNodeType}) specifies the role of the node.
+Table~\ref{tab:Verilog_AstNodeType} contains a list of all node types.
+\item {\bf The child nodes} \\
+This is a list of pointers to all children in the abstract syntax tree.
+\item {\bf Attributes} \\
+As almost every AST node might have Verilog attributes assigned to it, the
+\lstinline[language=C++]{AST::AstNode} has direct support for attributes. Note that the
+attribute values are again AST nodes.
+\item {\bf Node content} \\
+Each node might have additional content data. A series of member variables exist to hold such data.
+For example the member \lstinline[language=C++]{std::string str} can hold a string value and is
+used e.g.~in the {\tt AST\_IDENTIFIER} node type to store the identifier name.
+\item {\bf Source code location} \\
+Each \lstinline[language=C++]{AST::AstNode} is automatically annotated with the current
+source code location by the \lstinline[language=C++]{AST::AstNode} constructor. It is
+stored in the \lstinline[language=C++]{std::string filename} and \lstinline[language=C++]{int linenum}
+member variables.
+\end{itemize}
+
+The \lstinline[language=C++]{AST::AstNode} constructor can be called with up to
+two child nodes that are automatically added to the list of child nodes for the new object.
+This simplifies the creation of AST nodes for simple expressions a bit. For example the bison
+code for parsing multiplications:
+
+\begin{lstlisting}[numbers=left,frame=single]
+ basic_expr '*' attr basic_expr {
+ $$ = new AstNode(AST_MUL, $1, $4);
+ append_attr($$, $3);
+ } |
+\end{lstlisting}
+
+The generated AST data structure is then passed directly to the AST frontend
+that performs the actual conversion to RTLIL.
+
+Note that the Yosys command {\tt read\_verilog} provides the options {\tt -yydebug}
+and {\tt -dump\_ast} that can be used to print the parse tree or abstract syntax tree
+respectively.
+
+\section{Transforming AST to RTLIL}
+
+The {\it AST Frontend} converts a set of modules in AST representation to
+modules in RTLIL representation and adds them to the current design. This is done
+in two steps: {\it simplification} and {\it RTLIL generation}.
+
+The source code to the AST frontend can be found in {\tt frontends/ast/} in the Yosys source tree.
+
+\subsection{AST Simplification}
+
+A full-featured AST is too complex to be transformed into RTLIL directly. Therefore it must
+first be brought into a simpler form. This is done by calling the \lstinline[language=C++]{AST::AstNode::simplify()}
+method of all {\tt AST\_MODULE} nodes in the AST. This initiates a recursive process that performs the following transformations
+on the AST data structure:
+
+\begin{itemize}
+\item Inline all task and function calls.
+\item Evaluate all \lstinline[language=Verilog]{generate}-statements and unroll all \lstinline[language=Verilog]{for}-loops.
+\item Perform const folding where it is necessary (e.g.~in the value part of {\tt AST\_PARAMETER}, {\tt AST\_LOCALPARAM},
+{\tt AST\_PARASET} and {\tt AST\_RANGE} nodes).
+\item Replace {\tt AST\_PRIMITIVE} nodes with appropriate {\tt AST\_ASSIGN} nodes.
+\item Replace dynamic bit ranges in the left-hand-side of assignments with {\tt AST\_CASE} nodes with {\tt AST\_COND} children
+for each possible case.
+\item Detect array access patterns that are too complicated for the {\tt RTLIL::Memory} abstraction and replace them
+with a set of signals and cases for all reads and/or writes.
+\item Otherwise replace array accesses with {\tt AST\_MEMRD} and {\tt AST\_MEMWR} nodes.
+\end{itemize}
+
+In addition to these transformations, the simplifier also annotates the AST with additional information that is needed
+for the RTLIL generator, namely:
+
+\begin{itemize}
+\item All ranges (width of signals and bit selections) are not only const folded but (when a constant value
+is found) are also written to member variables in the {\tt AST\_RANGE} node.
+\item All identifiers are resolved and all {\tt AST\_IDENTIFIER} nodes are annotated with a pointer to the AST node
+that contains the declaration of the identifier. If no declaration has been found, an {\tt AST\_AUTOWIRE} node
+is created and used for the annotation.
+\end{itemize}
+
+This produces an AST that is fairly easy to convert to the RTLIL format.
+
+\subsection{Generating RTLIL}
+
+After AST simplification, the \lstinline[language=C++]{AST::AstNode::genRTLIL()} method of each {\tt AST\_MODULE} node
+in the AST is called. This initiates a recursive process that generates equivalent RTLIL data for the AST data.
+
+The \lstinline[language=C++]{AST::AstNode::genRTLIL()} method returns an \lstinline[language=C++]{RTLIL::SigSpec} structure.
+For nodes that represent expressions (operators, constants, signals, etc.), the cells needed to implement the calculation
+described by the expression are created and the resulting signal is returned. That way it is easy to generate the circuits
+for large expressions using depth-first recursion. For nodes that do not represent an expression (such as {\tt
+AST\_CELL}), the corresponding circuit is generated and an empty \lstinline[language=C++]{RTLIL::SigSpec} is returned.
+
+\section{Synthesizing Verilog always Blocks}
+
+For behavioural Verilog code (code utilizing \lstinline[language=Verilog]{always}- and
+\lstinline[language=Verilog]{initial}-blocks) it is necessary to also generate \lstinline[language=C++]{RTLIL::Process}
+objects. This is done in the following way:
+
+\begin{itemize}
+\item Whenever \lstinline[language=C++]{AST::AstNode::genRTLIL()} encounters an \lstinline[language=Verilog]{always}-
+or \lstinline[language=Verilog]{initial}-block, it creates an instance of
+\lstinline[language=Verilog]{AST_INTERNAL::ProcessGenerator}. This object then generates the
+\lstinline[language=C++]{RTLIL::Process} object for the block. It also calls \lstinline[language=C++]{AST::AstNode::genRTLIL()}
+for all right-hand-side expressions contained within the block.
+%
+\begin{sloppypar}
+\item First the \lstinline[language=Verilog]{AST_INTERNAL::ProcessGenerator} creates a list of all signals assigned
+within the block. It then creates a set of temporary signals using the naming scheme {\tt \$\it<number>\tt
+\textbackslash\it <original\_name>} for each of the assigned signals.
+\end{sloppypar}
+%
+\item Then an \lstinline[language=C++]{RTLIL::Process} is created that assigns all intermediate values for each left-hand-side
+signal to the temporary signal in its \lstinline[language=C++]{RTLIL::CaseRule}/\lstinline[language=C++]{RTLIL::SwitchRule} tree.
+%
+\item Finally a \lstinline[language=C++]{RTLIL::SyncRule} is created for the \lstinline[language=C++]{RTLIL::Process} that
+assigns the temporary signals for the final values to the actual signals.
+%
+\item Calls to \lstinline[language=C++]{AST::AstNode::genRTLIL()} are generated for right hand sides as needed. When blocking
+assignments are used, \lstinline[language=C++]{AST::AstNode::genRTLIL()} is configured using global variables to use
+the temporary signals that hold the correct intermediate values whenever one of the previously assigned signals is used
+in an expression.
+\end{itemize}
+
+Unfortunately the generation of a correct \lstinline[language=C++]{RTLIL::CaseRule}/\lstinline[language=C++]{RTLIL::SwitchRule}
+tree for behavioural code is a non-trivial task. The AST frontend solves the problem using the approach described on the following
+pages. The following example illustrates what the algorithm is supposed to do. Consider the following Verilog code:
+
+\begin{lstlisting}[numbers=left,frame=single,language=Verilog]
+always @(posedge clock) begin
+ out1 = in1;
+ if (in2)
+ out1 = !out1;
+ out2 <= out1;
+ if (in3)
+ out2 <= out2;
+ if (in4)
+ if (in5)
+ out3 <= in6;
+ else
+ out3 <= in7;
+ out1 = out1 ^ out2;
+end
+\end{lstlisting}
+
+This is translated by the Verilog and AST frontends into the following RTLIL code (attributes, cell parameters
+and wire declarations not included):
+
+\begin{lstlisting}[numbers=left,frame=single,language=rtlil]
+cell $logic_not $logic_not$<input>:4$2
+ connect \A \in1
+ connect \Y $logic_not$<input>:4$2_Y
+end
+cell $xor $xor$<input>:13$3
+ connect \A $1\out1[0:0]
+ connect \B \out2
+ connect \Y $xor$<input>:13$3_Y
+end
+process $proc$<input>:1$1
+ assign $0\out3[0:0] \out3
+ assign $0\out2[0:0] $1\out1[0:0]
+ assign $0\out1[0:0] $xor$<input>:13$3_Y
+ switch \in2
+ case 1'1
+ assign $1\out1[0:0] $logic_not$<input>:4$2_Y
+ case
+ assign $1\out1[0:0] \in1
+ end
+ switch \in3
+ case 1'1
+ assign $0\out2[0:0] \out2
+ case
+ end
+ switch \in4
+ case 1'1
+ switch \in5
+ case 1'1
+ assign $0\out3[0:0] \in6
+ case
+ assign $0\out3[0:0] \in7
+ end
+ case
+ end
+ sync posedge \clock
+ update \out1 $0\out1[0:0]
+ update \out2 $0\out2[0:0]
+ update \out3 $0\out3[0:0]
+end
+\end{lstlisting}
+
+Note that the two operators are translated into separate cells outside the generated process. The signal
+\lstinline[language=Verilog]{out1} is assigned using blocking assignments and therefore \lstinline[language=Verilog]{out1}
+has been replaced with a different signal in all expressions after the initial assignment. The signal
+\lstinline[language=Verilog]{out2} is assigned using nonblocking assignments and therefore is not substituted
+on the right-hand-side expressions.
+
+The \lstinline[language=C++]{RTLIL::CaseRule}/\lstinline[language=C++]{RTLIL::SwitchRule}
+tree must be interpreted the following way:
+
+\begin{itemize}
+\item On each case level (the body of the process is the {\it root case}), first the actions on this level are
+evaluated and then the switches within the case are evaluated. (Note that the last assignment on line 13 of the
+Verilog code has been moved to the beginning of the RTLIL process to line 13 of the RTLIL listing.)
+
+I.e.~the special cases deeper in the switch hierarchy override the defaults on the upper levels. The assignments
+in lines 12 and 22 of the RTLIL code serve as an example for this.
+
+Note that in contrast to this, the order within the \lstinline[language=C++]{RTLIL::SwitchRule} objects
+within a \lstinline[language=C++]{RTLIL::CaseRule} is preserved with respect to the original AST and
+Verilog code.
+%
+\item \begin{sloppypar}
+The whole \lstinline[language=C++]{RTLIL::CaseRule}/\lstinline[language=C++]{RTLIL::SwitchRule} tree
+describes an asynchronous circuit. I.e.~the decision tree formed by the switches can be seen independently for
+each assigned signal. Whenever one assigned signal changes, all signals that depend on the changed signals
+are to be updated. For example the assignments in lines 16 and 18 in the RTLIL code in fact influence the assignment
+in line 12, even though they are in the ``wrong order''.
+\end{sloppypar}
+\end{itemize}
+
+The only synchronous part of the process is in the \lstinline[language=C++]{RTLIL::SyncRule} object generated at line
+35 in the RTLIL code. The sync rule is the only part of the process where the original signals are assigned. The
+synchronization event from the original Verilog code has been translated into the synchronization type ({\tt posedge})
+and signal ({\tt \textbackslash clock}) for the \lstinline[language=C++]{RTLIL::SyncRule} object. In the case of
+this simple example the \lstinline[language=C++]{RTLIL::SyncRule} object is later simply transformed into a set of
+d-type flip-flops and the \lstinline[language=C++]{RTLIL::CaseRule}/\lstinline[language=C++]{RTLIL::SwitchRule} tree
+to a decision tree using multiplexers.
+
+\begin{sloppypar}
+In more complex examples (e.g.~asynchronous resets) the part of the
+\lstinline[language=C++]{RTLIL::CaseRule}/\lstinline[language=C++]{RTLIL::SwitchRule}
+tree that describes the asynchronous reset must first be transformed to the
+correct \lstinline[language=C++]{RTLIL::SyncRule} objects. This is done by the {\tt proc\_adff} pass.
+\end{sloppypar}
+
+\subsection{The ProcessGenerator Algorithm}
+
+The \lstinline[language=C++]{AST_INTERNAL::ProcessGenerator} uses the following internal state variables:
+
+\begin{itemize}
+\item \begin{sloppypar}
+\lstinline[language=C++]{subst_rvalue_from} and \lstinline[language=C++]{subst_rvalue_to} \\
+These two variables hold the replacement pattern that should be used by \lstinline[language=C++]{AST::AstNode::genRTLIL()}
+for signals with blocking assignments. After initialization of \lstinline[language=C++]{AST_INTERNAL::ProcessGenerator}
+these two variables are empty.
+\end{sloppypar}
+%
+\item \lstinline[language=C++]{subst_lvalue_from} and \lstinline[language=C++]{subst_lvalue_to} \\
+These two variables contain the mapping from left-hand-side signals ({\tt \textbackslash \it <name>}) to the current
+temporary signal for the same thing (initially {\tt \$0\textbackslash \it <name>}).
+%
+\item \lstinline[language=C++]{current_case} \\
+A pointer to a \lstinline[language=C++]{RTLIL::CaseRule} object. Initially this is the root case of the
+generated \lstinline[language=C++]{RTLIL::Process}.
+\end{itemize}
+
+As the algorithm runs these variables are continuously modified as well as pushed
+to the stack and later restored to their earlier values by popping from the stack.
+
+On startup the ProcessGenerator generates a new
+\lstinline[language=C++]{RTLIL::Process} object with an empty root case and
+initializes its state variables as described above. Then the \lstinline[language=C++]{RTLIL::SyncRule} objects
+are created using the synchronization events from the {\tt AST\_ALWAYS} node and the initial values of
+\lstinline[language=C++]{subst_lvalue_from} and \lstinline[language=C++]{subst_lvalue_to}. Then the
+AST for this process is evaluated recursively.
+
+During this recursive evaluation, three different relevant types of AST nodes can be discovered:
+{\tt AST\_ASSIGN\_LE} (nonblocking assignments), {\tt AST\_ASSIGN\_EQ} (blocking assignments) and
+{\tt AST\_CASE} (\lstinline[language=Verilog]{if} or \lstinline[language=Verilog]{case} statement).
+
+\subsubsection{Handling of Nonblocking Assignments}
+
+When an {\tt AST\_ASSIGN\_LE} node is discovered, the following actions are performed by the
+ProcessGenerator:
+
+\begin{itemize}
+\item The left-hand-side is evaluated using \lstinline[language=C++]{AST::AstNode::genRTLIL()} and mapped to
+a temporary signal name using \lstinline[language=C++]{subst_lvalue_from} and \lstinline[language=C++]{subst_lvalue_to}.
+%
+\item The right-hand-side is evaluated using \lstinline[language=C++]{AST::AstNode::genRTLIL()}. For this call,
+the values of \lstinline[language=C++]{subst_rvalue_from} and \lstinline[language=C++]{subst_rvalue_to} are used to
+map blocking-assigned signals correctly.
+%
+\item Remove all assignments to the same left-hand-side as this assignment from the \lstinline[language=C++]{current_case}
+and all cases within it.
+%
+\item Add the new assignment to the \lstinline[language=C++]{current_case}.
+\end{itemize}
+
+\subsubsection{Handling of Blocking Assignments}
+
+When an {\tt AST\_ASSIGN\_EQ} node is discovered, the following actions are performed by
+the ProcessGenerator:
+
+\begin{itemize}
+\item Perform all the steps that would be performed for a nonblocking assignment (see above).
+%
+\item Remove the found left-hand-side (before lvalue mapping) from
+\lstinline[language=C++]{subst_rvalue_from} and also remove the respective
+bits from \lstinline[language=C++]{subst_rvalue_to}.
+%
+\item Append the found left-hand-side (before lvalue mapping) to \lstinline[language=C++]{subst_rvalue_from}
+and append the found right-hand-side to \lstinline[language=C++]{subst_rvalue_to}.
+\end{itemize}
+
+\subsubsection{Handling of Cases and if-Statements}
+
+\begin{sloppypar}
+When an {\tt AST\_CASE} node is discovered, the following actions are performed by
+the ProcessGenerator:
+
+\begin{itemize}
+\item The values of \lstinline[language=C++]{subst_rvalue_from}, \lstinline[language=C++]{subst_rvalue_to},
+\lstinline[language=C++]{subst_lvalue_from} and \lstinline[language=C++]{subst_lvalue_to} are pushed to the stack.
+%
+\item A new \lstinline[language=C++]{RTLIL::SwitchRule} object is generated, the selection expression is evaluated using
+\lstinline[language=C++]{AST::AstNode::genRTLIL()} (with the use of \lstinline[language=C++]{subst_rvalue_from} and
+\lstinline[language=C++]{subst_rvalue_to}) and added to the \lstinline[language=C++]{RTLIL::SwitchRule} object and the
+object is added to the \lstinline[language=C++]{current_case}.
+%
+\item All lvalues assigned to within the {\tt AST\_CASE} node using blocking assignments are collected and
+saved in the local variable \lstinline[language=C++]{this_case_eq_lvalue}.
+%
+\item New temporary signals are generated for all signals in \lstinline[language=C++]{this_case_eq_lvalue} and stored
+in \lstinline[language=C++]{this_case_eq_ltemp}.
+%
+\item The signals in \lstinline[language=C++]{this_case_eq_lvalue} are mapped using \lstinline[language=C++]{subst_rvalue_from}
+and \lstinline[language=C++]{subst_rvalue_to} and the resulting set of signals is stored in
+\lstinline[language=C++]{this_case_eq_rvalue}.
+\end{itemize}
+
+Then the following steps are performed for each {\tt AST\_COND} node within the {\tt AST\_CASE} node:
+
+\begin{itemize}
+\item Set \lstinline[language=C++]{subst_rvalue_from}, \lstinline[language=C++]{subst_rvalue_to},
+\lstinline[language=C++]{subst_lvalue_from} and \lstinline[language=C++]{subst_lvalue_to} to the values
+that have been pushed to the stack.
+%
+\item Remove \lstinline[language=C++]{this_case_eq_lvalue} from
+\lstinline[language=C++]{subst_lvalue_from}/\lstinline[language=C++]{subst_lvalue_to}.
+%
+\item Append \lstinline[language=C++]{this_case_eq_lvalue} to \lstinline[language=C++]{subst_lvalue_from} and append
+\lstinline[language=C++]{this_case_eq_ltemp} to \lstinline[language=C++]{subst_lvalue_to}.
+%
+\item Push the value of \lstinline[language=C++]{current_case}.
+%
+\item Create a new \lstinline[language=C++]{RTLIL::CaseRule}. Set \lstinline[language=C++]{current_case} to the
+new object and add the new object to the \lstinline[language=C++]{RTLIL::SwitchRule} created above.
+%
+\item Add an assignment from \lstinline[language=C++]{this_case_eq_rvalue} to \lstinline[language=C++]{this_case_eq_ltemp}
+to the new \lstinline[language=C++]{current_case}.
+%
+\item Evaluate the compare value for this case using \lstinline[language=C++]{AST::AstNode::genRTLIL()} (with the use of
+\lstinline[language=C++]{subst_rvalue_from} and \lstinline[language=C++]{subst_rvalue_to}) modify the new
+\lstinline[language=C++]{current_case} accordingly.
+%
+\item Recursion into the children of the {\tt AST\_COND} node.
+%
+\item Restore \lstinline[language=C++]{current_case} by popping the old value from the stack.
+\end{itemize}
+
+Finally the following steps are performed:
+
+\begin{itemize}
+\item The values of \lstinline[language=C++]{subst_rvalue_from}, \lstinline[language=C++]{subst_rvalue_to},
+\lstinline[language=C++]{subst_lvalue_from} and \lstinline[language=C++]{subst_lvalue_to} are popped from the stack.
+%
+\item The signals from \lstinline[language=C++]{this_case_eq_lvalue} are removed from the
+\lstinline[language=C++]{subst_rvalue_from}/\lstinline[language=C++]{subst_rvalue_to}-pair.
+%
+\item The value of \lstinline[language=C++]{this_case_eq_lvalue} is appended to \lstinline[language=C++]{subst_rvalue_from}
+and the value of \lstinline[language=C++]{this_case_eq_ltemp} is appended to \lstinline[language=C++]{subst_rvalue_to}.
+%
+\item Map the signals in \lstinline[language=C++]{this_case_eq_lvalue} using
+\lstinline[language=C++]{subst_lvalue_from}/\lstinline[language=C++]{subst_lvalue_to}.
+%
+\item Remove all assignments to signals in \lstinline[language=C++]{this_case_eq_lvalue} in \lstinline[language=C++]{current_case}
+and all cases within it.
+%
+\item Add an assignment from \lstinline[language=C++]{this_case_eq_ltemp} to \lstinline[language=C++]{this_case_eq_lvalue}
+to \lstinline[language=C++]{current_case}.
+\end{itemize}
+\end{sloppypar}
+
+\subsubsection{Further Analysis of the Algorithm for Cases and if-Statements}
+
+With respect to nonblocking assignments the algorithm is easy: later assignments invalidate earlier assignments.
+For each signal assigned using nonblocking assignments exactly one temporary variable is generated (with the
+{\tt \$0}-prefix) and this variable is used for all assignments of the variable.
+
+Note how all the \lstinline[language=C++]{_eq_}-variables become empty when no blocking assignments are used
+and many of the steps in the algorithm can then be ignored as a result of this.
+
+For a variable with blocking assignments the algorithm shows the following behaviour: First a new temporary variable
+is created. This new temporary variable is then registered as the assignment target for all assignments for this
+variable within the cases for this {\tt AST\_CASE} node. Then for each case the new temporary variable is first
+assigned the old temporary variable. This assignment is overwritten if the variable is actually assigned in this
+case and is kept as a default value otherwise.
+
+This yields an \lstinline[language=C++]{RTLIL::CaseRule} that assigns the new temporary variable in all branches.
+So when all cases have been processed a final assignment is added to the containing block that assigns the new
+temporary variable to the old one. Note how this step always overrides a previous assignment to the old temporary
+variable. Other than nonblocking assignments, the old assignment could still have an effect somewhere
+in the design, as there have been calls to \lstinline[language=C++]{AST::AstNode::genRTLIL()} with a
+\lstinline[language=C++]{subst_rvalue_from}/\lstinline[language=C++]{subst_rvalue_to}-tuple that contained
+the right-hand-side of the old assignment.
+
+\subsection{The proc pass}
+
+The ProcessGenerator converts a behavioural model in AST representation to a behavioural model in
+\lstinline[language=C++]{RTLIL::Process} representation. The actual conversion from a behavioural
+model to an RTL representation is performed by the {\tt proc} pass and the passes it launches:
+
+\begin{itemize}
+\item {\tt proc\_clean} and {\tt proc\_rmdead} \\
+These two passes just clean up the \lstinline[language=C++]{RTLIL::Process} structure. The {\tt proc\_clean}
+pass removes empty parts (eg. empty assignments) from the process and {\tt proc\_rmdead} detects and removes
+unreachable branches from the process's decision trees.
+%
+\item {\tt proc\_arst} \\
+This pass detects processes that describe d-type flip-flops with asynchronous
+resets and rewrites the process to better reflect what they are modelling:
+Before this pass, an asynchronous reset has two edge-sensitive sync rules and
+one top-level \C{RTLIL::SwitchRule} for the reset path. After this pass the
+sync rule for the reset is level-sensitive and the top-level
+\C{RTLIL::SwitchRule} has been removed.
+%
+\item {\tt proc\_mux} \\
+This pass converts the \C{RTLIL::CaseRule}/\C{RTLIL::SwitchRule}-tree to a tree
+of multiplexers per written signal. After this, the \C{RTLIL::Process} structure only contains
+the \C{RTLIL::SyncRule}s that describe the output registers.
+%
+\item {\tt proc\_dff} \\
+This pass replaces the \C{RTLIL::SyncRule}s to d-type flip-flops (with
+asynchronous resets if necessary).
+%
+\item {\tt proc\_clean} \\
+A final call to {\tt proc\_clean} removes the now empty \C{RTLIL::Process} objects.
+\end{itemize}
+
+Performing these last processing steps in passes instead of in the Verilog frontend has two important benefits:
+
+First it improves the transparency of the process. Everything that happens in a separate pass is easier to debug,
+as the RTLIL data structures can be easily investigated before and after each of the steps.
+
+Second it improves flexibility. This scheme can easily be extended to support other types of storage-elements, such
+as sr-latches or d-latches, without having to extend the actual Verilog frontend.
+
+\section{Synthesizing Verilog Arrays}
+
+\begin{fixme}
+Add some information on the generation of {\tt \$memrd} and {\tt \$memwr} cells
+and how they are processed in the {\tt memory} pass.
+\end{fixme}
+
+\section{Synthesizing Parametric Designs}
+
+\begin{fixme}
+Add some information on the \lstinline[language=C++]{RTLIL::Module::derive()} method and how it
+is used to synthesize parametric modules via the {\tt hierarchy} pass.
+\end{fixme}
+
diff --git a/manual/PRESENTATION_ExAdv.tex b/manual/PRESENTATION_ExAdv.tex
new file mode 100644
index 00000000..ef8f64ce
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv.tex
@@ -0,0 +1,896 @@
+
+\section{Yosys by example -- Advanced Synthesis}
+
+\begin{frame}
+\sectionpage
+\end{frame}
+
+\begin{frame}{Overview}
+This section contains 4 subsections:
+\begin{itemize}
+\item Using selections
+\item Advanced uses of techmap
+\item Coarse-grain synthesis
+\item Automatic design changes
+\end{itemize}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Using selections}
+
+\begin{frame}
+\subsectionpage
+\subsectionpagesuffix
+\end{frame}
+
+\subsubsection{Simple selections}
+
+\begin{frame}[fragile]{\subsubsecname}
+Most Yosys commands make use of the ``selection framework'' of Yosys. It can be used
+to apply commands only to part of the design. For example:
+
+\medskip
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+delete # will delete the whole design, but
+
+delete foobar # will only delete the module foobar.
+\end{lstlisting}
+
+\bigskip
+The {\tt select} command can be used to create a selection for subsequent
+commands. For example:
+
+\medskip
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+select foobar # select the module foobar
+delete # delete selected objects
+select -clear # reset selection (select whole design)
+\end{lstlisting}
+\end{frame}
+
+\subsubsection{Selection by object name}
+
+\begin{frame}[fragile]{\subsubsecname}
+The easiest way to select objects is by object name. This is usually only done
+in synthesis scripts that are hand-tailored for a specific design.
+
+\bigskip
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+select foobar # select module foobar
+select foo* # select all modules whose names start with foo
+select foo*/bar* # select all objects matching bar* from modules matching foo*
+select */clk # select objects named clk from all modules
+\end{lstlisting}
+\end{frame}
+
+\subsubsection{Module and design context}
+
+\begin{frame}[fragile]{\subsubsecname}
+Commands can be executed in {\it module\/} or {\it design\/} context. Until now all
+commands have been executed in design context. The {\tt cd} command can be used
+to switch to module context.
+
+\bigskip
+In module context all commands only effect the active module. Objects in the module
+are selected without the {\tt <module\_name>/} prefix. For example:
+
+\bigskip
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+cd foo # switch to module foo
+delete bar # delete object foo/bar
+
+cd mycpu # switch to module mycpu
+dump reg_* # print details on all objects whose names start with reg_
+
+cd .. # switch back to design
+\end{lstlisting}
+
+\bigskip
+Note: Most synthesis scripts never switch to module context. But it is a very powerful
+tool for interactive design investigation.
+\end{frame}
+
+\subsubsection{Selecting by object property or type}
+
+\begin{frame}[fragile]{\subsubsecname}
+Special patterns can be used to select by object property or type. For example:
+
+\bigskip
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+select w:reg_* # select all wires whose names start with reg_
+select a:foobar # select all objects with the attribute foobar set
+select a:foobar=42 # select all objects with the attribute foobar set to 42
+select A:blabla # select all modules with the attribute blabla set
+select foo/t:$add # select all $add cells from the module foo
+\end{lstlisting}
+
+\bigskip
+A complete list of this pattern expressions can be found in the command
+reference to the {\tt select} command.
+\end{frame}
+
+\subsubsection{Combining selection}
+
+\begin{frame}[fragile]{\subsubsecname}
+When more than one selection expression is used in one statement, then they are
+pushed on a stack. The final elements on the stack are combined into a union:
+
+\medskip
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+select t:$dff r:WIDTH>1 # all cells of type $dff and/or with a parameter WIDTH > 1
+\end{lstlisting}
+
+\bigskip
+Special \%-commands can be used to combine the elements on the stack:
+
+\medskip
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+select t:$dff r:WIDTH>1 %i # all cells of type $dff *AND* with a parameter WIDTH > 1
+\end{lstlisting}
+
+\medskip
+\begin{block}{Examples for {\tt \%}-codes (see {\tt help select} for full list)}
+{\tt \%u} \dotfill union of top two elements on stack -- pop 2, push 1 \\
+{\tt \%d} \dotfill difference of top two elements on stack -- pop 2, push 1 \\
+{\tt \%i} \dotfill intersection of top two elements on stack -- pop 2, push 1 \\
+{\tt \%n} \dotfill inverse of top element on stack -- pop 1, push 1 \\
+\end{block}
+\end{frame}
+
+\subsubsection{Expanding selections}
+
+\begin{frame}[fragile]{\subsubsecname}
+Selections of cells and wires can be expanded along connections using {\tt \%}-codes
+for selecting input cones ({\tt \%ci}), output cones ({\tt \%co}), or both ({\tt \%x}).
+
+\medskip
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+# select all wires that are inputs to $add cells
+select t:$add %ci w:* %i
+\end{lstlisting}
+
+\bigskip
+Additional constraints such as port names can be specified.
+
+\medskip
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+# select all wires that connect a "Q" output with a "D" input
+select c:* %co:+[Q] w:* %i c:* %ci:+[D] w:* %i %i
+
+# select the multiplexer tree that drives the signal 'state'
+select state %ci*:+$mux,$pmux[A,B,Y]
+\end{lstlisting}
+
+\bigskip
+See {\tt help select} for full documentation of this expressions.
+\end{frame}
+
+\subsubsection{Incremental selection}
+
+\begin{frame}[fragile]{\subsubsecname}
+Sometimes a selection can most easily be described by a series of add/delete operations.
+The commands {\tt select -add} and {\tt select -del} respectively add or remove objects
+from the current selection instead of overwriting it.
+
+\medskip
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+select -none # start with an empty selection
+select -add reg_* # select a bunch of objects
+select -del reg_42 # but not this one
+select -add state %ci # and add mor stuff
+\end{lstlisting}
+
+\bigskip
+Within a select expression the token {\tt \%} can be used to push the previous selection
+on the stack.
+
+\medskip
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+select t:$add t:$sub # select all $add and $sub cells
+select % %ci % %d # select only the input wires to those cells
+\end{lstlisting}
+\end{frame}
+
+\subsubsection{Creating selection variables}
+
+\begin{frame}[fragile]{\subsubsecname}
+Selections can be stored under a name with the {\tt select -set <name>}
+command. The stored selections can be used in later select expressions
+using the syntax {\tt @<name>}.
+
+\medskip
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+select -set cone_a state_a %ci*:-$dff # set @cone_a to the input cone of state_a
+select -set cone_b state_b %ci*:-$dff # set @cone_b to the input cone of state_b
+select @cone_a @cone_b %i # select the objects that are in both cones
+\end{lstlisting}
+
+\bigskip
+Remember that select expressions can also be used directly as arguments to most
+commands. Some commands also except a single select argument to some options.
+In those cases selection variables must be used to capture more complex selections.
+
+\medskip
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+dump @cone_a @cone_b
+
+select -set cone_ab @cone_a @cone_b %i
+show -color red @cone_ab -color magenta @cone_a -color blue @cone_b
+\end{lstlisting}
+\end{frame}
+
+\begin{frame}[fragile]{\subsubsecname{} -- Example}
+\begin{columns}
+\column[t]{4cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{6pt}{7pt}\selectfont, language=verilog]{PRESENTATION_ExAdv/select.v}
+\column[t]{7cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExAdv/select.ys}
+\end{columns}
+\hfil\includegraphics[width=\linewidth,trim=0 0cm 0 0cm]{PRESENTATION_ExAdv/select.pdf}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Advanced uses of techmap}
+
+\begin{frame}
+\subsectionpage
+\subsectionpagesuffix
+\end{frame}
+
+\subsubsection{Introduction to techmap}
+
+\begin{frame}{\subsubsecname}
+\begin{itemize}
+\item
+The {\tt techmap} command replaces cells in the design with implementations given
+as Verilog code (called ``map files''). It can replace Yosys' internal cell
+types (such as {\tt \$or}) as well as user-defined cell types.
+\medskip\item
+Verilog parameters are used extensively to customize the internal cell types.
+\medskip\item
+Additional special parameters are used by techmap to communicate meta-data to the
+map files.
+\medskip\item
+Special wires are used to instruct techmap how to handle a module in the map file.
+\medskip\item
+Generate blocks and recursion are powerful tools for writing map files.
+\end{itemize}
+\end{frame}
+
+\begin{frame}[t]{\subsubsecname{} -- Example 1/2}
+\vskip-0.2cm
+To map the Verilog OR-reduction operator to 3-input OR gates:
+\vskip-0.2cm
+\begin{columns}
+\column[t]{0.35\linewidth}
+\lstinputlisting[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, lastline=24]{PRESENTATION_ExAdv/red_or3x1_map.v}
+\column[t]{0.65\linewidth}
+\lstinputlisting[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=25]{PRESENTATION_ExAdv/red_or3x1_map.v}
+\end{columns}
+\end{frame}
+
+\begin{frame}[t]{\subsubsecname{} -- Example 2/2}
+\vbox to 0cm{
+\hfil\includegraphics[width=10cm,trim=0 0cm 0 0cm]{PRESENTATION_ExAdv/red_or3x1.pdf}
+\vss
+}
+\begin{columns}
+\column[t]{6cm}
+\column[t]{4cm}
+\vskip-0.6cm\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, firstline=4, lastline=4, frame=single]{PRESENTATION_ExAdv/red_or3x1_test.ys}
+\vskip-0.2cm\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExAdv/red_or3x1_test.v}
+\end{columns}
+\end{frame}
+
+\subsubsection{Conditional techmap}
+
+\begin{frame}{\subsubsecname}
+\begin{itemize}
+\item In some cases only cells with certain properties should be substituted.
+\medskip
+\item The special wire {\tt \_TECHMAP\_FAIL\_} can be used to disable a module
+in the map file for a certain set of parameters.
+\medskip
+\item The wire {\tt \_TECHMAP\_FAIL\_} must be set to a constant value. If it
+is non-zero then the module is disabled for this set of parameters.
+\medskip
+\item Example use-cases:
+\begin{itemize}
+\item coarse-grain cell types that only operate on certain bit widths
+\item memory resources for different memory geometries (width, depth, ports, etc.)
+\end{itemize}
+\end{itemize}
+\end{frame}
+
+\begin{frame}[t]{\subsubsecname{} -- Example}
+\vbox to 0cm{
+\vskip-0.5cm
+\hfill\includegraphics[width=6cm,trim=0 0cm 0 0cm]{PRESENTATION_ExAdv/sym_mul.pdf}
+\vss
+}
+\vskip-0.5cm
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExAdv/sym_mul_map.v}
+\begin{columns}
+\column[t]{6cm}
+\vskip-0.5cm\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=verilog]{PRESENTATION_ExAdv/sym_mul_test.v}
+\column[t]{4cm}
+\vskip-0.5cm\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=ys, lastline=4]{PRESENTATION_ExAdv/sym_mul_test.ys}
+\end{columns}
+\end{frame}
+
+\subsubsection{Scripting in map modules}
+
+\begin{frame}{\subsubsecname}
+\begin{itemize}
+\item The special wires {\tt \_TECHMAP\_DO\_*} can be used to run Yosys scripts
+in the context of the replacement module.
+\medskip
+\item The wire that comes first in alphabetical oder is interpreted as string (must
+be connected to constants) that is executed as script. Then the wire is removed. Repeat.
+\medskip
+\item You can even call techmap recursively!
+\medskip
+\item Example use-cases:
+\begin{itemize}
+\item Using always blocks in map module: call {\tt proc}
+\item Perform expensive optimizations (such as {\tt freduce}) on cells where
+this is known to work well.
+\item Interacting with custom commands.
+\end{itemize}
+\end{itemize}
+
+\scriptsize
+PROTIP: Commands such as {\tt shell}, {\tt show -pause}, and {\tt dump} can be use
+in the {\tt \_TECHMAP\_DO\_*} scripts for debugging map modules.
+\end{frame}
+
+\begin{frame}[t]{\subsubsecname{} -- Example}
+\vbox to 0cm{
+\vskip4.2cm
+\hskip0.5cm\includegraphics[width=10cm,trim=0 0cm 0 0cm]{PRESENTATION_ExAdv/mymul.pdf}
+\vss
+}
+\vskip-0.6cm
+\begin{columns}
+\column[t]{6cm}
+\vskip-0.6cm
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExAdv/mymul_map.v}
+\column[t]{4.2cm}
+\vskip-0.6cm
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=verilog]{PRESENTATION_ExAdv/mymul_test.v}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=ys, lastline=5]{PRESENTATION_ExAdv/mymul_test.ys}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, frame=single, language=ys, firstline=7, lastline=12]{PRESENTATION_ExAdv/mymul_test.ys}
+\end{columns}
+\end{frame}
+
+\subsubsection{Handling constant inputs}
+
+\begin{frame}{\subsubsecname}
+\begin{itemize}
+\item The special parameters {\tt \_TECHMAP\_CONSTMSK\_\it <port-name>\tt \_} and
+{\tt \_TECHMAP\_CONSTVAL\_\it <port-name>\tt \_} can be used to handle constant
+input values to cells.
+\medskip
+\item The former contains 1-bits for all constant input bits on the port.
+\medskip
+\item The latter contains the constant bits or undef (x) for non-constant bits.
+\medskip
+\item Example use-cases:
+\begin{itemize}
+\item Converting arithmetic (for example multiply to shift)
+\item Identify constant addresses or enable bits in memory interfaces.
+\end{itemize}
+\end{itemize}
+\end{frame}
+
+\begin{frame}[t]{\subsubsecname{} -- Example}
+\vbox to 0cm{
+\vskip5.2cm
+\hskip6.5cm\includegraphics[width=5cm,trim=0 0cm 0 0cm]{PRESENTATION_ExAdv/mulshift.pdf}
+\vss
+}
+\vskip-0.6cm
+\begin{columns}
+\column[t]{6cm}
+\vskip-0.4cm
+\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog]{PRESENTATION_ExAdv/mulshift_map.v}
+\column[t]{4.2cm}
+\vskip-0.6cm
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=verilog]{PRESENTATION_ExAdv/mulshift_test.v}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=ys, lastline=5]{PRESENTATION_ExAdv/mulshift_test.ys}
+\end{columns}
+\end{frame}
+
+\subsubsection{Handling shorted inputs}
+
+\begin{frame}{\subsubsecname}
+\begin{itemize}
+\item The special parameters {\tt \_TECHMAP\_BITS\_CONNMAP\_} and
+{\tt \_TECHMAP\_CONNMAP\_\it <port-name>\tt \_} can be used to handle shorted inputs.
+\medskip
+\item Each bit of the port correlates to an {\tt \_TECHMAP\_BITS\_CONNMAP\_} bits wide
+number in {\tt \_TECHMAP\_CONNMAP\_\it <port-name>\tt \_}.
+\medskip
+\item Each unique signal bit is assigned its own number. Identical fields in the {\tt
+\_TECHMAP\_CONNMAP\_\it <port-name>\tt \_} parameters mean shorted signal bits.
+\medskip
+\item The numbers 0-3 are reserved for {\tt 0}, {\tt 1}, {\tt x}, and {\tt z} respectively.
+\medskip
+\item Example use-cases:
+\begin{itemize}
+\item Detecting shared clock or control signals in memory interfaces.
+\item In some cases this can be used for for optimization.
+\end{itemize}
+\end{itemize}
+\end{frame}
+
+\begin{frame}[t]{\subsubsecname{} -- Example}
+\vbox to 0cm{
+\vskip4.5cm
+\hskip6.5cm\includegraphics[width=5cm,trim=0 0cm 0 0cm]{PRESENTATION_ExAdv/addshift.pdf}
+\vss
+}
+\vskip-0.6cm
+\begin{columns}
+\column[t]{6cm}
+\vskip-0.4cm
+\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog]{PRESENTATION_ExAdv/addshift_map.v}
+\column[t]{4.2cm}
+\vskip-0.6cm
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=verilog]{PRESENTATION_ExAdv/addshift_test.v}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=ys, lastline=5]{PRESENTATION_ExAdv/addshift_test.ys}
+\end{columns}
+\end{frame}
+
+\subsubsection{Notes on using techmap}
+
+\begin{frame}{\subsubsecname}
+\begin{itemize}
+\item Don't use positional cell parameters in map modules.
+\medskip
+\item Don't try to implement basic logic optimization with techmap. \\
+{\small (So the OR-reduce using OR3X1 cells map was actually a bad example.)}
+\medskip
+\item You can use the {\tt \$\_\,\_}-prefix for internal cell types to avoid
+collisions with the user-namespace. But always use two underscores or the
+internal consistency checker will trigger on this cells.
+\medskip
+\item Techmap has two major use cases:
+\begin{itemize}
+\item Creating good logic-level representation of arithmetic functions. \\
+This also means using dedicated hardware resources such as half- and full-adder
+cells in ASICS or dedicated carry logic in FPGAs.
+\smallskip
+\item Mapping of coarse-grain resources such as block memory or DSP cells.
+\end{itemize}
+\end{itemize}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Coarse-grain synthesis}
+
+\begin{frame}
+\subsectionpage
+\subsectionpagesuffix
+\end{frame}
+
+\subsubsection{Intro to coarse-grain synthesis}
+
+\begin{frame}[fragile]{\subsubsecname}
+In coarse-grain synthesis the target architecture has cells of the same
+complexity or larger complexity than the internal RTL representation.
+
+For example:
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]
+ wire [15:0] a, b;
+ wire [31:0] c, y;
+ assign y = a * b + c;
+\end{lstlisting}
+
+This circuit contains two cells in the RTL representation: one multiplier and
+one adder. In some architectures this circuit can be implemented using
+a single circuit element, for example an FPGA DSP core. Coarse grain synthesis
+is this mapping of groups of circuit elements to larger components.
+
+\bigskip
+Fine-grain synthesis would be matching the circuit elements to smaller
+components, such as LUTs, gates, or half- and full-adders.
+\end{frame}
+
+\subsubsection{The extract pass}
+
+\begin{frame}{\subsubsecname}
+\begin{itemize}
+\item Like the {\tt techmap} pass, the {\tt extract} pass is called with
+a map file. It compares the circuits inside the modules of the map file
+with the design and looks for sub-circuits in the design that match any
+of the modules in the map file.
+\bigskip
+\item If a match is found, the {\tt extract} pass will replace the matching
+subcircuit with an instance of the module from the map file.
+\bigskip
+\item In a way the {\tt extract} pass is the inverse of the techmap pass.
+\end{itemize}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsubsecname{} -- Example 1/2}
+\vbox to 0cm{
+\vskip2cm
+\begin{tikzpicture}
+ \node at (0,0) {\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_simple_test_00a.pdf}};
+ \node at (3,-3) {\includegraphics[width=8cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_simple_test_00b.pdf}};
+ \draw[yshift=0.2cm,thick,-latex] (1,-1) -- (2,-2);
+\end{tikzpicture}
+\vss}
+\vskip-1.2cm
+\begin{columns}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExAdv/macc_simple_test.v}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=verilog]{PRESENTATION_ExAdv/macc_simple_xmap.v}
+\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=ys]
+read_verilog macc_simple_test.v
+hierarchy -check -top test
+
+extract -map macc_simple_xmap.v;;
+\end{lstlisting}
+\end{columns}
+\end{frame}
+
+\begin{frame}[fragile]{\subsubsecname{} -- Example 2/2}
+\hfil\begin{tabular}{cc}
+\fbox{\hbox to 5cm {\lstinputlisting[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExAdv/macc_simple_test_01.v}}} &
+\fbox{\hbox to 5cm {\lstinputlisting[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExAdv/macc_simple_test_02.v}}} \\
+$\downarrow$ & $\downarrow$ \\
+\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_simple_test_01a.pdf}} &
+\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_simple_test_02a.pdf}} \\
+$\downarrow$ & $\downarrow$ \\
+\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_simple_test_01b.pdf}} &
+\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_simple_test_02b.pdf}} \\
+\end{tabular}
+\end{frame}
+
+\subsubsection{The wrap-extract-unwrap method}
+
+\begin{frame}{\subsubsecname}
+\scriptsize
+Often a coarse-grain element has a constant bit-width, but can be used to
+implement operations with a smaller bit-width. For example, a 18x25-bit multiplier
+can also be used to implement 16x20-bit multiplication.
+
+\bigskip
+A way of mapping such elements in coarse grain synthesis is the wrap-extract-unwrap method:
+
+\begin{itemize}
+\item {\bf wrap} \\
+Identify candidate-cells in the circuit and wrap them in a cell with a constant
+wider bit-width using {\tt techmap}. The wrappers use the same parameters as the original cell, so
+the information about the original width of the ports is preserved. \\
+Then use the {\tt connwrappers} command to connect up the bit-extended in- and
+outputs of the wrapper cells.
+\item {\bf extract} \\
+Now all operations are encoded using the same bit-width as the coarse grain element. The {\tt
+extract} command can be used to replace circuits with cells of the target architecture.
+\item {\bf unwrap} \\
+The remaining wrapper cell can be unwrapped using {\tt techmap}.
+\end{itemize}
+
+\bigskip
+The following sides detail an example that shows how to map MACC operations of
+arbitrary size to MACC cells with a 18x25-bit multiplier and a 48-bit adder (such as
+the Xilinx DSP48 cells).
+\end{frame}
+
+\subsubsection{Example: DSP48\_MACC}
+
+\begin{frame}[t, fragile]{\subsubsecname{} -- 1/13}
+Preconditioning: {\tt macc\_xilinx\_swap\_map.v} \\
+Make sure {\tt A} is the smaller port on all multipliers
+
+\begin{columns}
+\column{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, lastline=15]{PRESENTATION_ExAdv/macc_xilinx_swap_map.v}
+\column{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=16]{PRESENTATION_ExAdv/macc_xilinx_swap_map.v}
+\end{columns}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsubsecname{} -- 2/13}
+Wrapping multipliers: {\tt macc\_xilinx\_wrap\_map.v}
+
+\begin{columns}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, lastline=23]{PRESENTATION_ExAdv/macc_xilinx_wrap_map.v}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=24, lastline=46]{PRESENTATION_ExAdv/macc_xilinx_wrap_map.v}
+\end{columns}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsubsecname{} -- 3/13}
+Wrapping adders: {\tt macc\_xilinx\_wrap\_map.v}
+
+\begin{columns}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=48, lastline=67]{PRESENTATION_ExAdv/macc_xilinx_wrap_map.v}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=68, lastline=89]{PRESENTATION_ExAdv/macc_xilinx_wrap_map.v}
+\end{columns}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsubsecname{} -- 4/13}
+Extract: {\tt macc\_xilinx\_xmap.v}
+
+\lstinputlisting[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=1, lastline=17]{PRESENTATION_ExAdv/macc_xilinx_xmap.v}
+
+.. simply use the same wrapping commands on this module as on the design to create a template for the {\tt extract} command.
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsubsecname{} -- 5/13}
+Unwrapping multipliers: {\tt macc\_xilinx\_unwrap\_map.v}
+
+\begin{columns}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=1, lastline=17]{PRESENTATION_ExAdv/macc_xilinx_unwrap_map.v}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=18, lastline=30]{PRESENTATION_ExAdv/macc_xilinx_unwrap_map.v}
+\end{columns}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsubsecname{} -- 6/13}
+Unwrapping adders: {\tt macc\_xilinx\_unwrap\_map.v}
+
+\begin{columns}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=32, lastline=48]{PRESENTATION_ExAdv/macc_xilinx_unwrap_map.v}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=49, lastline=61]{PRESENTATION_ExAdv/macc_xilinx_unwrap_map.v}
+\end{columns}
+\end{frame}
+
+\begin{frame}[fragile]{\subsubsecname{} -- 7/13}
+\hfil\begin{tabular}{cc}
+{\tt test1} & {\tt test2} \\
+\fbox{\hbox to 5cm {\lstinputlisting[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, firstline=1, lastline=6, language=verilog]{PRESENTATION_ExAdv/macc_xilinx_test.v}}} &
+\fbox{\hbox to 5cm {\lstinputlisting[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, firstline=8, lastline=13, language=verilog]{PRESENTATION_ExAdv/macc_xilinx_test.v}}} \\
+$\downarrow$ & $\downarrow$ \\
+\end{tabular}
+\vskip-0.5cm
+\begin{lstlisting}[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+ read_verilog macc_xilinx_test.v
+ hierarchy -check
+\end{lstlisting}
+\vskip-0.5cm
+\hfil\begin{tabular}{cc}
+$\downarrow$ & $\downarrow$ \\
+\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test1a.pdf}} &
+\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2a.pdf}} \\
+\end{tabular}
+\end{frame}
+
+\begin{frame}[fragile]{\subsubsecname{} -- 8/13}
+\hfil\begin{tabular}{cc}
+{\tt test1} & {\tt test2} \\
+\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test1a.pdf}} &
+\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2a.pdf}} \\
+$\downarrow$ & $\downarrow$ \\
+\end{tabular}
+\vskip-0.2cm
+\begin{lstlisting}[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+ techmap -map macc_xilinx_swap_map.v ;;
+\end{lstlisting}
+\vskip-0.2cm
+\hfil\begin{tabular}{cc}
+$\downarrow$ & $\downarrow$ \\
+\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test1b.pdf}} &
+\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2b.pdf}} \\
+\end{tabular}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsubsecname{} -- 9/13}
+Wrapping in {\tt test1}:
+\begin{columns}
+\column[t]{5cm}
+\vbox to 0cm{\fbox{\includegraphics[width=4.5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test1b.pdf}}\vss}
+\column[t]{6cm}
+\begin{lstlisting}[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+techmap -map macc_xilinx_wrap_map.v
+
+connwrappers -unsigned $__mul_wrapper \
+ Y Y_WIDTH \
+ -unsigned $__add_wrapper \
+ Y Y_WIDTH ;;
+\end{lstlisting}
+\end{columns}
+
+\vskip1cm
+\hfil\includegraphics[width=\linewidth,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test1c.pdf}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsubsecname{} -- 10/13}
+Wrapping in {\tt test2}:
+\begin{columns}
+\column[t]{5cm}
+\vbox to 0cm{\fbox{\includegraphics[width=4.5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2b.pdf}}\vss}
+\column[t]{6cm}
+\begin{lstlisting}[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+techmap -map macc_xilinx_wrap_map.v
+
+connwrappers -unsigned $__mul_wrapper \
+ Y Y_WIDTH \
+ -unsigned $__add_wrapper \
+ Y Y_WIDTH ;;
+\end{lstlisting}
+\end{columns}
+
+\vskip1cm
+\hfil\includegraphics[width=\linewidth,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2c.pdf}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsubsecname{} -- 11/13}
+Extract in {\tt test1}:
+\begin{columns}
+\column[t]{4.5cm}
+\vbox to 0cm{
+\begin{lstlisting}[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+design -push
+read_verilog macc_xilinx_xmap.v
+techmap -map macc_xilinx_swap_map.v
+techmap -map macc_xilinx_wrap_map.v;;
+design -save __macc_xilinx_xmap
+design -pop
+\end{lstlisting}
+\vss}
+\column[t]{5.5cm}
+\vskip-1cm
+\begin{lstlisting}[linewidth=5.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+extract -constports -ignore_parameters \
+ -map %__macc_xilinx_xmap \
+ -swap $__add_wrapper A,B ;;
+\end{lstlisting}
+\vbox to 0cm{\fbox{\includegraphics[width=4.5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test1c.pdf}}\vss}
+\end{columns}
+
+\vskip2cm
+\hfil\includegraphics[width=11cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test1d.pdf}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsubsecname{} -- 12/13}
+Extract in {\tt test2}:
+\begin{columns}
+\column[t]{4.5cm}
+\vbox to 0cm{
+\begin{lstlisting}[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+design -push
+read_verilog macc_xilinx_xmap.v
+techmap -map macc_xilinx_swap_map.v
+techmap -map macc_xilinx_wrap_map.v;;
+design -save __macc_xilinx_xmap
+design -pop
+\end{lstlisting}
+\vss}
+\column[t]{5.5cm}
+\vskip-1cm
+\begin{lstlisting}[linewidth=5.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+extract -constports -ignore_parameters \
+ -map %__macc_xilinx_xmap \
+ -swap $__add_wrapper A,B ;;
+\end{lstlisting}
+\vbox to 0cm{\fbox{\includegraphics[width=4.5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2c.pdf}}\vss}
+\end{columns}
+
+\vskip2cm
+\hfil\includegraphics[width=11cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2d.pdf}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsubsecname{} -- 13/13}
+Unwrap in {\tt test2}:
+
+\hfil\begin{tikzpicture}
+\node at (0,0) {\includegraphics[width=11cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2d.pdf}};
+\node at (0,-4) {\includegraphics[width=8cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2e.pdf}};
+\node at (1,-1.7) {\begin{lstlisting}[linewidth=5.5cm, frame=single, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+techmap -map macc_xilinx_unwrap_map.v ;;
+\end{lstlisting}};
+\draw[-latex] (4,-0.7) .. controls (5,-1.7) .. (4,-2.7);
+\end{tikzpicture}
+\end{frame}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Automatic design changes}
+
+\begin{frame}
+\subsectionpage
+\subsectionpagesuffix
+\end{frame}
+
+\subsubsection{Changing the design from Yosys}
+
+\begin{frame}{\subsubsecname}
+Yosys commands can be used to change the design in memory. Examples of this are:
+
+\begin{itemize}
+\item {\bf Changes in design hierarchy} \\
+Commands such as {\tt flatten} and {\tt submod} can be used to change the design hierarchy, i.e.
+flatten the hierarchy or moving parts of a module to a submodule. This has applications in synthesis
+scripts as well as in reverse engineering and analysis.
+
+\item {\bf Behavioral changes} \\
+Commands such as {\tt techmap} can be used to make behavioral changes to the design, for example
+changing asynchronous resets to synchronous resets. This has applications in design space exploration
+(evaluation of various architectures for one circuit).
+\end{itemize}
+\end{frame}
+
+\subsubsection{Example: Async reset to sync reset}
+
+\begin{frame}[t, fragile]{\subsubsecname}
+The following techmap map file replaces all positive-edge async reset flip-flops with
+positive-edge sync reset flip-flops. The code is taken from the example Yosys script
+for ASIC synthesis of the Amber ARMv2 CPU.
+
+\begin{columns}
+\column[t]{6cm}
+\vbox to 0cm{
+\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=Verilog]
+(* techmap_celltype = "$adff" *)
+module adff2dff (CLK, ARST, D, Q);
+
+ parameter WIDTH = 1;
+ parameter CLK_POLARITY = 1;
+ parameter ARST_POLARITY = 1;
+ parameter ARST_VALUE = 0;
+
+ input CLK, ARST;
+ input [WIDTH-1:0] D;
+ output reg [WIDTH-1:0] Q;
+
+ wire [1023:0] _TECHMAP_DO_ = "proc";
+
+ wire _TECHMAP_FAIL_ = !CLK_POLARITY || !ARST_POLARITY;
+\end{lstlisting}
+\vss}
+\column[t]{4cm}
+\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=Verilog]
+// ..continued..
+
+
+ always @(posedge CLK)
+ if (ARST)
+ Q <= ARST_VALUE;
+ else
+ <= D;
+
+endmodule
+\end{lstlisting}
+\end{columns}
+
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Summary}
+
+\begin{frame}{\subsecname}
+\begin{itemize}
+\item A lot can be achieved in Yosys just with the standard set of commands.
+\item The commands {\tt techmap} and {\tt extract} can be used to prototype many complex synthesis tasks.
+\end{itemize}
+
+\bigskip
+\bigskip
+\begin{center}
+Questions?
+\end{center}
+
+\bigskip
+\bigskip
+\begin{center}
+\url{http://www.clifford.at/yosys/}
+\end{center}
+\end{frame}
+
diff --git a/manual/PRESENTATION_ExAdv/.gitignore b/manual/PRESENTATION_ExAdv/.gitignore
new file mode 100644
index 00000000..cf658897
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/.gitignore
@@ -0,0 +1 @@
+*.dot
diff --git a/manual/PRESENTATION_ExAdv/Makefile b/manual/PRESENTATION_ExAdv/Makefile
new file mode 100644
index 00000000..993a9d9e
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/Makefile
@@ -0,0 +1,28 @@
+
+all: select.pdf red_or3x1.pdf sym_mul.pdf mymul.pdf mulshift.pdf addshift.pdf \
+ macc_simple_xmap.pdf macc_xilinx_xmap.pdf
+
+select.pdf: select.v select.ys
+ ../../yosys select.ys
+
+red_or3x1.pdf: red_or3x1_*
+ ../../yosys red_or3x1_test.ys
+
+sym_mul.pdf: sym_mul_*
+ ../../yosys sym_mul_test.ys
+
+mymul.pdf: mymul_*
+ ../../yosys mymul_test.ys
+
+mulshift.pdf: mulshift_*
+ ../../yosys mulshift_test.ys
+
+addshift.pdf: addshift_*
+ ../../yosys addshift_test.ys
+
+macc_simple_xmap.pdf: macc_simple_*.v macc_simple_test.ys
+ ../../yosys macc_simple_test.ys
+
+macc_xilinx_xmap.pdf: macc_xilinx_*.v macc_xilinx_test.ys
+ ../../yosys macc_xilinx_test.ys
+
diff --git a/manual/PRESENTATION_ExAdv/addshift_map.v b/manual/PRESENTATION_ExAdv/addshift_map.v
new file mode 100644
index 00000000..13ecf0ba
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/addshift_map.v
@@ -0,0 +1,20 @@
+module \$add (A, B, Y);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] Y;
+
+ parameter _TECHMAP_BITS_CONNMAP_ = 0;
+ parameter _TECHMAP_CONNMAP_A_ = 0;
+ parameter _TECHMAP_CONNMAP_B_ = 0;
+
+ wire _TECHMAP_FAIL_ = A_WIDTH != B_WIDTH || B_WIDTH < Y_WIDTH ||
+ _TECHMAP_CONNMAP_A_ != _TECHMAP_CONNMAP_B_;
+
+ assign Y = A << 1;
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/addshift_test.v b/manual/PRESENTATION_ExAdv/addshift_test.v
new file mode 100644
index 00000000..b53271fa
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/addshift_test.v
@@ -0,0 +1,5 @@
+module test (A, B, X, Y);
+input [7:0] A, B;
+output [7:0] X = A + B;
+output [7:0] Y = A + A;
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/addshift_test.ys b/manual/PRESENTATION_ExAdv/addshift_test.ys
new file mode 100644
index 00000000..c08f1106
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/addshift_test.ys
@@ -0,0 +1,6 @@
+read_verilog addshift_test.v
+hierarchy -check -top test
+
+techmap -map addshift_map.v;;
+
+show -prefix addshift -format pdf -notitle
diff --git a/manual/PRESENTATION_ExAdv/macc_simple_test.v b/manual/PRESENTATION_ExAdv/macc_simple_test.v
new file mode 100644
index 00000000..6358a47c
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/macc_simple_test.v
@@ -0,0 +1,6 @@
+module test(a, b, c, d, y);
+input [15:0] a, b;
+input [31:0] c, d;
+output [31:0] y;
+assign y = a * b + c + d;
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/macc_simple_test.ys b/manual/PRESENTATION_ExAdv/macc_simple_test.ys
new file mode 100644
index 00000000..8d106a28
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/macc_simple_test.ys
@@ -0,0 +1,37 @@
+read_verilog macc_simple_test.v
+hierarchy -check -top test;;
+
+show -prefix macc_simple_test_00a -format pdf -notitle -lib macc_simple_xmap.v
+
+extract -constports -map macc_simple_xmap.v;;
+show -prefix macc_simple_test_00b -format pdf -notitle -lib macc_simple_xmap.v
+
+#################################################
+
+design -reset
+read_verilog macc_simple_test_01.v
+hierarchy -check -top test;;
+
+show -prefix macc_simple_test_01a -format pdf -notitle -lib macc_simple_xmap.v
+
+extract -map macc_simple_xmap.v;;
+show -prefix macc_simple_test_01b -format pdf -notitle -lib macc_simple_xmap.v
+
+#################################################
+
+design -reset
+read_verilog macc_simple_test_02.v
+hierarchy -check -top test;;
+
+show -prefix macc_simple_test_02a -format pdf -notitle -lib macc_simple_xmap.v
+
+extract -map macc_simple_xmap.v;;
+show -prefix macc_simple_test_02b -format pdf -notitle -lib macc_simple_xmap.v
+
+#################################################
+
+design -reset
+read_verilog macc_simple_xmap.v
+hierarchy -check -top macc_16_16_32;;
+
+show -prefix macc_simple_xmap -format pdf -notitle
diff --git a/manual/PRESENTATION_ExAdv/macc_simple_test_01.v b/manual/PRESENTATION_ExAdv/macc_simple_test_01.v
new file mode 100644
index 00000000..8391fb38
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/macc_simple_test_01.v
@@ -0,0 +1,6 @@
+module test(a, b, c, d, x, y);
+input [15:0] a, b, c, d;
+input [31:0] x;
+output [31:0] y;
+assign y = a*b + c*d + x;
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/macc_simple_test_02.v b/manual/PRESENTATION_ExAdv/macc_simple_test_02.v
new file mode 100644
index 00000000..3630102f
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/macc_simple_test_02.v
@@ -0,0 +1,6 @@
+module test(a, b, c, d, x, y);
+input [15:0] a, b, c, d;
+input [31:0] x;
+output [31:0] y;
+assign y = a*b + (c*d + x);
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/macc_simple_xmap.v b/manual/PRESENTATION_ExAdv/macc_simple_xmap.v
new file mode 100644
index 00000000..42f5bae9
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/macc_simple_xmap.v
@@ -0,0 +1,6 @@
+module macc_16_16_32(a, b, c, y);
+input [15:0] a, b;
+input [31:0] c;
+output [31:0] y;
+assign y = a*b + c;
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/macc_xilinx_swap_map.v b/manual/PRESENTATION_ExAdv/macc_xilinx_swap_map.v
new file mode 100644
index 00000000..e3696722
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/macc_xilinx_swap_map.v
@@ -0,0 +1,28 @@
+(* techmap_celltype = "$mul" *)
+module mul_swap_ports (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 1;
+parameter B_WIDTH = 1;
+parameter Y_WIDTH = 1;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+wire _TECHMAP_FAIL_ = A_WIDTH <= B_WIDTH;
+
+\$mul #(
+ .A_SIGNED(B_SIGNED),
+ .B_SIGNED(A_SIGNED),
+ .A_WIDTH(B_WIDTH),
+ .B_WIDTH(A_WIDTH),
+ .Y_WIDTH(Y_WIDTH)
+) _TECHMAP_REPLACE_ (
+ .A(B),
+ .B(A),
+ .Y(Y)
+);
+
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/macc_xilinx_test.v b/manual/PRESENTATION_ExAdv/macc_xilinx_test.v
new file mode 100644
index 00000000..683d9d84
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/macc_xilinx_test.v
@@ -0,0 +1,13 @@
+module test1(a, b, c, d, e, f, y);
+ input [19:0] a, b, c;
+ input [15:0] d, e, f;
+ output [41:0] y;
+ assign y = a*b + c*d + e*f;
+endmodule
+
+module test2(a, b, c, d, e, f, y);
+ input [19:0] a, b, c;
+ input [15:0] d, e, f;
+ output [41:0] y;
+ assign y = a*b + (c*d + e*f);
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/macc_xilinx_test.ys b/manual/PRESENTATION_ExAdv/macc_xilinx_test.ys
new file mode 100644
index 00000000..f3e8af4f
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/macc_xilinx_test.ys
@@ -0,0 +1,43 @@
+read_verilog macc_xilinx_test.v
+read_verilog -lib -icells macc_xilinx_unwrap_map.v
+read_verilog -lib -icells macc_xilinx_xmap.v
+hierarchy -check ;;
+
+show -prefix macc_xilinx_test1a -format pdf -notitle test1
+show -prefix macc_xilinx_test2a -format pdf -notitle test2
+
+techmap -map macc_xilinx_swap_map.v;;
+
+show -prefix macc_xilinx_test1b -format pdf -notitle test1
+show -prefix macc_xilinx_test2b -format pdf -notitle test2
+
+techmap -map macc_xilinx_wrap_map.v
+
+connwrappers -unsigned $__mul_wrapper Y Y_WIDTH \
+ -unsigned $__add_wrapper Y Y_WIDTH;;
+
+show -prefix macc_xilinx_test1c -format pdf -notitle test1
+show -prefix macc_xilinx_test2c -format pdf -notitle test2
+
+design -push
+read_verilog macc_xilinx_xmap.v
+techmap -map macc_xilinx_swap_map.v
+techmap -map macc_xilinx_wrap_map.v;;
+design -save __macc_xilinx_xmap
+design -pop
+
+extract -constports -ignore_parameters \
+ -map %__macc_xilinx_xmap \
+ -swap $__add_wrapper A,B ;;
+
+show -prefix macc_xilinx_test1d -format pdf -notitle test1
+show -prefix macc_xilinx_test2d -format pdf -notitle test2
+
+techmap -map macc_xilinx_unwrap_map.v;;
+
+show -prefix macc_xilinx_test1e -format pdf -notitle test1
+show -prefix macc_xilinx_test2e -format pdf -notitle test2
+
+design -load __macc_xilinx_xmap
+show -prefix macc_xilinx_xmap -format pdf -notitle
+
diff --git a/manual/PRESENTATION_ExAdv/macc_xilinx_unwrap_map.v b/manual/PRESENTATION_ExAdv/macc_xilinx_unwrap_map.v
new file mode 100644
index 00000000..9dfaef13
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/macc_xilinx_unwrap_map.v
@@ -0,0 +1,61 @@
+module \$__mul_wrapper (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 1;
+parameter B_WIDTH = 1;
+parameter Y_WIDTH = 1;
+
+input [17:0] A;
+input [24:0] B;
+output [47:0] Y;
+
+wire [A_WIDTH-1:0] A_ORIG = A;
+wire [B_WIDTH-1:0] B_ORIG = B;
+wire [Y_WIDTH-1:0] Y_ORIG;
+assign Y = Y_ORIG;
+
+\$mul #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(B_WIDTH),
+ .Y_WIDTH(Y_WIDTH)
+) _TECHMAP_REPLACE_ (
+ .A(A_ORIG),
+ .B(B_ORIG),
+ .Y(Y_ORIG)
+);
+
+endmodule
+
+module \$__add_wrapper (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 1;
+parameter B_WIDTH = 1;
+parameter Y_WIDTH = 1;
+
+input [47:0] A;
+input [47:0] B;
+output [47:0] Y;
+
+wire [A_WIDTH-1:0] A_ORIG = A;
+wire [B_WIDTH-1:0] B_ORIG = B;
+wire [Y_WIDTH-1:0] Y_ORIG;
+assign Y = Y_ORIG;
+
+\$add #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(B_WIDTH),
+ .Y_WIDTH(Y_WIDTH)
+) _TECHMAP_REPLACE_ (
+ .A(A_ORIG),
+ .B(B_ORIG),
+ .Y(Y_ORIG)
+);
+
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/macc_xilinx_wrap_map.v b/manual/PRESENTATION_ExAdv/macc_xilinx_wrap_map.v
new file mode 100644
index 00000000..f23f6c02
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/macc_xilinx_wrap_map.v
@@ -0,0 +1,89 @@
+(* techmap_celltype = "$mul" *)
+module mul_wrap (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 1;
+parameter B_WIDTH = 1;
+parameter Y_WIDTH = 1;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+wire [17:0] A_18 = A;
+wire [24:0] B_25 = B;
+wire [47:0] Y_48;
+assign Y = Y_48;
+
+wire [1023:0] _TECHMAP_DO_ = "proc; clean";
+
+reg _TECHMAP_FAIL_;
+initial begin
+ _TECHMAP_FAIL_ <= 0;
+ if (A_SIGNED || B_SIGNED)
+ _TECHMAP_FAIL_ <= 1;
+ if (A_WIDTH < 4 || B_WIDTH < 4)
+ _TECHMAP_FAIL_ <= 1;
+ if (A_WIDTH > 18 || B_WIDTH > 25)
+ _TECHMAP_FAIL_ <= 1;
+ if (A_WIDTH*B_WIDTH < 100)
+ _TECHMAP_FAIL_ <= 1;
+end
+
+\$__mul_wrapper #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(B_WIDTH),
+ .Y_WIDTH(Y_WIDTH)
+) _TECHMAP_REPLACE_ (
+ .A(A_18),
+ .B(B_25),
+ .Y(Y_48)
+);
+
+endmodule
+
+(* techmap_celltype = "$add" *)
+module add_wrap (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 1;
+parameter B_WIDTH = 1;
+parameter Y_WIDTH = 1;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+wire [47:0] A_48 = A;
+wire [47:0] B_48 = B;
+wire [47:0] Y_48;
+assign Y = Y_48;
+
+wire [1023:0] _TECHMAP_DO_ = "proc; clean";
+
+reg _TECHMAP_FAIL_;
+initial begin
+ _TECHMAP_FAIL_ <= 0;
+ if (A_SIGNED || B_SIGNED)
+ _TECHMAP_FAIL_ <= 1;
+ if (A_WIDTH < 10 && B_WIDTH < 10)
+ _TECHMAP_FAIL_ <= 1;
+end
+
+\$__add_wrapper #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(B_WIDTH),
+ .Y_WIDTH(Y_WIDTH)
+) _TECHMAP_REPLACE_ (
+ .A(A_48),
+ .B(B_48),
+ .Y(Y_48)
+);
+
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/macc_xilinx_xmap.v b/manual/PRESENTATION_ExAdv/macc_xilinx_xmap.v
new file mode 100644
index 00000000..06372f5a
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/macc_xilinx_xmap.v
@@ -0,0 +1,10 @@
+module DSP48_MACC (a, b, c, y);
+
+input [17:0] a;
+input [24:0] b;
+input [47:0] c;
+output [47:0] y;
+
+assign y = a*b + c;
+
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/mulshift_map.v b/manual/PRESENTATION_ExAdv/mulshift_map.v
new file mode 100644
index 00000000..4a3c2a06
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/mulshift_map.v
@@ -0,0 +1,26 @@
+module MYMUL(A, B, Y);
+ parameter WIDTH = 1;
+ input [WIDTH-1:0] A, B;
+ output reg [WIDTH-1:0] Y;
+
+ parameter _TECHMAP_CONSTVAL_A_ = WIDTH'bx;
+ parameter _TECHMAP_CONSTVAL_B_ = WIDTH'bx;
+
+ reg _TECHMAP_FAIL_;
+ wire [1023:0] _TECHMAP_DO_ = "proc; clean";
+
+ integer i;
+ always @* begin
+ _TECHMAP_FAIL_ <= 1;
+ for (i = 0; i < WIDTH; i=i+1) begin
+ if (_TECHMAP_CONSTVAL_A_ === WIDTH'd1 << i) begin
+ _TECHMAP_FAIL_ <= 0;
+ Y <= B << i;
+ end
+ if (_TECHMAP_CONSTVAL_B_ === WIDTH'd1 << i) begin
+ _TECHMAP_FAIL_ <= 0;
+ Y <= A << i;
+ end
+ end
+ end
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/mulshift_test.v b/manual/PRESENTATION_ExAdv/mulshift_test.v
new file mode 100644
index 00000000..4b975f41
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/mulshift_test.v
@@ -0,0 +1,5 @@
+module test (A, X, Y);
+input [7:0] A;
+output [7:0] X = A * 8'd 6;
+output [7:0] Y = A * 8'd 8;
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/mulshift_test.ys b/manual/PRESENTATION_ExAdv/mulshift_test.ys
new file mode 100644
index 00000000..c5dac49e
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/mulshift_test.ys
@@ -0,0 +1,7 @@
+read_verilog mulshift_test.v
+hierarchy -check -top test
+
+techmap -map sym_mul_map.v \
+ -map mulshift_map.v;;
+
+show -prefix mulshift -format pdf -notitle -lib sym_mul_cells.v
diff --git a/manual/PRESENTATION_ExAdv/mymul_map.v b/manual/PRESENTATION_ExAdv/mymul_map.v
new file mode 100644
index 00000000..e888a7a7
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/mymul_map.v
@@ -0,0 +1,15 @@
+module MYMUL(A, B, Y);
+ parameter WIDTH = 1;
+ input [WIDTH-1:0] A, B;
+ output reg [WIDTH-1:0] Y;
+
+ wire [1023:0] _TECHMAP_DO_ = "proc; clean";
+
+ integer i;
+ always @* begin
+ Y = 0;
+ for (i = 0; i < WIDTH; i=i+1)
+ if (A[i])
+ Y = Y + (B << i);
+ end
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/mymul_test.v b/manual/PRESENTATION_ExAdv/mymul_test.v
new file mode 100644
index 00000000..620a06d9
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/mymul_test.v
@@ -0,0 +1,4 @@
+module test(A, B, Y);
+ input [1:0] A, B;
+ output [1:0] Y = A * B;
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/mymul_test.ys b/manual/PRESENTATION_ExAdv/mymul_test.ys
new file mode 100644
index 00000000..48203e31
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/mymul_test.ys
@@ -0,0 +1,15 @@
+read_verilog mymul_test.v
+hierarchy -check -top test
+
+techmap -map sym_mul_map.v \
+ -map mymul_map.v;;
+
+rename test test_mapped
+read_verilog mymul_test.v
+miter -equiv test test_mapped miter
+flatten miter
+
+sat -verify -prove trigger 0 miter
+
+splitnets -ports test_mapped/A
+show -prefix mymul -format pdf -notitle test_mapped
diff --git a/manual/PRESENTATION_ExAdv/red_or3x1_cells.v b/manual/PRESENTATION_ExAdv/red_or3x1_cells.v
new file mode 100644
index 00000000..0750a130
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/red_or3x1_cells.v
@@ -0,0 +1,5 @@
+module OR3X1(A, B, C, Y);
+ input A, B, C;
+ output Y;
+ assign Y = A | B | C;
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/red_or3x1_map.v b/manual/PRESENTATION_ExAdv/red_or3x1_map.v
new file mode 100644
index 00000000..8c37b1db
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/red_or3x1_map.v
@@ -0,0 +1,48 @@
+module \$reduce_or (A, Y);
+
+ parameter A_SIGNED = 0;
+ parameter A_WIDTH = 0;
+ parameter Y_WIDTH = 0;
+
+ input [A_WIDTH-1:0] A;
+ output [Y_WIDTH-1:0] Y;
+
+ function integer min;
+ input integer a, b;
+ begin
+ if (a < b)
+ min = a;
+ else
+ min = b;
+ end
+ endfunction
+
+ genvar i;
+ generate begin
+ if (A_WIDTH == 0) begin
+ assign Y = 0;
+ end
+ if (A_WIDTH == 1) begin
+ assign Y = A;
+ end
+ if (A_WIDTH == 2) begin
+ wire ybuf;
+ OR3X1 g (.A(A[0]), .B(A[1]), .C(1'b0), .Y(ybuf));
+ assign Y = ybuf;
+ end
+ if (A_WIDTH == 3) begin
+ wire ybuf;
+ OR3X1 g (.A(A[0]), .B(A[1]), .C(A[2]), .Y(ybuf));
+ assign Y = ybuf;
+ end
+ if (A_WIDTH > 3) begin
+ localparam next_stage_sz = (A_WIDTH+2) / 3;
+ wire [next_stage_sz-1:0] next_stage;
+ for (i = 0; i < next_stage_sz; i = i+1) begin
+ localparam bits = min(A_WIDTH - 3*i, 3);
+ assign next_stage[i] = |A[3*i +: bits];
+ end
+ assign Y = |next_stage;
+ end
+ end endgenerate
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/red_or3x1_test.v b/manual/PRESENTATION_ExAdv/red_or3x1_test.v
new file mode 100644
index 00000000..bcdd32cb
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/red_or3x1_test.v
@@ -0,0 +1,5 @@
+module test (A, Y);
+ input [6:0] A;
+ output Y;
+ assign Y = |A;
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/red_or3x1_test.ys b/manual/PRESENTATION_ExAdv/red_or3x1_test.ys
new file mode 100644
index 00000000..b9234603
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/red_or3x1_test.ys
@@ -0,0 +1,7 @@
+read_verilog red_or3x1_test.v
+hierarchy -check -top test
+
+techmap -map red_or3x1_map.v;;
+
+splitnets -ports
+show -prefix red_or3x1 -format pdf -notitle -lib red_or3x1_cells.v
diff --git a/manual/PRESENTATION_ExAdv/select.v b/manual/PRESENTATION_ExAdv/select.v
new file mode 100644
index 00000000..1b0bb7ee
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/select.v
@@ -0,0 +1,15 @@
+module test(clk, s, a, y);
+ input clk, s;
+ input [15:0] a;
+ output [15:0] y;
+ reg [15:0] b, c;
+
+ always @(posedge clk) begin
+ b <= a;
+ c <= b;
+ end
+
+ wire [15:0] state_a = (a ^ b) + c;
+ wire [15:0] state_b = (a ^ b) - c;
+ assign y = !s ? state_a : state_b;
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/select.ys b/manual/PRESENTATION_ExAdv/select.ys
new file mode 100644
index 00000000..9832c104
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/select.ys
@@ -0,0 +1,10 @@
+read_verilog select.v
+hierarchy -check -top test
+proc; opt
+cd test
+select -set cone_a state_a %ci*:-$dff
+select -set cone_b state_b %ci*:-$dff
+select -set cone_ab @cone_a @cone_b %i
+show -prefix select -format pdf -notitle \
+ -color red @cone_ab -color magenta @cone_a \
+ -color blue @cone_b
diff --git a/manual/PRESENTATION_ExAdv/sym_mul_cells.v b/manual/PRESENTATION_ExAdv/sym_mul_cells.v
new file mode 100644
index 00000000..ce177154
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/sym_mul_cells.v
@@ -0,0 +1,6 @@
+module MYMUL(A, B, Y);
+ parameter WIDTH = 1;
+ input [WIDTH-1:0] A, B;
+ output [WIDTH-1:0] Y;
+ assign Y = A * B;
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/sym_mul_map.v b/manual/PRESENTATION_ExAdv/sym_mul_map.v
new file mode 100644
index 00000000..b4dbd9e0
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/sym_mul_map.v
@@ -0,0 +1,15 @@
+module \$mul (A, B, Y);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] Y;
+
+ wire _TECHMAP_FAIL_ = A_WIDTH != B_WIDTH || B_WIDTH != Y_WIDTH;
+
+ MYMUL #( .WIDTH(Y_WIDTH) ) g ( .A(A), .B(B), .Y(Y) );
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/sym_mul_test.v b/manual/PRESENTATION_ExAdv/sym_mul_test.v
new file mode 100644
index 00000000..eb715f83
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/sym_mul_test.v
@@ -0,0 +1,5 @@
+module test(A, B, C, Y1, Y2);
+ input [7:0] A, B, C;
+ output [7:0] Y1 = A * B;
+ output [15:0] Y2 = A * C;
+endmodule
diff --git a/manual/PRESENTATION_ExAdv/sym_mul_test.ys b/manual/PRESENTATION_ExAdv/sym_mul_test.ys
new file mode 100644
index 00000000..0c07e7e8
--- /dev/null
+++ b/manual/PRESENTATION_ExAdv/sym_mul_test.ys
@@ -0,0 +1,6 @@
+read_verilog sym_mul_test.v
+hierarchy -check -top test
+
+techmap -map sym_mul_map.v;;
+
+show -prefix sym_mul -format pdf -notitle -lib sym_mul_cells.v
diff --git a/manual/PRESENTATION_ExOth.tex b/manual/PRESENTATION_ExOth.tex
new file mode 100644
index 00000000..73f8bea2
--- /dev/null
+++ b/manual/PRESENTATION_ExOth.tex
@@ -0,0 +1,227 @@
+
+\section{Yosys by example -- Beyond Synthesis}
+
+\begin{frame}
+\sectionpage
+\end{frame}
+
+\begin{frame}{Overview}
+This section contains 2 subsections:
+\begin{itemize}
+\item Interactive Design Investigation
+\item Symbolic Model Checking
+\end{itemize}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Interactive Design Investigation}
+
+\begin{frame}
+\subsectionpage
+\subsectionpagesuffix
+\end{frame}
+
+\begin{frame}{\subsecname}
+Yosys can also be used to investigate designs (or netlists created
+from other tools).
+
+\begin{itemize}
+\item
+The selection mechanism (see slides ``Using Selections''), especially patterns such
+as {\tt \%ci} and {\tt \%co}, can be used to figure out how parts of the design
+are connected.
+
+\item
+Commands such as {\tt submod}, {\tt expose}, {\tt splice}, \dots can be used
+to transform the design into an equivalent design that is easier to analyse.
+
+\item
+Commands such as {\tt eval} and {\tt sat} can be used to investigate the
+behavior of the circuit.
+\end{itemize}
+\end{frame}
+
+\begin{frame}[t, fragile]{Example: Reorganizing a module}
+\begin{columns}
+\column[t]{4cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{6pt}{7pt}\selectfont, language=verilog]{PRESENTATION_ExOth/scrambler.v}
+\column[t]{7cm}
+\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]
+read_verilog scrambler.v
+
+hierarchy; proc;;
+
+cd scrambler
+submod -name xorshift32 \
+ xs %c %ci %D %c %ci:+[D] %D \
+ %ci*:-$dff xs %co %ci %d
+\end{lstlisting}
+\end{columns}
+
+\hfil\includegraphics[width=11cm,trim=0 0cm 0 1.5cm]{PRESENTATION_ExOth/scrambler_p01.pdf}
+
+\hfil\includegraphics[width=11cm,trim=0 0cm 0 2cm]{PRESENTATION_ExOth/scrambler_p02.pdf}
+\end{frame}
+
+\begin{frame}[t, fragile]{Example: Analysis of circuit behavior}
+\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+> read_verilog scrambler.v
+> hierarchy; proc;; cd scrambler
+> submod -name xorshift32 xs %c %ci %D %c %ci:+[D] %D %ci*:-$dff xs %co %ci %d
+
+> cd xorshift32
+> rename n2 in
+> rename n1 out
+
+> eval -set in 1 -show out
+Eval result: \out = 270369.
+
+> eval -set in 270369 -show out
+Eval result: \out = 67634689.
+
+> sat -set out 632435482
+Signal Name Dec Hex Bin
+-------------------- ---------- ---------- -------------------------------------
+\in 745495504 2c6f5bd0 00101100011011110101101111010000
+\out 632435482 25b2331a 00100101101100100011001100011010
+\end{lstlisting}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Symbolic Model Checking}
+
+\begin{frame}
+\subsectionpage
+\subsectionpagesuffix
+\end{frame}
+
+\begin{frame}{\subsecname}
+Symbolic Model Checking (SMC) is used to formally prove that a circuit has
+(or has not) a given property.
+
+\bigskip
+One application is Formal Equivalence Checking: Proving that two circuits
+are identical. For example this is a very useful feature when debugging custom
+passes in Yosys.
+
+\bigskip
+Other applications include checking if a module conforms to interface
+standards.
+
+\bigskip
+The {\tt sat} command in Yosys can be used to perform Symbolic Model Checking.
+\end{frame}
+
+\begin{frame}[t]{Example: Formal Equivalence Checking (1/2)}
+Remember the following example?
+\vskip1em
+
+\vbox to 0cm{
+\vskip-0.3cm
+\lstinputlisting[basicstyle=\ttfamily\fontsize{6pt}{7pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/techmap_01_map.v}
+}\vbox to 0cm{
+\vskip-0.5cm
+\lstinputlisting[xleftmargin=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=verilog]{PRESENTATION_ExSyn/techmap_01.v}
+\lstinputlisting[xleftmargin=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/techmap_01.ys}}
+
+\vskip5cm\hskip5cm
+Lets see if it is correct..
+\end{frame}
+
+\begin{frame}[t, fragile]{Example: Formal Equivalence Checking (2/2)}
+\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]
+# read test design
+read_verilog techmap_01.v
+hierarchy -top test
+
+# create two version of the design: test_orig and test_mapped
+copy test test_orig
+rename test test_mapped
+
+# apply the techmap only to test_mapped
+techmap -map techmap_01_map.v test_mapped
+
+# create a miter circuit to test equivalence
+miter -equiv -make_assert -make_outputs test_orig test_mapped miter
+flatten miter
+
+# run equivalence check
+sat -verify -prove-asserts -show-inputs -show-outputs miter
+\end{lstlisting}
+
+\dots
+\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont]
+Solving problem with 945 variables and 2505 clauses..
+SAT proof finished - no model found: SUCCESS!
+\end{lstlisting}
+\end{frame}
+
+\begin{frame}[t, fragile]{Example: Symbolic Model Checking (1/2)}
+\small
+The following AXI4 Stream Master has a bug. But the bug is not exposed if the
+slave keeps {\tt tready} asserted all the time. (Something a test bench might do.)
+
+\medskip
+Symbolic Model Checking can be used to expose the bug and find a sequence
+of values for {\tt tready} that yield the incorrect behavior.
+
+\vskip-1em
+\begin{columns}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{5pt}{6pt}\selectfont, language=verilog]{PRESENTATION_ExOth/axis_master.v}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{5pt}{6pt}\selectfont, language=verilog]{PRESENTATION_ExOth/axis_test.v}
+\end{columns}
+\end{frame}
+
+\begin{frame}[t, fragile]{Example: Symbolic Model Checking (2/2)}
+\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]
+read_verilog -sv axis_master.v axis_test.v
+hierarchy -top axis_test
+
+proc; flatten;;
+sat -seq 50 -prove-asserts
+\end{lstlisting}
+
+\bigskip
+\dots with unmodified {\tt axis\_master.v}:
+\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont]
+Solving problem with 159344 variables and 442126 clauses..
+SAT proof finished - model found: FAIL!
+\end{lstlisting}
+
+\bigskip
+\dots with fixed {\tt axis\_master.v}:
+\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont]
+Solving problem with 159144 variables and 441626 clauses..
+SAT proof finished - no model found: SUCCESS!
+\end{lstlisting}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Summary}
+
+\begin{frame}{\subsecname}
+\begin{itemize}
+\item Yosys provides useful features beyond synthesis.
+\item The commands {\tt sat} and {\tt eval} can be used to analyse the behavior of a circuit.
+\item The {\tt sat} command can also be used for symbolic model checking.
+\item This can be useful for debugging and testing designs and Yosys extensions alike.
+\end{itemize}
+
+\bigskip
+\bigskip
+\begin{center}
+Questions?
+\end{center}
+
+\bigskip
+\bigskip
+\begin{center}
+\url{http://www.clifford.at/yosys/}
+\end{center}
+\end{frame}
+
diff --git a/manual/PRESENTATION_ExOth/.gitignore b/manual/PRESENTATION_ExOth/.gitignore
new file mode 100644
index 00000000..cf658897
--- /dev/null
+++ b/manual/PRESENTATION_ExOth/.gitignore
@@ -0,0 +1 @@
+*.dot
diff --git a/manual/PRESENTATION_ExOth/Makefile b/manual/PRESENTATION_ExOth/Makefile
new file mode 100644
index 00000000..4864d8d5
--- /dev/null
+++ b/manual/PRESENTATION_ExOth/Makefile
@@ -0,0 +1,16 @@
+
+all: scrambler_p01.pdf scrambler_p02.pdf equiv.log axis_test.log
+
+scrambler_p01.pdf: scrambler.ys scrambler.v
+ ../../yosys scrambler.ys
+
+scrambler_p02.pdf: scrambler_p01.pdf
+
+equiv.log: equiv.ys
+ ../../yosys -l equiv.log_new equiv.ys
+ mv equiv.log_new equiv.log
+
+axis_test.log: axis_test.ys axis_master.v axis_test.v
+ ../../yosys -l axis_test.log_new axis_test.ys
+ mv axis_test.log_new axis_test.log
+
diff --git a/manual/PRESENTATION_ExOth/axis_master.v b/manual/PRESENTATION_ExOth/axis_master.v
new file mode 100644
index 00000000..fe9008ad
--- /dev/null
+++ b/manual/PRESENTATION_ExOth/axis_master.v
@@ -0,0 +1,27 @@
+module axis_master(aclk, aresetn, tvalid, tready, tdata);
+ input aclk, aresetn, tready;
+ output reg tvalid;
+ output reg [7:0] tdata;
+
+ reg [31:0] state;
+ always @(posedge aclk) begin
+ if (!aresetn) begin
+ state <= 314159265;
+ tvalid <= 0;
+ tdata <= 'bx;
+ end else begin
+ if (tvalid && tready)
+ tvalid <= 0;
+ if (!tvalid || !tready) begin
+ // ^- should not be inverted!
+ state = state ^ state << 13;
+ state = state ^ state >> 7;
+ state = state ^ state << 17;
+ if (state[9:8] == 0) begin
+ tvalid <= 1;
+ tdata <= state;
+ end
+ end
+ end
+ end
+endmodule
diff --git a/manual/PRESENTATION_ExOth/axis_test.v b/manual/PRESENTATION_ExOth/axis_test.v
new file mode 100644
index 00000000..0be833f1
--- /dev/null
+++ b/manual/PRESENTATION_ExOth/axis_test.v
@@ -0,0 +1,27 @@
+module axis_test(aclk, tready);
+ input aclk, tready;
+ wire aresetn, tvalid;
+ wire [7:0] tdata;
+
+ integer counter = 0;
+ reg aresetn = 0;
+
+ axis_master uut (aclk, aresetn, tvalid, tready, tdata);
+
+ always @(posedge aclk) begin
+ if (aresetn && tready && tvalid) begin
+ if (counter == 0) assert(tdata == 19);
+ if (counter == 1) assert(tdata == 99);
+ if (counter == 2) assert(tdata == 1);
+ if (counter == 3) assert(tdata == 244);
+ if (counter == 4) assert(tdata == 133);
+ if (counter == 5) assert(tdata == 209);
+ if (counter == 6) assert(tdata == 241);
+ if (counter == 7) assert(tdata == 137);
+ if (counter == 8) assert(tdata == 176);
+ if (counter == 9) assert(tdata == 6);
+ counter <= counter + 1;
+ end
+ aresetn <= 1;
+ end
+endmodule
diff --git a/manual/PRESENTATION_ExOth/axis_test.ys b/manual/PRESENTATION_ExOth/axis_test.ys
new file mode 100644
index 00000000..19663ac7
--- /dev/null
+++ b/manual/PRESENTATION_ExOth/axis_test.ys
@@ -0,0 +1,5 @@
+read_verilog -sv axis_master.v axis_test.v
+hierarchy -top axis_test
+
+proc; flatten;;
+sat -falsify -seq 50 -prove-asserts
diff --git a/manual/PRESENTATION_ExOth/equiv.ys b/manual/PRESENTATION_ExOth/equiv.ys
new file mode 100644
index 00000000..8db0a88a
--- /dev/null
+++ b/manual/PRESENTATION_ExOth/equiv.ys
@@ -0,0 +1,17 @@
+# read test design
+read_verilog ../PRESENTATION_ExSyn/techmap_01.v
+hierarchy -top test
+
+# create two version of the design: test_orig and test_mapped
+copy test test_orig
+rename test test_mapped
+
+# apply the techmap only to test_mapped
+techmap -map ../PRESENTATION_ExSyn/techmap_01_map.v test_mapped
+
+# create a miter circuit to test equivalence
+miter -equiv -make_assert -make_outputs test_orig test_mapped miter
+flatten miter
+
+# run equivalence check
+sat -verify -prove-asserts -show-inputs -show-outputs miter
diff --git a/manual/PRESENTATION_ExOth/scrambler.v b/manual/PRESENTATION_ExOth/scrambler.v
new file mode 100644
index 00000000..d4c1fa2b
--- /dev/null
+++ b/manual/PRESENTATION_ExOth/scrambler.v
@@ -0,0 +1,14 @@
+module scrambler(
+ input clk, rst, in_bit,
+ output reg out_bit
+);
+ reg [31:0] xs;
+ always @(posedge clk) begin
+ if (rst)
+ xs = 1;
+ xs = xs ^ (xs << 13);
+ xs = xs ^ (xs >> 17);
+ xs = xs ^ (xs << 5);
+ out_bit <= in_bit ^ xs[0];
+ end
+endmodule
diff --git a/manual/PRESENTATION_ExOth/scrambler.ys b/manual/PRESENTATION_ExOth/scrambler.ys
new file mode 100644
index 00000000..2ef14c56
--- /dev/null
+++ b/manual/PRESENTATION_ExOth/scrambler.ys
@@ -0,0 +1,23 @@
+
+read_verilog scrambler.v
+
+hierarchy; proc;;
+
+cd scrambler
+submod -name xorshift32 xs %c %ci %D %c %ci:+[D] %D %ci*:-$dff xs %co %ci %d
+cd ..
+
+show -prefix scrambler_p01 -format pdf -notitle scrambler
+show -prefix scrambler_p02 -format pdf -notitle xorshift32
+
+echo on
+
+cd xorshift32
+rename n2 in
+rename n1 out
+
+eval -set in 1 -show out
+eval -set in 270369 -show out
+
+sat -set out 632435482
+
diff --git a/manual/PRESENTATION_ExSyn.tex b/manual/PRESENTATION_ExSyn.tex
new file mode 100644
index 00000000..655720eb
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn.tex
@@ -0,0 +1,515 @@
+
+\section{Yosys by example -- Synthesis}
+
+\begin{frame}
+\sectionpage
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Typical Phases of a Synthesis Flow}
+
+\begin{frame}{\subsecname}
+\begin{itemize}
+\item Reading and elaborating the design
+\item Higher-level synthesis and optimization
+\begin{itemize}
+\item Converting {\tt always}-blocks to logic and registers
+\item Perform coarse-grain optimizations (resource sharing, const folding, ...)
+\item Handling of memories and other coarse-grain blocks
+\item Extracting and optimizing finite state machines
+\end{itemize}
+\item Convert remaining logic to bit-level logic functions
+\item Perform optimizations on bit-level logic functions
+\item Map bit-level logic gates and registers to cell library
+\item Write results to output file
+\end{itemize}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Reading the design}
+
+\begin{frame}[fragile]{\subsecname}
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+read_verilog file1.v
+read_verilog -I include_dir -D enable_foo -D WIDTH=12 file2.v
+read_verilog -lib cell_library.v
+
+verilog_defaults -add -I include_dir
+read_verilog file3.v
+read_verilog file4.v
+verilog_defaults -clear
+
+verilog_defaults -push
+verilog_defaults -add -I include_dir
+read_verilog file5.v
+read_verilog file6.v
+verilog_defaults -pop
+\end{lstlisting}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Design elaboration}
+
+\begin{frame}[fragile]{\subsecname}
+During design elaboration Yosys figures out how the modules are hierarchically
+connected. It also re-runs the AST parts of the Verilog frontend to create
+all needed variations of parametric modules.
+
+\bigskip
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+# simplest form. at least this version should be used after reading all input files
+#
+hierarchy
+
+# recommended form. fails if parts of the design hierarchy are missing, removes
+# everything that is unreachable from the top module, and marks the top module.
+#
+hierarchy -check -top top_module
+\end{lstlisting}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{The {\tt proc} command}
+
+\begin{frame}[fragile]{\subsecname}
+The Verilog frontend converts {\tt always}-blocks to RTL netlists for the
+expressions and ``processes'' for the control- and memory elements.
+
+\medskip
+The {\tt proc} command transforms this ``processes'' to netlists of RTL
+multiplexer and register cells.
+
+\medskip
+The {\tt proc} command is actually a macro-command that calls the following
+other commands:
+
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+proc_clean # remove empty branches and processes
+proc_rmdead # remove unreachable branches
+proc_init # special handling of "initial" blocks
+proc_arst # identify modeling of async resets
+proc_mux # convert decision trees to multiplexer networks
+proc_dff # extract registers from processes
+proc_clean # if all went fine, this should remove all the processes
+\end{lstlisting}
+
+\medskip
+Many commands can not operate on modules with ``processes'' in them. Usually
+a call to {\tt proc} is the first command in the actual synthesis procedure
+after design elaboration.
+\end{frame}
+
+\begin{frame}[fragile]{\subsecname{} -- Example 1/3}
+\begin{columns}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/proc_01.v}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/proc_01.ys}
+\end{columns}
+\hfil\includegraphics[width=8cm,trim=0 0cm 0 0cm]{PRESENTATION_ExSyn/proc_01.pdf}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsecname{} -- Example 2/3}
+\vbox to 0cm{\includegraphics[width=\linewidth,trim=0cm 0cm 0cm -2.5cm]{PRESENTATION_ExSyn/proc_02.pdf}\vss}
+\vskip-1cm
+\begin{columns}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/proc_02.v}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/proc_02.ys}
+\end{columns}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsecname{} -- Example 3/3}
+\vbox to 0cm{\includegraphics[width=\linewidth,trim=0cm 0cm 0cm -1.5cm]{PRESENTATION_ExSyn/proc_03.pdf}\vss}
+\vskip-1cm
+\begin{columns}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/proc_03.ys}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/proc_03.v}
+\end{columns}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{The {\tt opt} command}
+
+\begin{frame}[fragile]{\subsecname}
+The {\tt opt} command implements a series of simple optimizations. It also
+is a macro command that calls other commands:
+
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+opt_expr # const folding and simple expression rewriting
+opt_merge -nomux # merging identical cells
+
+do
+ opt_muxtree # remove never-active branches from multiplexer tree
+ opt_reduce # consolidate trees of boolean ops to reduce functions
+ opt_merge # merging identical cells
+ opt_rmdff # remove/simplify registers with constant inputs
+ opt_clean # remove unused objects (cells, wires) from design
+ opt_expr # const folding and simple expression rewriting
+while [changed design]
+\end{lstlisting}
+
+The command {\tt clean} can be used as alias for {\tt opt\_clean}. And {\tt ;;}
+can be used as shortcut for {\tt clean}. For example:
+
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+proc; opt; memory; opt_expr;; fsm;;
+\end{lstlisting}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsecname{} -- Example 1/4}
+\vbox to 0cm{\includegraphics[width=\linewidth,trim=0cm 0cm 0cm -0.5cm]{PRESENTATION_ExSyn/opt_01.pdf}\vss}
+\vskip-1cm
+\begin{columns}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/opt_01.ys}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/opt_01.v}
+\end{columns}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsecname{} -- Example 2/4}
+\vbox to 0cm{\includegraphics[width=\linewidth,trim=0cm 0cm 0cm 0cm]{PRESENTATION_ExSyn/opt_02.pdf}\vss}
+\vskip-1cm
+\begin{columns}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/opt_02.ys}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/opt_02.v}
+\end{columns}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsecname{} -- Example 3/4}
+\vbox to 0cm{\includegraphics[width=\linewidth,trim=0cm 0cm 0cm -2cm]{PRESENTATION_ExSyn/opt_03.pdf}\vss}
+\vskip-1cm
+\begin{columns}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/opt_03.ys}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/opt_03.v}
+\end{columns}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsecname{} -- Example 4/4}
+\vbox to 0cm{\hskip6cm\includegraphics[width=6cm,trim=0cm 0cm 0cm -3cm]{PRESENTATION_ExSyn/opt_04.pdf}\vss}
+\vskip-1cm
+\begin{columns}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/opt_04.v}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/opt_04.ys}
+\end{columns}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{When to use {\tt opt} or {\tt clean}}
+
+\begin{frame}{\subsecname}
+Usually it does not hurt to call {\tt opt} after each regular command in the
+synthesis script. But it increases the synthesis time, so it is favourable
+to only call {\tt opt} when an improvement can be achieved.
+
+\bigskip
+The designs in {\tt yosys-bigsim} are a good playground for experimenting with
+the effects of calling {\tt opt} in various places of the flow.
+
+\bigskip
+It generally is a good idea to call {\tt opt} before inherently expensive
+commands such as {\tt sat} or {\tt freduce}, as the possible gain is much
+higher in this cases as the possible loss.
+
+\bigskip
+The {\tt clean} command on the other hand is very fast and many commands leave
+a mess (dangling signal wires, etc). For example, most commands do not remove
+any wires or cells. They just change the connections and depend on a later
+call to clean to get rid of the now unused objects. So the occasional {\tt ;;}
+is a good idea in every synthesis script.
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{The {\tt memory} command}
+
+\begin{frame}[fragile]{\subsecname}
+In the RTL netlist, memory reads and writes are individual cells. This makes
+consolidating the number of ports for a memory easier. The {\tt memory}
+transforms memories to an implementation. Per default that is logic for address
+decoders and registers. It also is a macro command that calls other commands:
+
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+# this merges registers into the memory read- and write cells.
+memory_dff
+
+# this collects all read and write cells for a memory and transforms them
+# into one multi-port memory cell.
+memory_collect
+
+# this takes the multi-port memory cell and transforms it to address decoder
+# logic and registers. This step is skipped if "memory" is called with -nomap.
+memory_map
+\end{lstlisting}
+
+\bigskip
+Usually it is preferred to use architecture-specific RAM resources for memory.
+For example:
+
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+memory -nomap; techmap -map my_memory_map.v; memory_map
+\end{lstlisting}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsecname{} -- Example 1/2}
+\vbox to 0cm{\includegraphics[width=0.7\linewidth,trim=0cm 0cm 0cm -10cm]{PRESENTATION_ExSyn/memory_01.pdf}\vss}
+\vskip-1cm
+\begin{columns}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/memory_01.ys}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/memory_01.v}
+\end{columns}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsecname{} -- Example 2/2}
+\vbox to 0cm{\hfill\includegraphics[width=7.5cm,trim=0cm 0cm 0cm -5cm]{PRESENTATION_ExSyn/memory_02.pdf}\vss}
+\vskip-1cm
+\begin{columns}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{6pt}{8pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/memory_02.v}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/memory_02.ys}
+\end{columns}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{The {\tt fsm} command}
+
+\begin{frame}[fragile]{\subsecname{}}
+The {\tt fsm} command identifies, extracts, optimizes (re-encodes), and
+re-synthesizes finite state machines. It again is a macro that calls
+a series of other commands:
+
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+fsm_detect # unless got option -nodetect
+fsm_extract
+
+fsm_opt
+clean
+fsm_opt
+
+fsm_expand # if got option -expand
+clean # if got option -expand
+fsm_opt # if got option -expand
+
+fsm_recode # unless got option -norecode
+
+fsm_info
+
+fsm_export # if got option -export
+fsm_map # unless got option -nomap
+\end{lstlisting}
+\end{frame}
+
+\begin{frame}{\subsecname{} -- details}
+Some details on the most important commands from the {\tt fsm\_*} group:
+
+\bigskip
+The {\tt fsm\_detect} command identifies FSM state registers and marks them
+with the {\tt (* fsm\_encoding = "auto" *)} attribute, if they do not have the
+{\tt fsm\_encoding} set already. Mark registers with {\tt (* fsm\_encoding =
+"none" *)} to disable FSM optimization for a register.
+
+\bigskip
+The {\tt fsm\_extract} command replaces the entire FSM (logic and state
+registers) with a {\tt \$fsm} cell.
+
+\bigskip
+The commands {\tt fsm\_opt} and {\tt fsm\_recode} can be used to optimize the
+FSM.
+
+\bigskip
+Finally the {\tt fsm\_map} command can be used to convert the (optimized) {\tt
+\$fsm} cell back to logic and registers.
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{The {\tt techmap} command}
+
+\begin{frame}[t]{\subsecname}
+\vbox to 0cm{\includegraphics[width=12cm,trim=-15cm 0cm 0cm -20cm]{PRESENTATION_ExSyn/techmap_01.pdf}\vss}
+\vskip-0.8cm
+The {\tt techmap} command replaces cells with implementations given as
+verilog source. For example implementing a 32 bit adder using 16 bit adders:
+
+\vbox to 0cm{
+\vskip-0.3cm
+\lstinputlisting[basicstyle=\ttfamily\fontsize{6pt}{7pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/techmap_01_map.v}
+}\vbox to 0cm{
+\vskip-0.5cm
+\lstinputlisting[xleftmargin=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=verilog]{PRESENTATION_ExSyn/techmap_01.v}
+\lstinputlisting[xleftmargin=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/techmap_01.ys}
+}
+\end{frame}
+
+\begin{frame}[t]{\subsecname{} -- stdcell mapping}
+When {\tt techmap} is used without a map file, it uses a built-in map file
+to map all RTL cell types to a generic library of built-in logic gates and registers.
+
+\bigskip
+\begin{block}{The built-in logic gate types are:}
+{\tt \$\_NOT\_ \$\_AND\_ \$\_OR\_ \$\_XOR\_ \$\_MUX\_}
+\end{block}
+
+\bigskip
+\begin{block}{The register types are:}
+{\tt \$\_SR\_NN\_ \$\_SR\_NP\_ \$\_SR\_PN\_ \$\_SR\_PP\_ \\
+\$\_DFF\_N\_ \$\_DFF\_P\_ \\
+\$\_DFF\_NN0\_ \$\_DFF\_NN1\_ \$\_DFF\_NP0\_ \$\_DFF\_NP1\_ \\
+\$\_DFF\_PN0\_ \$\_DFF\_PN1\_ \$\_DFF\_PP0\_ \$\_DFF\_PP1\_ \\
+\$\_DFFSR\_NNN\_ \$\_DFFSR\_NNP\_ \$\_DFFSR\_NPN\_ \$\_DFFSR\_NPP\_ \\
+\$\_DFFSR\_PNN\_ \$\_DFFSR\_PNP\_ \$\_DFFSR\_PPN\_ \$\_DFFSR\_PPP\_ \\
+\$\_DLATCH\_N\_ \$\_DLATCH\_P\_}
+\end{block}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{The {\tt abc} command}
+
+\begin{frame}{\subsecname}
+The {\tt abc} command provides an interface to ABC\footnote[frame]{\url{http://www.eecs.berkeley.edu/~alanmi/abc/}},
+an open source tool for low-level logic synthesis.
+
+\medskip
+The {\tt abc} command processes a netlist of internal gate types and can perform:
+\begin{itemize}
+\item logic minimization (optimization)
+\item mapping of logic to standard cell library (liberty format)
+\item mapping of logic to k-LUTs (for FPGA synthesis)
+\end{itemize}
+
+\medskip
+Optionally {\tt abc} can process registers from one clock domain and perform
+sequential optimization (such as register balancing).
+
+\medskip
+ABC is also controlled using scripts. An ABC script can be specified to use
+more advanced ABC features. It is also possible to write the design with
+{\tt write\_blif} and load the output file into ABC outside of Yosys.
+\end{frame}
+
+\begin{frame}[fragile]{\subsecname{} -- Example}
+\begin{columns}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/abc_01.v}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/abc_01.ys}
+\end{columns}
+\includegraphics[width=\linewidth,trim=0 0cm 0 0cm]{PRESENTATION_ExSyn/abc_01.pdf}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Other special-purpose mapping commands}
+
+\begin{frame}{\subsecname}
+\begin{block}{\tt dfflibmap}
+This command maps the internal register cell types to the register types
+described in a liberty file.
+\end{block}
+
+\bigskip
+\begin{block}{\tt hilomap}
+Some architectures require special driver cells for driving a constant hi or lo
+value. This command replaces simple constants with instances of such driver cells.
+\end{block}
+
+\bigskip
+\begin{block}{\tt iopadmap}
+Top-level input/outputs must usually be implemented using special I/O-pad cells.
+This command inserts this cells to the design.
+\end{block}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Example Synthesis Script}
+
+\begin{frame}[fragile]{\subsecname}
+\begin{columns}
+\column[t]{4cm}
+\begin{lstlisting}[basicstyle=\ttfamily\fontsize{6pt}{7pt}\selectfont, language=ys]
+# read and elaborate design
+read_verilog cpu_top.v cpu_ctrl.v cpu_regs.v
+read_verilog -D WITH_MULT cpu_alu.v
+hierarchy -check -top cpu_top
+
+# high-level synthesis
+proc; opt; fsm;; memory -nomap; opt
+
+# substitute block rams
+techmap -map map_rams.v
+
+# map remaining memories
+memory_map
+
+# low-level synthesis
+techmap; opt; flatten;; abc -lut6
+techmap -map map_xl_cells.v
+
+# add clock buffers
+select -set xl_clocks t:FDRE %x:+FDRE[C] t:FDRE %d
+iopadmap -inpad BUFGP O:I @xl_clocks
+
+# add io buffers
+select -set xl_nonclocks w:* t:BUFGP %x:+BUFGP[I] %d
+iopadmap -outpad OBUF I:O -inpad IBUF O:I @xl_nonclocks
+
+# write synthesis results
+write_edif synth.edif
+\end{lstlisting}
+\column[t]{6cm}
+\vskip1cm
+\begin{block}{Teaser / Outlook}
+\small\parbox{6cm}{
+The weird {\tt select} expressions at the end of this script are discussed in
+the next part (Section 3, ``Advanced Synthesis'') of this presentation.}
+\end{block}
+\end{columns}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Summary}
+
+\begin{frame}{\subsecname}
+\begin{itemize}
+\item Yosys provides commands for each phase of the synthesis.
+\item Each command solves a (more or less) simple problem.
+\item Complex commands are often only front-ends to simple commands.
+\item {\tt proc; opt; fsm; opt; memory; opt; techmap; opt; abc;;}
+\end{itemize}
+
+\bigskip
+\bigskip
+\begin{center}
+Questions?
+\end{center}
+
+\bigskip
+\bigskip
+\begin{center}
+\url{http://www.clifford.at/yosys/}
+\end{center}
+\end{frame}
+
diff --git a/manual/PRESENTATION_ExSyn/.gitignore b/manual/PRESENTATION_ExSyn/.gitignore
new file mode 100644
index 00000000..cf658897
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/.gitignore
@@ -0,0 +1 @@
+*.dot
diff --git a/manual/PRESENTATION_ExSyn/Makefile b/manual/PRESENTATION_ExSyn/Makefile
new file mode 100644
index 00000000..c34eae3f
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/Makefile
@@ -0,0 +1,20 @@
+
+TARGETS += proc_01 proc_02 proc_03
+TARGETS += opt_01 opt_02 opt_03 opt_04
+TARGETS += memory_01 memory_02
+TARGETS += techmap_01
+TARGETS += abc_01
+
+all: $(addsuffix .pdf,$(TARGETS))
+
+define make_pdf_template
+$(1).pdf: $(1)*.v $(1)*.ys
+ ../../yosys -p 'script $(1).ys; show -notitle -prefix $(1) -format pdf'
+endef
+
+$(foreach trg,$(TARGETS),$(eval $(call make_pdf_template,$(trg))))
+
+clean:
+ rm -f $(addsuffix .pdf,$(TARGETS))
+ rm -f $(addsuffix .dot,$(TARGETS))
+
diff --git a/manual/PRESENTATION_ExSyn/abc_01.v b/manual/PRESENTATION_ExSyn/abc_01.v
new file mode 100644
index 00000000..3bc68635
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/abc_01.v
@@ -0,0 +1,10 @@
+module test(input clk, a, b, c,
+ output reg y);
+
+ reg [2:0] q1, q2;
+ always @(posedge clk) begin
+ q1 <= { a, b, c };
+ q2 <= q1;
+ y <= ^q2;
+ end
+endmodule
diff --git a/manual/PRESENTATION_ExSyn/abc_01.ys b/manual/PRESENTATION_ExSyn/abc_01.ys
new file mode 100644
index 00000000..bb0b3780
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/abc_01.ys
@@ -0,0 +1,5 @@
+read_verilog abc_01.v
+read_verilog -lib abc_01_cells.v
+hierarchy -check -top test
+proc; opt; techmap
+abc -dff -liberty abc_01_cells.lib;;
diff --git a/manual/PRESENTATION_ExSyn/abc_01_cells.lib b/manual/PRESENTATION_ExSyn/abc_01_cells.lib
new file mode 100644
index 00000000..bf6b3478
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/abc_01_cells.lib
@@ -0,0 +1,54 @@
+// test comment
+/* test comment */
+library(demo) {
+ cell(BUF) {
+ area: 6;
+ pin(A) { direction: input; }
+ pin(Y) { direction: output;
+ function: "A"; }
+ }
+ cell(NOT) {
+ area: 3;
+ pin(A) { direction: input; }
+ pin(Y) { direction: output;
+ function: "A'"; }
+ }
+ cell(NAND) {
+ area: 4;
+ pin(A) { direction: input; }
+ pin(B) { direction: input; }
+ pin(Y) { direction: output;
+ function: "(A*B)'"; }
+ }
+ cell(NOR) {
+ area: 4;
+ pin(A) { direction: input; }
+ pin(B) { direction: input; }
+ pin(Y) { direction: output;
+ function: "(A+B)'"; }
+ }
+ cell(DFF) {
+ area: 18;
+ ff(IQ, IQN) { clocked_on: C;
+ next_state: D; }
+ pin(C) { direction: input;
+ clock: true; }
+ pin(D) { direction: input; }
+ pin(Q) { direction: output;
+ function: "IQ"; }
+ }
+ cell(DFFSR) {
+ area: 18;
+ ff(IQ, IQN) { clocked_on: C;
+ next_state: D;
+ preset: S;
+ clear: R; }
+ pin(C) { direction: input;
+ clock: true; }
+ pin(D) { direction: input; }
+ pin(Q) { direction: output;
+ function: "IQ"; }
+ pin(S) { direction: input; }
+ pin(R) { direction: input; }
+ }
+}
diff --git a/manual/PRESENTATION_ExSyn/abc_01_cells.v b/manual/PRESENTATION_ExSyn/abc_01_cells.v
new file mode 100644
index 00000000..44409479
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/abc_01_cells.v
@@ -0,0 +1,40 @@
+
+module BUF(A, Y);
+input A;
+output Y = A;
+endmodule
+
+module NOT(A, Y);
+input A;
+output Y = ~A;
+endmodule
+
+module NAND(A, B, Y);
+input A, B;
+output Y = ~(A & B);
+endmodule
+
+module NOR(A, B, Y);
+input A, B;
+output Y = ~(A | B);
+endmodule
+
+module DFF(C, D, Q);
+input C, D;
+output reg Q;
+always @(posedge C)
+ Q <= D;
+endmodule
+
+module DFFSR(C, D, Q, S, R);
+input C, D, S, R;
+output reg Q;
+always @(posedge C, posedge S, posedge R)
+ if (S)
+ Q <= 1'b1;
+ else if (R)
+ Q <= 1'b0;
+ else
+ Q <= D;
+endmodule
+
diff --git a/manual/PRESENTATION_ExSyn/memory_01.v b/manual/PRESENTATION_ExSyn/memory_01.v
new file mode 100644
index 00000000..0a3f9acd
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/memory_01.v
@@ -0,0 +1,9 @@
+module test(input CLK, ADDR,
+ input [7:0] DIN,
+ output reg [7:0] DOUT);
+ reg [7:0] mem [0:1];
+ always @(posedge CLK) begin
+ mem[ADDR] <= DIN;
+ DOUT <= mem[ADDR];
+ end
+endmodule
diff --git a/manual/PRESENTATION_ExSyn/memory_01.ys b/manual/PRESENTATION_ExSyn/memory_01.ys
new file mode 100644
index 00000000..2ffd8223
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/memory_01.ys
@@ -0,0 +1,3 @@
+read_verilog memory_01.v
+hierarchy -check -top test
+proc;; memory; opt
diff --git a/manual/PRESENTATION_ExSyn/memory_02.v b/manual/PRESENTATION_ExSyn/memory_02.v
new file mode 100644
index 00000000..dbe86ed1
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/memory_02.v
@@ -0,0 +1,27 @@
+module test(
+ input WR1_CLK, WR2_CLK,
+ input WR1_WEN, WR2_WEN,
+ input [7:0] WR1_ADDR, WR2_ADDR,
+ input [7:0] WR1_DATA, WR2_DATA,
+ input RD1_CLK, RD2_CLK,
+ input [7:0] RD1_ADDR, RD2_ADDR,
+ output reg [7:0] RD1_DATA, RD2_DATA
+);
+
+reg [7:0] memory [0:255];
+
+always @(posedge WR1_CLK)
+ if (WR1_WEN)
+ memory[WR1_ADDR] <= WR1_DATA;
+
+always @(posedge WR2_CLK)
+ if (WR2_WEN)
+ memory[WR2_ADDR] <= WR2_DATA;
+
+always @(posedge RD1_CLK)
+ RD1_DATA <= memory[RD1_ADDR];
+
+always @(posedge RD2_CLK)
+ RD2_DATA <= memory[RD2_ADDR];
+
+endmodule
diff --git a/manual/PRESENTATION_ExSyn/memory_02.ys b/manual/PRESENTATION_ExSyn/memory_02.ys
new file mode 100644
index 00000000..9da6fda5
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/memory_02.ys
@@ -0,0 +1,4 @@
+read_verilog memory_02.v
+hierarchy -check -top test
+proc;; memory -nomap
+opt -mux_undef -mux_bool
diff --git a/manual/PRESENTATION_ExSyn/opt_01.v b/manual/PRESENTATION_ExSyn/opt_01.v
new file mode 100644
index 00000000..5d3c1ea4
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/opt_01.v
@@ -0,0 +1,3 @@
+module test(input A, B, output Y);
+assign Y = A ? A ? B : 1'b1 : B;
+endmodule
diff --git a/manual/PRESENTATION_ExSyn/opt_01.ys b/manual/PRESENTATION_ExSyn/opt_01.ys
new file mode 100644
index 00000000..34ed123b
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/opt_01.ys
@@ -0,0 +1,3 @@
+read_verilog opt_01.v
+hierarchy -check -top test
+opt
diff --git a/manual/PRESENTATION_ExSyn/opt_02.v b/manual/PRESENTATION_ExSyn/opt_02.v
new file mode 100644
index 00000000..762fc1a8
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/opt_02.v
@@ -0,0 +1,3 @@
+module test(input A, output Y, Z);
+assign Y = A == A, Z = A != A;
+endmodule
diff --git a/manual/PRESENTATION_ExSyn/opt_02.ys b/manual/PRESENTATION_ExSyn/opt_02.ys
new file mode 100644
index 00000000..fc92a636
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/opt_02.ys
@@ -0,0 +1,3 @@
+read_verilog opt_02.v
+hierarchy -check -top test
+opt
diff --git a/manual/PRESENTATION_ExSyn/opt_03.v b/manual/PRESENTATION_ExSyn/opt_03.v
new file mode 100644
index 00000000..134161bb
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/opt_03.v
@@ -0,0 +1,4 @@
+module test(input [3:0] A, B,
+ output [3:0] Y, Z);
+assign Y = A + B, Z = B + A;
+endmodule
diff --git a/manual/PRESENTATION_ExSyn/opt_03.ys b/manual/PRESENTATION_ExSyn/opt_03.ys
new file mode 100644
index 00000000..282f06dd
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/opt_03.ys
@@ -0,0 +1,3 @@
+read_verilog opt_03.v
+hierarchy -check -top test
+opt
diff --git a/manual/PRESENTATION_ExSyn/opt_04.v b/manual/PRESENTATION_ExSyn/opt_04.v
new file mode 100644
index 00000000..2ed44763
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/opt_04.v
@@ -0,0 +1,19 @@
+module test(input CLK, ARST,
+ output [7:0] Q1, Q2, Q3);
+
+wire NO_CLK = 0;
+
+always @(posedge CLK, posedge ARST)
+ if (ARST)
+ Q1 <= 42;
+
+always @(posedge NO_CLK, posedge ARST)
+ if (ARST)
+ Q2 <= 42;
+ else
+ Q2 <= 23;
+
+always @(posedge CLK)
+ Q3 <= 42;
+
+endmodule
diff --git a/manual/PRESENTATION_ExSyn/opt_04.ys b/manual/PRESENTATION_ExSyn/opt_04.ys
new file mode 100644
index 00000000..f5ddae29
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/opt_04.ys
@@ -0,0 +1,3 @@
+read_verilog opt_04.v
+hierarchy -check -top test
+proc; opt
diff --git a/manual/PRESENTATION_ExSyn/proc_01.v b/manual/PRESENTATION_ExSyn/proc_01.v
new file mode 100644
index 00000000..61286319
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/proc_01.v
@@ -0,0 +1,7 @@
+module test(input D, C, R, output reg Q);
+ always @(posedge C, posedge R)
+ if (R)
+ Q <= 0;
+ else
+ Q <= D;
+endmodule
diff --git a/manual/PRESENTATION_ExSyn/proc_01.ys b/manual/PRESENTATION_ExSyn/proc_01.ys
new file mode 100644
index 00000000..c22a2fd5
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/proc_01.ys
@@ -0,0 +1,3 @@
+read_verilog proc_01.v
+hierarchy -check -top test
+proc;;
diff --git a/manual/PRESENTATION_ExSyn/proc_02.v b/manual/PRESENTATION_ExSyn/proc_02.v
new file mode 100644
index 00000000..8e440f6c
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/proc_02.v
@@ -0,0 +1,8 @@
+module test(input D, C, R, RV,
+ output reg Q);
+ always @(posedge C, posedge R)
+ if (R)
+ Q <= RV;
+ else
+ Q <= D;
+endmodule
diff --git a/manual/PRESENTATION_ExSyn/proc_02.ys b/manual/PRESENTATION_ExSyn/proc_02.ys
new file mode 100644
index 00000000..823b18d6
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/proc_02.ys
@@ -0,0 +1,3 @@
+read_verilog proc_02.v
+hierarchy -check -top test
+proc;;
diff --git a/manual/PRESENTATION_ExSyn/proc_03.v b/manual/PRESENTATION_ExSyn/proc_03.v
new file mode 100644
index 00000000..a89c965e
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/proc_03.v
@@ -0,0 +1,10 @@
+module test(input A, B, C, D, E,
+ output reg Y);
+ always @* begin
+ Y <= A;
+ if (B)
+ Y <= C;
+ if (D)
+ Y <= E;
+ end
+endmodule
diff --git a/manual/PRESENTATION_ExSyn/proc_03.ys b/manual/PRESENTATION_ExSyn/proc_03.ys
new file mode 100644
index 00000000..3e7e6dda
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/proc_03.ys
@@ -0,0 +1,3 @@
+read_verilog proc_03.v
+hierarchy -check -top test
+proc;;
diff --git a/manual/PRESENTATION_ExSyn/techmap_01.v b/manual/PRESENTATION_ExSyn/techmap_01.v
new file mode 100644
index 00000000..c53ca91a
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/techmap_01.v
@@ -0,0 +1,4 @@
+module test(input [31:0] a, b,
+ output [31:0] y);
+assign y = a + b;
+endmodule
diff --git a/manual/PRESENTATION_ExSyn/techmap_01.ys b/manual/PRESENTATION_ExSyn/techmap_01.ys
new file mode 100644
index 00000000..8ef9de22
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/techmap_01.ys
@@ -0,0 +1,3 @@
+read_verilog techmap_01.v
+hierarchy -check -top test
+techmap -map techmap_01_map.v;;
diff --git a/manual/PRESENTATION_ExSyn/techmap_01_map.v b/manual/PRESENTATION_ExSyn/techmap_01_map.v
new file mode 100644
index 00000000..4fd86e85
--- /dev/null
+++ b/manual/PRESENTATION_ExSyn/techmap_01_map.v
@@ -0,0 +1,24 @@
+module \$add (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 1;
+parameter B_WIDTH = 1;
+parameter Y_WIDTH = 1;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if ((A_WIDTH == 32) && (B_WIDTH == 32))
+ begin
+ wire [16:0] S1 = A[15:0] + B[15:0];
+ wire [15:0] S2 = A[31:16] + B[31:16] + S1[16];
+ assign Y = {S2[15:0], S1[15:0]};
+ end
+ else
+ wire _TECHMAP_FAIL_ = 1;
+endgenerate
+
+endmodule
diff --git a/manual/PRESENTATION_Intro.tex b/manual/PRESENTATION_Intro.tex
new file mode 100644
index 00000000..555ec917
--- /dev/null
+++ b/manual/PRESENTATION_Intro.tex
@@ -0,0 +1,956 @@
+
+\section{Introduction to Yosys}
+
+\begin{frame}
+\sectionpage
+\end{frame}
+
+\iffalse
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Representations of (digital) Circuits}
+
+\begin{frame}[t]{\subsecname}
+\begin{itemize}
+ \item Graphical
+ \begin{itemize}
+ \item \alert<1>{Schematic Diagram}
+ \item \alert<2>{Physical Layout}
+ \end{itemize}
+ \bigskip
+ \item Non-graphical
+ \begin{itemize}
+ \item \alert<3>{Netlists}
+ \item \alert<4>{Hardware Description Languages (HDLs)}
+ \end{itemize}
+\end{itemize}
+\bigskip
+\begin{block}{Definition:
+\only<1>{Schematic Diagram}%
+\only<2>{Physical Layout}%
+\only<3>{Netlists}%
+\only<4>{Hardware Description Languages (HDLs)}}
+\only<1>{
+ Graphical representation of the circuit topology. Circuit elements
+ are represented by symbols and electrical connections by lines. The geometric
+ layout is for readability only.
+}%
+\only<2>{
+ The actual physical geometry of the device (PCB or ASIC manufacturing masks).
+ This is the final product of the design process.
+}%
+\only<3>{
+ A list of circuit elements and a list of connections. This is the raw circuit
+ topology.
+}%
+\only<4>{
+ Computer languages (like programming languages) that can be used to describe
+ circuits. HDLs are much more powerful in describing huge circuits than
+ schematic diagrams.
+}%
+\end{block}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\fi
+
+\subsection{Levels of Abstraction for Digital Circuits}
+
+\begin{frame}[t]{\subsecname}
+\begin{itemize}
+ \item \alert<1>{System Level}
+ \item \alert<2>{High Level}
+ \item \alert<3>{Behavioral Level}
+ \item \alert<4>{Register-Transfer Level (RTL)}
+ \item \alert<5>{Logical Gate Level}
+ \item \alert<6>{Physical Gate Level}
+ \item \alert<7>{Switch Level}
+\end{itemize}
+\bigskip
+\begin{block}{Definition:
+\only<1>{System Level}%
+\only<2>{High Level}%
+\only<3>{Behavioral Level}%
+\only<4>{Register-Transfer Level (RTL)}%
+\only<5>{Logical Gate Level}%
+\only<6>{Physical Gate Level}%
+\only<7>{Switch Level}}
+\only<1>{
+ Overall view of the circuit. E.g. block-diagrams or instruction-set architecture descriptions.
+}%
+\only<2>{
+ Functional implementation of circuit in high-level programming language (C, C++, SystemC, Matlab, Python, etc.).
+}%
+\only<3>{
+ Cycle-accurate description of circuit in hardware description language (Verilog, VHDL, etc.).
+}%
+\only<4>{
+ List of registers (flip-flops) and logic functions that calculate the next state from the previous one. Usually
+ a netlist utilizing high-level cells such as adders, multipliers, multiplexer, etc.
+}%
+\only<5>{
+ Netlist of single-bit registers and basic logic gates (such as AND, OR,
+ NOT, etc.). Popular form: And-Inverter-Graphs (AIGs) with pairs of primary
+ inputs and outputs for each register bit.
+}%
+\only<6>{
+ Netlist of cells that actually are available on the target architecture
+ (such as CMOS gates in an ASIC or LUTs in an FPGA). Optimized for
+ area, power, and/or speed (static timing or number of logic levels).
+}%
+\only<7>{
+ Netlist of individual transistors.
+}%
+\end{block}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Digital Circuit Synthesis}
+
+\begin{frame}{\subsecname}
+ Synthesis Tools (such as Yosys) can transform HDL code to circuits:
+
+ \bigskip
+ \begin{center}
+ \begin{tikzpicture}[scale=0.8, every node/.style={transform shape}]
+ \tikzstyle{lvl} = [draw, fill=MyBlue, rectangle, minimum height=2em, minimum width=15em]
+ \node[lvl] (sys) {System Level};
+ \node[lvl] (hl) [below of=sys] {High Level};
+ \node[lvl] (beh) [below of=hl] {Behavioral Level};
+ \node[lvl] (rtl) [below of=beh] {Register-Transfer Level (RTL)};
+ \node[lvl] (lg) [below of=rtl] {Logical Gate Level};
+ \node[lvl] (pg) [below of=lg] {Physical Gate Level};
+ \node[lvl] (sw) [below of=pg] {Switch Level};
+
+ \draw[dotted] (sys.east) -- ++(1,0) coordinate (sysx);
+ \draw[dotted] (hl.east) -- ++(1,0) coordinate (hlx);
+ \draw[dotted] (beh.east) -- ++(1,0) coordinate (behx);
+ \draw[dotted] (rtl.east) -- ++(1,0) coordinate (rtlx);
+ \draw[dotted] (lg.east) -- ++(1,0) coordinate (lgx);
+ \draw[dotted] (pg.east) -- ++(1,0) coordinate (pgx);
+ \draw[dotted] (sw.east) -- ++(1,0) coordinate (swx);
+
+ \draw[gray,|->] (sysx) -- node[right] {System Design} (hlx);
+ \draw[|->|] (hlx) -- node[right] {High Level Synthesis (HLS)} (behx);
+ \draw[->|] (behx) -- node[right] {Behavioral Synthesis} (rtlx);
+ \draw[->|] (rtlx) -- node[right] {RTL Synthesis} (lgx);
+ \draw[->|] (lgx) -- node[right] {Logic Synthesis} (pgx);
+ \draw[gray,->|] (pgx) -- node[right] {Cell Library} (swx);
+
+ \draw[dotted] (behx) -- ++(4,0) coordinate (a);
+ \draw[dotted] (pgx) -- ++(4,0) coordinate (b);
+ \draw[|->|] (a) -- node[right] {Yosys} (b);
+ \end{tikzpicture}
+ \end{center}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{What Yosys can and can't do}
+
+\begin{frame}{\subsecname}
+
+Things Yosys can do:
+\begin{itemize}
+\item Read and process (most of) modern Verilog-2005 code.
+\item Perform all kinds of operations on netlist (RTL, Logic, Gate).
+\item Perform logic optimizations and gate mapping with ABC\footnote[frame]{\url{http://www.eecs.berkeley.edu/~alanmi/abc/}}.
+\end{itemize}
+
+\bigskip
+Things Yosys can't do:
+\begin{itemize}
+\item Process high-level languages such as C/C++/SystemC.
+\item Create physical layouts (place\&route).
+\end{itemize}
+
+\bigskip
+A typical flow combines Yosys with with a low-level implementation tool, such
+as Qflow\footnote[frame]{\url{http://opencircuitdesign.com/qflow/}} for ASIC designs.
+
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Yosys Data- and Control-Flow}
+
+\begin{frame}{\subsecname}
+ A (usually short) synthesis script controls Yosys.
+
+ This scripts contain three types of commands:
+ \begin{itemize}
+ \item {\bf Frontends}, that read input files (usually Verilog).
+ \item {\bf Passes}, that perform transformations on the design in memory.
+ \item {\bf Backends}, that write the design in memory to a file (various formats are available: Verilog, BLIF, EDIF, SPICE, BTOR, \dots).
+ \end{itemize}
+
+ \bigskip
+ \begin{center}
+ \begin{tikzpicture}[scale=0.6, every node/.style={transform shape}]
+ \path (-1.5,3) coordinate (cursor);
+ \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0);
+ \draw[fill=orange!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Frontend} ++(1,3) coordinate (cursor);
+ \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0);
+ \draw[fill=green!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Pass} ++(1,3) coordinate (cursor);
+ \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0);
+ \draw[fill=green!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Pass} ++(1,3) coordinate (cursor);
+ \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0);
+ \draw[fill=green!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Pass} ++(1,3) coordinate (cursor);
+ \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0);
+ \draw[fill=orange!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Backend} ++(1,3) coordinate (cursor);
+ \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0);
+
+ \path (-3,-0.5) coordinate (cursor);
+ \draw (cursor) -- node[below] {HDL} ++(3,0) coordinate (cursor);
+ \draw[|-|] (cursor) -- node[below] {Internal Format (RTLIL)} ++(8,0) coordinate (cursor);
+ \draw (cursor) -- node[below] {Netlist} ++(3,0);
+
+ \path (-3,3.5) coordinate (cursor);
+ \draw[-] (cursor) -- node[above] {High-Level} ++(3,0) coordinate (cursor);
+ \draw[-] (cursor) -- ++(8,0) coordinate (cursor);
+ \draw[->] (cursor) -- node[above] {Low-Level} ++(3,0);
+ \end{tikzpicture}
+ \end{center}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Program Components and Data Formats}
+
+\begin{frame}{\subsecname}
+ \begin{center}
+ \begin{tikzpicture}[scale=0.6, every node/.style={transform shape}]
+ \tikzstyle{process} = [draw, fill=green!10, rectangle, minimum height=3em, minimum width=10em, node distance=15em]
+ \tikzstyle{data} = [draw, fill=blue!10, ellipse, minimum height=3em, minimum width=7em, node distance=15em]
+ \node[process] (vlog) {Verilog Frontend};
+ \node[process, dashed, fill=green!5] (vhdl) [right of=vlog] {VHDL Frontend};
+ \node[process] (ilang) [right of=vhdl] {Other Frontends};
+ \node[data] (ast) [below of=vlog, node distance=5em, xshift=7.5em] {AST};
+ \node[process] (astfe) [below of=ast, node distance=5em] {AST Frontend};
+ \node[data] (rtlil) [below of=astfe, node distance=5em, xshift=7.5em] {RTLIL};
+ \node[process] (pass) [right of=rtlil, node distance=5em, xshift=7.5em] {Passes};
+ \node[process] (vlbe) [below of=rtlil, node distance=7em, xshift=-13em] {Verilog Backend};
+ \node[process] (ilangbe) [below of=rtlil, node distance=7em, xshift=0em] {ILANG Backend};
+ \node[process, fill=green!5] (otherbe) [below of=rtlil, node distance=7em, xshift=+13em] {Other Backends};
+
+ \draw[-latex] (vlog) -- (ast);
+ \draw[-latex] (vhdl) -- (ast);
+ \draw[-latex] (ast) -- (astfe);
+ \draw[-latex] (astfe) -- (rtlil);
+ \draw[-latex] (ilang) -- (rtlil);
+ \draw[latex-latex] (rtlil) -- (pass);
+ \draw[-latex] (rtlil) -- (vlbe);
+ \draw[-latex] (rtlil) -- (ilangbe);
+ \draw[-latex] (rtlil) -- (otherbe);
+ \end{tikzpicture}
+ \end{center}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Example Project}
+
+\begin{frame}[t]{\subsecname}
+The following slides cover an example project. This project contains three files:
+\begin{itemize}
+\item A simple ASIC synthesis script
+\item A digital design written in Verilog
+\item A simple CMOS cell library
+\end{itemize}
+\vfill
+Direct link to the files: \\ \footnotesize
+\url{https://github.com/cliffordwolf/yosys/tree/master/manual/PRESENTATION_Intro}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{frame}[t]{\subsecname{} -- Synthesis Script}
+
+\setbeamercolor{alerted text}{fg=white,bg=red}
+
+\begin{minipage}[t]{6cm}
+\tt\scriptsize
+{\color{YosysGreen}\# read design}\\
+\boxalert<1>{read\_verilog counter.v}\\
+\boxalert<2>{hierarchy -check -top counter}
+
+\medskip
+{\color{YosysGreen}\# the high-level stuff}\\
+\boxalert<3>{proc}; \boxalert<4>{opt}; \boxalert<5>{fsm}; \boxalert<6>{opt}; \boxalert<7>{memory}; \boxalert<8>{opt}
+
+\medskip
+{\color{YosysGreen}\# mapping to internal cell library}\\
+\boxalert<9>{techmap}; \boxalert<10>{opt}
+\end{minipage}
+\begin{minipage}[t]{5cm}
+\tt\scriptsize
+{\color{YosysGreen}\# mapping flip-flops to mycells.lib}\\
+\boxalert<11>{dfflibmap -liberty mycells.lib}
+
+\medskip
+{\color{YosysGreen}\# mapping logic to mycells.lib}\\
+\boxalert<12>{abc -liberty mycells.lib}
+
+\medskip
+{\color{YosysGreen}\# cleanup}\\
+\boxalert<13>{clean}
+
+\medskip
+{\color{YosysGreen}\# write synthesized design}\\
+\boxalert<14>{write\_verilog synth.v}
+\end{minipage}
+
+\vskip1cm
+
+\begin{block}{Command: \tt
+\only<1>{read\_verilog counter.v}%
+\only<2>{hierarchy -check -top counter}%
+\only<3>{proc}%
+\only<4>{opt}%
+\only<5>{fsm}%
+\only<6>{opt}%
+\only<7>{memory}%
+\only<8>{opt}%
+\only<9>{techmap}%
+\only<10>{opt}%
+\only<11>{dfflibmap -liberty mycells.lib}%
+\only<12>{abc -liberty mycells.lib}%
+\only<13>{clean}%
+\only<14>{write\_verilog synth.v}}
+\only<1>{
+ Read Verilog source file and convert to internal representation.
+}%
+\only<2>{
+ Elaborate the design hierarchy. Should always be the first
+ command after reading the design. Can re-run AST front-end.
+}%
+\only<3>{
+ Convert ``processes'' (the internal representation of behavioral
+ Verilog code) into multiplexers and registers.
+}%
+\only<4>{
+ Perform some basic optimizations and cleanups.
+}%
+\only<5>{
+ Analyze and optimize finite state machines.
+}%
+\only<6>{
+ Perform some basic optimizations and cleanups.
+}%
+\only<7>{
+ Analyze memories and create circuits to implement them.
+}%
+\only<8>{
+ Perform some basic optimizations and cleanups.
+}%
+\only<9>{
+ Map coarse-grain RTL cells (adders, etc.) to fine-grain
+ logic gates (AND, OR, NOT, etc.).
+}%
+\only<10>{
+ Perform some basic optimizations and cleanups.
+}%
+\only<11>{
+ Map registers to available hardware flip-flops.
+}%
+\only<12>{
+ Map logic to available hardware gates.
+}%
+\only<13>{
+ Clean up the design (just the last step of {\tt opt}).
+}%
+\only<14>{
+ Write final synthesis result to output file.
+}%
+\end{block}
+
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{frame}[fragile]{\subsecname{} -- Verilog Source: \tt counter.v}
+\lstinputlisting[xleftmargin=1cm, language=Verilog]{PRESENTATION_Intro/counter.v}
+\end{frame}
+
+\begin{frame}[fragile]{\subsecname{} -- Cell Library: \tt mycells.lib}
+\begin{columns}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=liberty, lastline=20]{PRESENTATION_Intro/mycells.lib}
+\column[t]{5cm}
+\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=liberty, firstline=21]{PRESENTATION_Intro/mycells.lib}
+\end{columns}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Running the Synthesis Script}
+
+\begin{frame}[t, fragile]{\subsecname{} -- Step 1/4}
+\begin{verbatim}
+read_verilog counter.v
+hierarchy -check -top counter
+\end{verbatim}
+
+\vfill
+\includegraphics[width=\linewidth,trim=0 0cm 0 0cm]{PRESENTATION_Intro/counter_00.pdf}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsecname{} -- Step 2/4}
+\begin{verbatim}
+proc; opt; fsm; opt; memory; opt
+\end{verbatim}
+
+\vfill
+\includegraphics[width=\linewidth,trim=0 0cm 0 0cm]{PRESENTATION_Intro/counter_01.pdf}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsecname{} -- Step 3/4}
+\begin{verbatim}
+techmap; opt
+\end{verbatim}
+
+\vfill
+\includegraphics[width=\linewidth,trim=0 0cm 0 2cm]{PRESENTATION_Intro/counter_02.pdf}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsecname{} -- Step 4/4}
+\begin{verbatim}
+dfflibmap -liberty mycells.lib
+abc -liberty mycells.lib
+clean
+\end{verbatim}
+
+\vfill\hfil
+\includegraphics[width=10cm,trim=0 0cm 0 0cm]{PRESENTATION_Intro/counter_03.pdf}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{The synth command}
+
+\begin{frame}[fragile]{\subsecname{}}
+Yosys contains a default (recommended example) synthesis script in form of the
+{\tt synth} command. The following commands are executed by this synthesis command:
+
+\begin{columns}
+\column[t]{5cm}
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+begin:
+ hierarchy -check [-top <top>]
+
+coarse:
+ proc
+ opt
+ wreduce
+ alumacc
+ share
+ opt
+ fsm
+ opt -fast
+ memory -nomap
+ opt_clean
+\end{lstlisting}
+\column[t]{5cm}
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+fine:
+ opt -fast -full
+ memory_map
+ opt -full
+ techmap
+ opt -fast
+
+abc:
+ abc -fast
+ opt -fast
+\end{lstlisting}
+\end{columns}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Yosys Commands}
+
+\begin{frame}[fragile]{\subsecname{} 1/3 \hspace{0pt plus 1 filll} (excerpt)}
+Command reference:
+\begin{itemize}
+\item Use ``{\tt help}'' for a command list and ``{\tt help \it command}'' for details.
+\item Or run ``{\tt yosys -H}'' or ``{\tt yosys -h \it command}''.
+\item Or go to \url{http://www.clifford.at/yosys/documentation.html}.
+\end{itemize}
+
+\bigskip
+Commands for design navigation and investigation:
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+ cd # a shortcut for 'select -module <name>'
+ ls # list modules or objects in modules
+ dump # print parts of the design in ilang format
+ show # generate schematics using graphviz
+ select # modify and view the list of selected objects
+\end{lstlisting}
+
+\bigskip
+Commands for executing scripts or entering interactive mode:
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+ shell # enter interactive command mode
+ history # show last interactive commands
+ script # execute commands from script file
+ tcl # execute a TCL script file
+\end{lstlisting}
+\end{frame}
+
+\begin{frame}[fragile]{\subsecname{} 2/3 \hspace{0pt plus 1 filll} (excerpt)}
+Commands for reading and elaborating the design:
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+ read_ilang # read modules from ilang file
+ read_verilog # read modules from Verilog file
+ hierarchy # check, expand and clean up design hierarchy
+\end{lstlisting}
+
+\bigskip
+Commands for high-level synthesis:
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+ proc # translate processes to netlists
+ fsm # extract and optimize finite state machines
+ memory # translate memories to basic cells
+ opt # perform simple optimizations
+\end{lstlisting}
+
+\bigskip
+Commands for technology mapping:
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+ techmap # generic technology mapper
+ abc # use ABC for technology mapping
+ dfflibmap # technology mapping of flip-flops
+ hilomap # technology mapping of constant hi- and/or lo-drivers
+ iopadmap # technology mapping of i/o pads (or buffers)
+ flatten # flatten design
+\end{lstlisting}
+\end{frame}
+
+\begin{frame}[fragile]{\subsecname{} 3/3 \hspace{0pt plus 1 filll} (excerpt)}
+Commands for writing the results:
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+ write_blif # write design to BLIF file
+ write_btor # write design to BTOR file
+ write_edif # write design to EDIF netlist file
+ write_ilang # write design to ilang file
+ write_spice # write design to SPICE netlist file
+ write_verilog # write design to Verilog file
+\end{lstlisting}
+
+\bigskip
+Script-Commands for standard synthesis tasks:
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+ synth # generic synthesis script
+ synth_xilinx # synthesis for Xilinx FPGAs
+\end{lstlisting}
+
+\bigskip
+Commands for model checking:
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys]
+ sat # solve a SAT problem in the circuit
+ miter # automatically create a miter circuit
+ scc # detect strongly connected components (logic loops)
+\end{lstlisting}
+
+\bigskip
+... and many many more.
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{More Verilog Examples}
+
+\begin{frame}[fragile]{\subsecname{} 1/3}
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=Verilog]
+module detectprime(a, y);
+ input [4:0] a;
+ output y;
+
+ integer i, j;
+ reg [31:0] lut;
+
+ initial begin
+ for (i = 0; i < 32; i = i+1) begin
+ lut[i] = i > 1;
+ for (j = 2; j*j <= i; j = j+1)
+ if (i % j == 0)
+ lut[i] = 0;
+ end
+ end
+
+ assign y = lut[a];
+endmodule
+\end{lstlisting}
+\end{frame}
+
+\begin{frame}[fragile]{\subsecname{} 2/3}
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=Verilog]
+module carryadd(a, b, y);
+ parameter WIDTH = 8;
+ input [WIDTH-1:0] a, b;
+ output [WIDTH-1:0] y;
+
+ genvar i;
+ generate
+ for (i = 0; i < WIDTH; i = i+1) begin:STAGE
+ wire IN1 = a[i], IN2 = b[i];
+ wire C, Y;
+ if (i == 0)
+ assign C = IN1 & IN2, Y = IN1 ^ IN2;
+ else
+ assign C = (IN1 & IN2) | ((IN1 | IN2) & STAGE[i-1].C),
+ Y = IN1 ^ IN2 ^ STAGE[i-1].C;
+ assign y[i] = Y;
+ end
+ endgenerate
+endmodule
+\end{lstlisting}
+\end{frame}
+
+\begin{frame}[fragile]{\subsecname{} 3/3}
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{7pt}{8.5pt}\selectfont, language=Verilog]
+module cam(clk, wr_enable, wr_addr, wr_data, rd_data, rd_addr, rd_match);
+ parameter WIDTH = 8;
+ parameter DEPTH = 16;
+ localparam ADDR_BITS = $clog2(DEPTH-1);
+
+ input clk, wr_enable;
+ input [ADDR_BITS-1:0] wr_addr;
+ input [WIDTH-1:0] wr_data, rd_data;
+ output reg [ADDR_BITS-1:0] rd_addr;
+ output reg rd_match;
+
+ integer i;
+ reg [WIDTH-1:0] mem [0:DEPTH-1];
+
+ always @(posedge clk) begin
+ rd_addr <= 'bx;
+ rd_match <= 0;
+ for (i = 0; i < DEPTH; i = i+1)
+ if (mem[i] == rd_data) begin
+ rd_addr <= i;
+ rd_match <= 1;
+ end
+ if (wr_enable)
+ mem[wr_addr] <= wr_data;
+ end
+endmodule
+\end{lstlisting}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Currently unsupported Verilog-2005 language features}
+
+\begin{frame}{\subsecname}
+\begin{itemize}
+\item Tri-state logic
+\item The wor/wand wire types (maybe for 0.5)
+\item Latched logic (is synthesized as logic with feedback loops)
+\item Some non-synthesizable features that should be ignored in synthesis are not supported by the parser and cause a parser error (file a bug report if you encounter this problem)
+\end{itemize}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Verification of Yosys}
+
+\begin{frame}{\subsecname}
+Continuously checking the correctness of Yosys and making sure that new features
+do not break old ones is a high priority in Yosys.
+
+\bigskip
+Two external test suites have been built for Yosys: VlogHammer and yosys-bigsim
+(see next slides)
+
+\bigskip
+In addition to that, yosys comes with $\approx\!200$ test cases used in ``{\tt make test}''.
+
+\bigskip
+A debug build of Yosys also contains a lot of asserts and checks the integrity of
+the internal state after each command.
+\end{frame}
+
+\begin{frame}[fragile]{\subsecname{} -- VlogHammer}
+VlogHammer is a Verilog regression test suite developed to test the different
+subsystems in Yosys by comparing them to each other and to the output created
+by some other tools (Xilinx Vivado, Xilinx XST, Altera Quartus II, ...).
+
+\bigskip
+Yosys Subsystems tested: Verilog frontend, const folding, const eval, technology mapping,
+simulation models, SAT models.
+
+\bigskip
+Thousands of auto-generated test cases containing code such as:
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=Verilog]
+assign y9 = $signed(((+$signed((^(6'd2 ** a2))))<$unsigned($unsigned(((+a3))))));
+assign y10 = (-((+((+{2{(~^p13)}})))^~(!{{b5,b1,a0},(a1&p12),(a4+a3)})));
+assign y11 = (~&(-{(-3'sd3),($unsigned($signed($unsigned({p0,b4,b1}))))}));
+\end{lstlisting}
+
+\bigskip
+Some bugs in Yosys where found and fixed thanks to VlogHammer. Over 50 bugs in
+the other tools used as external reference where found and reported so far.
+\end{frame}
+
+\begin{frame}{\subsecname{} -- yosys-bigsim}
+yosys-bigsim is a collection of real-world open-source Verilog designs and test
+benches. yosys-bigsim compares the testbench outputs of simulations of the original
+Verilog code and synthesis results.
+
+\bigskip
+The following designs are included in yosys-bigsim (excerpt):
+\begin{itemize}
+\item {\tt openmsp430} -- an MSP430 compatible 16 bit CPU
+\item {\tt aes\_5cycle\_2stage} -- an AES encryption core
+\item {\tt softusb\_navre} -- an AVR compatible 8 bit CPU
+\item {\tt amber23} -- an ARMv2 compatible 32 bit CPU
+\item {\tt lm32} -- another 32 bit CPU from Lattice Semiconductor
+\item {\tt verilog-pong} -- a hardware pong game with VGA output
+\item {\tt elliptic\_curve\_group} -- ECG point-add and point-scalar-mul core
+\item {\tt reed\_solomon\_decoder} -- a Reed-Solomon Error Correction Decoder
+\end{itemize}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Benefits of Open Source HDL Synthesis}
+
+\begin{frame}{\subsecname}
+\begin{itemize}
+\item Cost (also applies to ``free as in free beer'' solutions)
+\item Availability and Reproducibility
+\item Framework- and all-in-one-aspects
+\item Educational Tool
+\end{itemize}
+
+\bigskip
+
+Yosys is open source under the ISC license.
+\end{frame}
+
+\begin{frame}{\subsecname{} -- 1/3}
+\begin{itemize}
+\item Cost (also applies to ``free as in free beer'' solutions): \smallskip\par
+Today the cost for a mask set in $\unit[180]{nm}$ technology is far less than
+the cost for the design tools needed to design the mask layouts. Open Source
+ASIC flows are an important enabler for ASIC-level Open Source Hardware.
+
+\bigskip
+\item Availability and Reproducibility: \smallskip\par
+If you are a researcher who is publishing, you want to use tools that everyone
+else can also use. Even if most universities have access to all major
+commercial tools, you usually do not have easy access to the version that was
+used in a research project a couple of years ago. With Open Source tools you
+can even release the source code of the tool you have used alongside your data.
+\end{itemize}
+\end{frame}
+
+\begin{frame}{\subsecname{} -- 2/3}
+\begin{itemize}
+\item Framework: \smallskip\par
+Yosys is not only a tool. It is a framework that can be used as basis for other
+developments, so researchers and hackers alike do not need to re-invent the
+basic functionality. Extensibility was one of Yosys' design goals.
+
+\bigskip
+\item All-in-one: \smallskip\par
+Because of the framework characteristics of Yosys, an increasing number of features
+become available in one tool. Yosys not only can be used for circuit synthesis but
+also for formal equivalence checking, SAT solving, and for circuit analysis, to
+name just a few other application domains. With proprietary software one needs to
+learn a new tool for each of these applications.
+\end{itemize}
+\end{frame}
+
+\begin{frame}{\subsecname{} -- 3/3}
+\begin{itemize}
+\item Educational Tool: \smallskip\par
+Proprietary synthesis tools are at times very secretive about their inner
+workings. They often are ``black boxes''. Yosys is very open about its
+internals and it is easy to observe the different steps of synthesis.
+\end{itemize}
+
+\bigskip
+\begin{block}{Yosys is licensed under the ISC license:}
+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.
+\end{block}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Typical Applications for Yosys}
+
+\begin{frame}{\subsecname}
+\begin{itemize}
+\item Synthesis of final production designs
+\item Pre-production synthesis (trial runs before investing in other tools)
+\item Conversion of full-featured Verilog to simple Verilog
+\item Conversion of Verilog to other formats (BLIF, BTOR, etc)
+\item Demonstrating synthesis algorithms (e.g. for educational purposes)
+\item Framework for experimenting with new algorithms
+\item Framework for building custom flows\footnote[frame]{Not limited to synthesis
+but also formal verification, reverse engineering, ...}
+\end{itemize}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Projects (that I know of) using Yosys}
+
+\begin{frame}{\subsecname{} -- (1/2)}
+\begin{itemize}
+\item Ongoing PhD project on coarse grain synthesis \\
+{\setlength{\parindent}{0.5cm}\footnotesize
+Johann Glaser and Clifford Wolf. Methodology and Example-Driven Interconnect
+Synthesis for Designing Heterogeneous Coarse-Grain Reconfigurable
+Architectures. In Jan Haase, editor, \it Models, Methods, and Tools for Complex
+Chip Design. Lecture Notes in Electrical Engineering. Volume 265, 2014, pp
+201-221. Springer, 2013.}
+
+\bigskip
+\item I know several people that use Yosys simply as Verilog frontend for other
+flows (using either the BLIF and BTOR backends).
+
+\bigskip
+\item I know some analog chip designers that use Yosys for small digital
+control logic because it is simpler than setting up a commercial flow.
+\end{itemize}
+\end{frame}
+
+\begin{frame}{\subsecname{} -- (2/2)}
+\begin{itemize}
+\item Efabless
+\begin{itemize}
+\smallskip \item Not much information on the website (\url{http://efabless.com}) yet.
+\smallskip \item Very cheap 180nm prototyping process (partnering with various fabs)
+\smallskip \item A semiconductor company, NOT an EDA company
+\smallskip \item Web-based design environment
+\smallskip \item HDL Synthesis using Yosys
+\smallskip \item Custom place\&route tool
+
+\bigskip
+\item efabless is building an Open Source IC as reference design. \\
+\hskip1cm (to be announced soon: \url{http://www.openic.io})
+\end{itemize}
+\end{itemize}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Supported Platforms}
+
+\begin{frame}{\subsecname}
+\begin{itemize}
+\item Main development OS: Kubuntu 14.04
+\item There is a PPA for ubuntu (not maintained by me)
+\item Any current Debian-based system should work out of the box
+\item When building on other Linux distributions:
+\begin{itemize}
+\item Needs compiler with some C++11 support
+\item See README file for build instructions
+\item Post to the subreddit if you get stuck
+\end{itemize}
+\item Ported to OS X (Darwin) and OpenBSD
+\item Native win32 build with VisualStudio
+\item Cross win32 build with MXE
+\end{itemize}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Other Open Source Tools}
+
+\begin{frame}{\subsecname}
+\begin{itemize}
+\item Icarus Verilog \\
+\smallskip\hskip1cm{}Verilog Simulation (and also a good syntax checker) \\
+\smallskip\hskip1cm{}\url{http://iverilog.icarus.com/}
+
+\bigskip
+\item Qflow (incl. TimberWolf, qrouter and Magic) \\
+\smallskip\hskip1cm{}A complete ASIC synthesis flow, using Yosys and ABC \\
+\smallskip\hskip1cm{}\url{http://opencircuitdesign.com/qflow/}
+
+\bigskip
+\item ABC \\
+\smallskip\hskip1cm{}Logic optimization, technology mapping, and more \\
+\smallskip\hskip1cm{}\url{http://www.eecs.berkeley.edu/~alanmi/abc/}
+\end{itemize}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Yosys needs you}
+
+\begin{frame}{\subsecname}
+\dots as an active user:
+\begin{itemize}
+\item Use Yosys for on your own projects
+\item .. even if you are not using it as final synthesis tool
+\item Join the discussion on the Subreddit
+\item Report bugs and send in feature requests
+\end{itemize}
+
+\bigskip
+\dots as a developer:
+\begin{itemize}
+\item Use Yosys as environment for your (research) work
+\item .. you might also want to look into ABC for logic-level stuff
+\item Fork the project on github or create loadable plugins
+\item We need a VHDL frontend or a good VHDL-to-Verilog converter
+\end{itemize}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Documentation, Downloads, Contacts}
+
+\begin{frame}{\subsecname}
+\begin{itemize}
+\item Website: \\
+\smallskip\hskip1cm\url{http://www.clifford.at/yosys/}
+
+\bigskip
+\item Manual, Command Reference, Application Notes: \\
+\smallskip\hskip1cm\url{http://www.clifford.at/yosys/documentation.html}
+
+\bigskip
+\item Instead of a mailing list we have a SubReddit: \\
+\smallskip\hskip1cm\url{http://www.reddit.com/r/yosys/}
+
+\bigskip
+\item Direct link to the source code: \\
+\smallskip\hskip1cm\url{https://github.com/cliffordwolf/yosys}
+\end{itemize}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Summary}
+
+\begin{frame}{\subsecname}
+\begin{itemize}
+\item Yosys is a powerful tool and framework for Verilog synthesis.
+\item It uses a command-based interface and can be controlled by scripts.
+\item By combining existing commands and implementing new commands Yosys can
+be used in a wide range of application far beyond simple synthesis.
+\end{itemize}
+
+\bigskip
+\bigskip
+\begin{center}
+Questions?
+\end{center}
+
+\bigskip
+\bigskip
+\begin{center}
+\url{http://www.clifford.at/yosys/}
+\end{center}
+\end{frame}
+
diff --git a/manual/PRESENTATION_Intro/.gitignore b/manual/PRESENTATION_Intro/.gitignore
new file mode 100644
index 00000000..d0c4618a
--- /dev/null
+++ b/manual/PRESENTATION_Intro/.gitignore
@@ -0,0 +1,4 @@
+counter_00.dot
+counter_01.dot
+counter_02.dot
+counter_03.dot
diff --git a/manual/PRESENTATION_Intro/Makefile b/manual/PRESENTATION_Intro/Makefile
new file mode 100644
index 00000000..abc354e4
--- /dev/null
+++ b/manual/PRESENTATION_Intro/Makefile
@@ -0,0 +1,10 @@
+
+all: counter_00.pdf counter_01.pdf counter_02.pdf counter_03.pdf
+
+counter_00.pdf: counter.v counter.ys mycells.lib
+ ../../yosys counter.ys
+
+counter_01.pdf: counter_00.pdf
+counter_02.pdf: counter_00.pdf
+counter_03.pdf: counter_00.pdf
+
diff --git a/manual/PRESENTATION_Intro/counter.v b/manual/PRESENTATION_Intro/counter.v
new file mode 100644
index 00000000..36b878e3
--- /dev/null
+++ b/manual/PRESENTATION_Intro/counter.v
@@ -0,0 +1,12 @@
+module counter (clk, rst, en, count);
+
+ input clk, rst, en;
+ output reg [1:0] count;
+
+ always @(posedge clk)
+ if (rst)
+ count <= 2'd0;
+ else if (en)
+ count <= count + 2'd1;
+
+endmodule
diff --git a/manual/PRESENTATION_Intro/counter.ys b/manual/PRESENTATION_Intro/counter.ys
new file mode 100644
index 00000000..cc4e7cd3
--- /dev/null
+++ b/manual/PRESENTATION_Intro/counter.ys
@@ -0,0 +1,27 @@
+# read design
+read_verilog counter.v
+hierarchy -check -top counter
+
+show -notitle -stretch -format pdf -prefix counter_00
+
+# the high-level stuff
+proc; opt; memory; opt; fsm; opt
+
+show -notitle -stretch -format pdf -prefix counter_01
+
+# mapping to internal cell library
+techmap; opt
+
+splitnets -ports;;
+show -notitle -stretch -format pdf -prefix counter_02
+
+# mapping flip-flops to mycells.lib
+dfflibmap -liberty mycells.lib
+
+# mapping logic to mycells.lib
+abc -liberty mycells.lib
+
+# cleanup
+clean
+
+show -notitle -stretch -lib mycells.v -format pdf -prefix counter_03
diff --git a/manual/PRESENTATION_Intro/mycells.lib b/manual/PRESENTATION_Intro/mycells.lib
new file mode 100644
index 00000000..a0204d7e
--- /dev/null
+++ b/manual/PRESENTATION_Intro/mycells.lib
@@ -0,0 +1,38 @@
+library(demo) {
+ cell(BUF) {
+ area: 6;
+ pin(A) { direction: input; }
+ pin(Y) { direction: output;
+ function: "A"; }
+ }
+ cell(NOT) {
+ area: 3;
+ pin(A) { direction: input; }
+ pin(Y) { direction: output;
+ function: "A'"; }
+ }
+ cell(NAND) {
+ area: 4;
+ pin(A) { direction: input; }
+ pin(B) { direction: input; }
+ pin(Y) { direction: output;
+ function: "(A*B)'"; }
+ }
+ cell(NOR) {
+ area: 4;
+ pin(A) { direction: input; }
+ pin(B) { direction: input; }
+ pin(Y) { direction: output;
+ function: "(A+B)'"; }
+ }
+ cell(DFF) {
+ area: 18;
+ ff(IQ, IQN) { clocked_on: C;
+ next_state: D; }
+ pin(C) { direction: input;
+ clock: true; }
+ pin(D) { direction: input; }
+ pin(Q) { direction: output;
+ function: "IQ"; }
+ }
+}
diff --git a/manual/PRESENTATION_Intro/mycells.v b/manual/PRESENTATION_Intro/mycells.v
new file mode 100644
index 00000000..802f5871
--- /dev/null
+++ b/manual/PRESENTATION_Intro/mycells.v
@@ -0,0 +1,23 @@
+
+module NOT(A, Y);
+input A;
+output Y = ~A;
+endmodule
+
+module NAND(A, B, Y);
+input A, B;
+output Y = ~(A & B);
+endmodule
+
+module NOR(A, B, Y);
+input A, B;
+output Y = ~(A | B);
+endmodule
+
+module DFF(C, D, Q);
+input C, D;
+output reg Q;
+always @(posedge C)
+ Q <= D;
+endmodule
+
diff --git a/manual/PRESENTATION_Prog.tex b/manual/PRESENTATION_Prog.tex
new file mode 100644
index 00000000..b85eda89
--- /dev/null
+++ b/manual/PRESENTATION_Prog.tex
@@ -0,0 +1,597 @@
+
+\section{Writing Yosys extensions in C++}
+
+\begin{frame}
+\sectionpage
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Program Components and Data Formats}
+
+\begin{frame}{\subsecname}
+\begin{center}
+\begin{tikzpicture}[scale=0.6, every node/.style={transform shape}]
+ \tikzstyle{process} = [draw, fill=green!10, rectangle, minimum height=3em, minimum width=10em, node distance=15em]
+ \tikzstyle{data} = [draw, fill=blue!10, ellipse, minimum height=3em, minimum width=7em, node distance=15em]
+ \node[process] (vlog) {Verilog Frontend};
+ \node[process, dashed, fill=green!5] (vhdl) [right of=vlog] {VHDL Frontend};
+ \node[process] (ilang) [right of=vhdl] {Other Frontends};
+ \node[data] (ast) [below of=vlog, node distance=5em, xshift=7.5em] {AST};
+ \node[process] (astfe) [below of=ast, node distance=5em] {AST Frontend};
+ \node[data] (rtlil) [below of=astfe, node distance=5em, xshift=7.5em] {RTLIL};
+ \node[process] (pass) [right of=rtlil, node distance=5em, xshift=7.5em] {Passes};
+ \node[process] (vlbe) [below of=rtlil, node distance=7em, xshift=-13em] {Verilog Backend};
+ \node[process] (ilangbe) [below of=rtlil, node distance=7em, xshift=0em] {ILANG Backend};
+ \node[process, fill=green!5] (otherbe) [below of=rtlil, node distance=7em, xshift=+13em] {Other Backends};
+
+ \draw[-latex] (vlog) -- (ast);
+ \draw[-latex] (vhdl) -- (ast);
+ \draw[-latex] (ast) -- (astfe);
+ \draw[-latex] (astfe) -- (rtlil);
+ \draw[-latex] (ilang) -- (rtlil);
+ \draw[latex-latex] (rtlil) -- (pass);
+ \draw[-latex] (rtlil) -- (vlbe);
+ \draw[-latex] (rtlil) -- (ilangbe);
+ \draw[-latex] (rtlil) -- (otherbe);
+\end{tikzpicture}
+\end{center}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Simplified RTLIL Entity-Relationship Diagram}
+
+\begin{frame}{\subsecname}
+Between passses and frontends/backends the design is stored in Yosys' internal
+RTLIL (RTL Intermediate Language) format. For writing Yosys extensions it is
+key to understand this format.
+
+\bigskip
+\begin{center}
+\begin{tikzpicture}[scale=0.6, every node/.style={transform shape}]
+ \tikzstyle{entity} = [draw, fill=gray!10, rectangle, minimum height=3em, minimum width=7em, node distance=5em, font={\ttfamily}]
+ \node[entity] (design) {RTLIL::Design};
+ \node[entity] (module) [right of=design, node distance=11em] {RTLIL::Module} edge [-latex] node[above] {\tiny 1 \hskip3em N} (design);
+
+ \node[entity] (process) [fill=green!10, right of=module, node distance=10em] {RTLIL::Process} (process.west) edge [-latex] (module);
+ \node[entity] (memory) [fill=red!10, below of=process] {RTLIL::Memory} edge [-latex] (module);
+ \node[entity] (wire) [fill=blue!10, above of=process] {RTLIL::Wire} (wire.west) edge [-latex] (module);
+ \node[entity] (cell) [fill=blue!10, above of=wire] {RTLIL::Cell} (cell.west) edge [-latex] (module);
+
+ \node[entity] (case) [fill=green!10, right of=process, node distance=10em] {RTLIL::CaseRule} edge [latex-latex] (process);
+ \node[entity] (sync) [fill=green!10, above of=case] {RTLIL::SyncRule} edge [-latex] (process);
+ \node[entity] (switch) [fill=green!10, below of=case] {RTLIL::SwitchRule} edge [-latex] (case);
+ \draw[latex-] (switch.east) -- ++(1em,0) |- (case.east);
+\end{tikzpicture}
+\end{center}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{RTLIL without memories and processes}
+
+\begin{frame}[fragile]{\subsecname}
+After the commands {\tt proc} and {\tt memory} (or {\tt memory -nomap}), we are
+left with a much simpler version of RTLIL:
+
+\begin{center}
+\begin{tikzpicture}[scale=0.6, every node/.style={transform shape}]
+ \tikzstyle{entity} = [draw, fill=gray!10, rectangle, minimum height=3em, minimum width=7em, node distance=5em, font={\ttfamily}]
+ \node[entity] (design) {RTLIL::Design};
+ \node[entity] (module) [right of=design, node distance=11em] {RTLIL::Module} edge [-latex] node[above] {\tiny 1 \hskip3em N} (design);
+
+ \node[entity] (wire) [fill=blue!10, right of=module, node distance=10em] {RTLIL::Wire} (wire.west) edge [-latex] (module);
+ \node[entity] (cell) [fill=blue!10, above of=wire] {RTLIL::Cell} (cell.west) edge [-latex] (module);
+\end{tikzpicture}
+\end{center}
+
+\bigskip
+Many commands simply choose to only work on this simpler version:
+\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont]
+for (RTLIL::Module *module : design->selected_modules() {
+ if (module->has_memories_warn() || module->has_processes_warn())
+ continue;
+ ....
+}
+\end{lstlisting}
+
+For simplicity we only discuss this version of RTLIL in this presentation.
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Using dump and show commands}
+
+\begin{frame}{\subsecname}
+\begin{itemize}
+\item The {\tt dump} command prints the design (or parts of it) in ILANG format. This is
+a text representation of RTLIL.
+
+\bigskip
+\item The {\tt show} command visualizes how the components in the design are connected.
+\end{itemize}
+
+\bigskip
+When trying to understand what a command does, create a small test case and
+look at the output of {\tt dump} and {\tt show} before and after the command
+has been executed.
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{The RTLIL Data Structures}
+
+\begin{frame}{\subsecname}
+The RTLIL data structures are simple structs utilizing {\tt pool<>} and
+{\tt dict<>} containers (drop-in replacements for {\tt
+std::unordered\_set<>} and {\tt std::unordered\_map<>}).
+
+\bigskip
+\begin{itemize}
+\item Most operations are performed directly on the RTLIL structs without
+setter or getter functions.
+
+\bigskip
+\item In debug builds a consistency checker is run over the in-memory design
+between commands to make sure that the RTLIL representation is intact.
+
+\bigskip
+\item Most RTLIL structs have helper methods that perform the most common operations.
+\end{itemize}
+
+\bigskip
+See {\tt yosys/kernel/rtlil.h} for details.
+\end{frame}
+
+\subsubsection{RTLIL::IdString}
+
+\begin{frame}{\subsubsecname}{}
+{\tt RTLIL::IdString} in many ways behave like a {\tt std::string}. It is used
+for names of RTLIL objects. Internally a RTLIL::IdString object is only a
+single integer.
+
+\medskip
+The first character of a {\tt RTLIL::IdString} specifies if the name is {\it public\/} or {\it private\/}:
+
+\medskip
+\begin{itemize}
+\item {\tt RTLIL::IdString[0] == '\textbackslash\textbackslash'}: \\
+This is a public name. Usually this means it is a name that was declared in a Verilog file.
+
+\bigskip
+\item {\tt RTLIL::IdString[0] == '\$'}: \\
+This is a private name. It was assigned by Yosys.
+\end{itemize}
+
+\bigskip
+Use the {\tt NEW\_ID} macro to create a new unique private name.
+\end{frame}
+
+\subsubsection{RTLIL::Design and RTLIL::Module}
+
+\begin{frame}[t, fragile]{\subsubsecname}
+The {\tt RTLIL::Design} and {\tt RTLIL::Module} structs are the top-level RTLIL
+data structures. Yosys always operates on one active design, but can hold many designs in memory.
+
+\bigskip
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++]
+struct RTLIL::Design {
+ dict<RTLIL::IdString, RTLIL::Module*> modules_;
+ ...
+};
+
+struct RTLIL::Module {
+ RTLIL::IdString name;
+ dict<RTLIL::IdString, RTLIL::Wire*> wires_;
+ dict<RTLIL::IdString, RTLIL::Cell*> cells_;
+ std::vector<RTLIL::SigSig> connections_;
+ ...
+};
+\end{lstlisting}
+
+(Use the various accessor functions instead of directly working with the {\tt *\_} members.)
+\end{frame}
+
+\subsubsection{The RTLIL::Wire Structure}
+
+\begin{frame}[t, fragile]{\subsubsecname}
+Each wire in the design is represented by a {\tt RTLIL::Wire} struct:
+
+\medskip
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++]
+struct RTLIL::Wire {
+ RTLIL::IdString name;
+ int width, start_offset, port_id;
+ bool port_input, port_output;
+ ...
+};
+\end{lstlisting}
+
+\medskip
+\hfil\begin{tabular}{p{3cm}l}
+{\tt width} \dotfill & The total number of bits. E.g. 10 for {\tt [9:0]}. \\
+{\tt start\_offset} \dotfill & The lowest bit index. E.g. 3 for {\tt [5:3]}. \\
+{\tt port\_id} \dotfill & Zero for non-ports. Positive index for ports. \\
+{\tt port\_input} \dotfill & True for {\tt input} and {\tt inout} ports. \\
+{\tt port\_output} \dotfill & True for {\tt output} and {\tt inout} ports. \\
+\end{tabular}
+\end{frame}
+
+\subsubsection{RTLIL::State and RTLIL::Const}
+
+\begin{frame}[t, fragile]{\subsubsecname}
+The {\tt RTLIL::State} enum represents a simple 1-bit logic level:
+
+\smallskip
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++]
+enum RTLIL::State {
+ S0 = 0,
+ S1 = 1,
+ Sx = 2, // undefined value or conflict
+ Sz = 3, // high-impedance / not-connected
+ Sa = 4, // don't care (used only in cases)
+ Sm = 5 // marker (used internally by some passes)
+};
+\end{lstlisting}
+
+\bigskip
+The {\tt RTLIL::Const} struct represents a constant multi-bit value:
+
+\smallskip
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++]
+struct RTLIL::Const {
+ std::vector<RTLIL::State> bits;
+ ...
+};
+\end{lstlisting}
+
+\bigskip
+Notice that Yosys is not using special {\tt VCC} or {\tt GND} driver cells to represent constants. Instead
+constants are part of the RTLIL representation itself.
+\end{frame}
+
+\subsubsection{The RTLIL::SigSpec Structure}
+
+\begin{frame}[t, fragile]{\subsubsecname}
+The {\tt RTLIL::SigSpec} struct represents a signal vector. Each bit can either be a bit from a wire
+or a constant value.
+
+\bigskip
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++]
+struct RTLIL::SigBit
+{
+ RTLIL::Wire *wire;
+ union {
+ RTLIL::State data; // used if wire == NULL
+ int offset; // used if wire != NULL
+ };
+ ...
+};
+
+struct RTLIL::SigSpec {
+ std::vector<RTLIL::SigBit> bits_; // LSB at index 0
+ ...
+};
+\end{lstlisting}
+
+\bigskip
+The {\tt RTLIL::SigSpec} struct has a ton of additional helper methods to compare, analyze, and
+manipulate instances of {\tt RTLIL::SigSpec}.
+\end{frame}
+
+\subsubsection{The RTLIL::Cell Structure}
+
+\begin{frame}[t, fragile]{\subsubsecname (1/2)}
+The {\tt RTLIL::Cell} struct represents an instance of a module or library cell.
+
+\smallskip
+The ports of the cell
+are associated with {\tt RTLIL::SigSpec} instances and the parameters are associated with {\tt RTLIL::Const}
+instances:
+
+\bigskip
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++]
+struct RTLIL::Cell {
+ RTLIL::IdString name, type;
+ dict<RTLIL::IdString, RTLIL::SigSpec> connections_;
+ dict<RTLIL::IdString, RTLIL::Const> parameters;
+ ...
+};
+\end{lstlisting}
+
+\bigskip
+The {\tt type} may refer to another module in the same design, a cell name from a cell library, or a
+cell name from the internal cell library:
+
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{6pt}{7pt}\selectfont]
+$not $pos $neg $and $or $xor $xnor $reduce_and $reduce_or $reduce_xor $reduce_xnor
+$reduce_bool $shl $shr $sshl $sshr $lt $le $eq $ne $eqx $nex $ge $gt $add $sub $mul $div $mod
+$pow $logic_not $logic_and $logic_or $mux $pmux $slice $concat $lut $assert $sr $dff
+$dffsr $adff $dlatch $dlatchsr $memrd $memwr $mem $fsm $_NOT_ $_AND_ $_OR_ $_XOR_ $_MUX_ $_SR_NN_
+$_SR_NP_ $_SR_PN_ $_SR_PP_ $_DFF_N_ $_DFF_P_ $_DFF_NN0_ $_DFF_NN1_ $_DFF_NP0_ $_DFF_NP1_ $_DFF_PN0_
+$_DFF_PN1_ $_DFF_PP0_ $_DFF_PP1_ $_DFFSR_NNN_ $_DFFSR_NNP_ $_DFFSR_NPN_ $_DFFSR_NPP_ $_DFFSR_PNN_
+$_DFFSR_PNP_ $_DFFSR_PPN_ $_DFFSR_PPP_ $_DLATCH_N_ $_DLATCH_P_ $_DLATCHSR_NNN_ $_DLATCHSR_NNP_
+$_DLATCHSR_NPN_ $_DLATCHSR_NPP_ $_DLATCHSR_PNN_ $_DLATCHSR_PNP_ $_DLATCHSR_PPN_ $_DLATCHSR_PPP_
+\end{lstlisting}
+\end{frame}
+
+\begin{frame}[t, fragile]{\subsubsecname (2/2)}
+Simulation models (i.e. {\it documentation\/} :-) for the internal cell library:
+
+\smallskip
+\hskip2em {\tt yosys/techlibs/common/simlib.v} and \\
+\hskip2em {\tt yosys/techlibs/common/simcells.v}
+
+\bigskip
+The lower-case cell types (such as {\tt \$and}) are parameterized cells of variable
+width. This so-called {\it RTL Cells\/} are the cells described in {\tt simlib.v}.
+
+\bigskip
+The upper-case cell types (such as {\tt \$\_AND\_}) are single-bit cells that are not
+parameterized. This so-called {\it Internal Logic Gates} are the cells described
+in {\tt simcells.v}.
+
+\bigskip
+The consistency checker also checks the interfaces to the internal cell library.
+If you want to use private cell types for your own purposes, use the {\tt \$\_\_}-prefix
+to avoid name collisions.
+\end{frame}
+
+\subsubsection{Connecting wires or constant drivers}
+
+\begin{frame}[t, fragile]{\subsubsecname}
+Additional connections between wires or between wires and constants are modelled using
+{\tt RTLIL::Module::connections}:
+
+\bigskip
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++]
+typedef std::pair<RTLIL::SigSpec, RTLIL::SigSpec> RTLIL::SigSig;
+
+struct RTLIL::Module {
+ ...
+ std::vector<RTLIL::SigSig> connections_;
+ ...
+};
+\end{lstlisting}
+
+\bigskip
+{\tt RTLIL::SigSig::first} is the driven signal and {\tt RTLIL::SigSig::second} is the driving signal.
+Example usage (setting wire {\tt foo} to value {\tt 42}):
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++]
+module->connect(module->wire("\\foo"),
+ RTLIL::SigSpec(42, module->wire("\\foo")->width));
+\end{lstlisting}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Creating modules from scratch}
+
+\begin{frame}[t, fragile]{\subsecname}
+Let's create the following module using the RTLIL API:
+
+\smallskip
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=Verilog]
+module absval(input signed [3:0] a, output [3:0] y);
+ assign y = a[3] ? -a : a;
+endmodule
+\end{lstlisting}
+
+\smallskip
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++]
+RTLIL::Module *module = new RTLIL::Module;
+module->name = "\\absval";
+
+RTLIL::Wire *a = module->addWire("\\a", 4);
+a->port_input = true;
+a->port_id = 1;
+
+RTLIL::Wire *y = module->addWire("\\y", 4);
+y->port_output = true;
+y->port_id = 2;
+
+RTLIL::Wire *a_inv = module->addWire(NEW_ID, 4);
+module->addNeg(NEW_ID, a, a_inv, true);
+module->addMux(NEW_ID, a, a_inv, RTLIL::SigSpec(a, 1, 3), y);
+
+module->fixup_ports();
+\end{lstlisting}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Modifying modules}
+
+\begin{frame}{\subsecname}
+Most commands modify existing modules, not create new ones.
+
+When modifying existing modules, stick to the following DOs and DON'Ts:
+
+\begin{itemize}
+\item Do not remove wires. Simply disconnect them and let a successive {\tt clean} command worry about removing it.
+
+\item Use {\tt module->fixup\_ports()} after changing the {\tt port\_*} properties of wires.
+
+\item You can safely remove cells or change the {\tt connections} property of a cell, but be careful when
+changing the size of the {\tt SigSpec} connected to a cell port.
+
+\item Use the {\tt SigMap} helper class (see next slide) when you need a unique handle for each signal bit.
+\end{itemize}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Using the SigMap helper class}
+
+\begin{frame}[t, fragile]{\subsecname}
+Consider the following module:
+
+\smallskip
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=Verilog]
+module test(input a, output x, y);
+ assign x = a, y = a;
+endmodule
+\end{lstlisting}
+
+In this case {\tt a}, {\tt x}, and {\tt y} are all different names for the same signal. However:
+
+\smallskip
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++]
+RTLIL::SigSpec a(module->wire("\\a")), x(module->wire("\\x")),
+ y(module->wire("\\y"));
+log("%d %d %d\n", a == x, x == y, y == a); // will print "0 0 0"
+\end{lstlisting}
+
+The {\tt SigMap} helper class can be used to map all such aliasing signals to a
+unique signal from the group (usually the wire that is directly driven by a cell or port).
+
+\smallskip
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++]
+SigMap sigmap(module);
+log("%d %d %d\n", sigmap(a) == sigmap(x), sigmap(x) == sigmap(y),
+ sigmap(y) == sigmap(a)); // will print "1 1 1"
+\end{lstlisting}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Printing log messages}
+
+\begin{frame}[t, fragile]{\subsecname}
+The {\tt log()} function is a {\tt printf()}-like function that can be used to create log messages.
+
+\medskip
+Use {\tt log\_signal()} to create a C-string for a SigSpec object\footnote[frame]{The pointer returned
+by {\tt log\_signal()} is automatically freed by the log framework at a later time.}:
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++]
+log("Mapped signal x: %s\n", log_signal(sigmap(x)));
+\end{lstlisting}
+
+\medskip
+Use {\tt log\_id()} to create a C-string for an {\tt RTLIL::IdString}:
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++]
+log("Name of this module: %s\n", log_id(module->name));
+\end{lstlisting}
+
+\medskip
+Use {\tt log\_header()} and {\tt log\_push()}/{\tt log\_pop()} to structure log messages:
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++]
+log_header(design, "Doing important stuff!\n");
+log_push();
+for (int i = 0; i < 10; i++)
+ log("Log message #%d.\n", i);
+log_pop();
+\end{lstlisting}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Error handling}
+
+\begin{frame}[t, fragile]{\subsecname}
+Use {\tt log\_error()} to report a non-recoverable error:
+
+\medskip
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++]
+if (design->modules.count(module->name) != 0)
+ log_error("A module with the name %s already exists!\n",
+ RTLIL::id2cstr(module->name));
+\end{lstlisting}
+
+\bigskip
+Use {\tt log\_cmd\_error()} to report a recoverable error:
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++]
+if (design->selection_stack.back().empty())
+ log_cmd_error("This command can't operator on an empty selection!\n");
+\end{lstlisting}
+
+\bigskip
+Use {\tt log\_assert()} and {\tt log\_abort()} instead of {\tt assert()} and {\tt abort()}.
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Creating a command}
+
+\begin{frame}[t, fragile]{\subsecname}
+Simply create a global instance of a class derived from {\tt Pass} to create
+a new yosys command:
+
+\bigskip
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++]
+#include "kernel/yosys.h"
+USING_YOSYS_NAMESPACE
+
+struct MyPass : public Pass {
+ MyPass() : Pass("my_cmd", "just a simple test") { }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log("Arguments to my_cmd:\n");
+ for (auto &arg : args)
+ log(" %s\n", arg.c_str());
+
+ log("Modules in current design:\n");
+ for (auto mod : design->modules())
+ log(" %s (%d wires, %d cells)\n", log_id(mod),
+ GetSize(mod->wires()), GetSize(mod->cells()));
+ }
+} MyPass;
+\end{lstlisting}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Creating a plugin}
+
+\begin{frame}[fragile]{\subsecname}
+Yosys can be extended by adding additional C++ code to the Yosys code base, or
+by loading plugins into Yosys.
+
+\bigskip
+Use the following command to compile a Yosys plugin:
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont]
+yosys-config --exec --cxx --cxxflags --ldflags \
+ -o my_cmd.so -shared my_cmd.cc --ldlibs
+\end{lstlisting}
+
+\bigskip
+Or shorter:
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont]
+yosys-config --build my_cmd.so my_cmd.cc
+\end{lstlisting}
+
+\bigskip
+Load the plugin using the yosys {\tt -m} option:
+\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont]
+yosys -m ./my_cmd.so -p 'my_cmd foo bar'
+\end{lstlisting}
+\end{frame}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Summary}
+
+\begin{frame}{\subsecname}
+\begin{itemize}
+\item Writing Yosys extensions is very straight-forward.
+\item \dots and even simpler if you don't need RTLIL::Memory or RTLIL::Process objects.
+
+\bigskip
+\item Writing synthesis software? Consider learning the Yosys API and make your work
+part of the Yosys framework.
+\end{itemize}
+
+\bigskip
+\bigskip
+\begin{center}
+Questions?
+\end{center}
+
+\bigskip
+\bigskip
+\begin{center}
+\url{http://www.clifford.at/yosys/}
+\end{center}
+\end{frame}
+
diff --git a/manual/PRESENTATION_Prog/.gitignore b/manual/PRESENTATION_Prog/.gitignore
new file mode 100644
index 00000000..ccdd6bd5
--- /dev/null
+++ b/manual/PRESENTATION_Prog/.gitignore
@@ -0,0 +1,2 @@
+my_cmd.so
+my_cmd.d
diff --git a/manual/PRESENTATION_Prog/Makefile b/manual/PRESENTATION_Prog/Makefile
new file mode 100644
index 00000000..794f5c12
--- /dev/null
+++ b/manual/PRESENTATION_Prog/Makefile
@@ -0,0 +1,18 @@
+
+all: test0.log test1.log test2.log
+
+my_cmd.so: my_cmd.cc
+ ../../yosys-config --exec --cxx --cxxflags --ldflags -o my_cmd.so -shared my_cmd.cc --ldlibs
+
+test0.log: my_cmd.so
+ ../../yosys -Ql test0.log_new -m ./my_cmd.so -p 'my_cmd foo bar' absval_ref.v
+ mv test0.log_new test0.log
+
+test1.log: my_cmd.so
+ ../../yosys -Ql test1.log_new -m ./my_cmd.so -p 'clean; test1; dump' absval_ref.v
+ mv test1.log_new test1.log
+
+test2.log: my_cmd.so
+ ../../yosys -Ql test2.log_new -m ./my_cmd.so -p 'test2' sigmap_test.v
+ mv test2.log_new test2.log
+
diff --git a/manual/PRESENTATION_Prog/absval_ref.v b/manual/PRESENTATION_Prog/absval_ref.v
new file mode 100644
index 00000000..ca0a115a
--- /dev/null
+++ b/manual/PRESENTATION_Prog/absval_ref.v
@@ -0,0 +1,3 @@
+module absval_ref(input signed [3:0] a, output [3:0] y);
+ assign y = a[3] ? -a : a;
+endmodule
diff --git a/manual/PRESENTATION_Prog/my_cmd.cc b/manual/PRESENTATION_Prog/my_cmd.cc
new file mode 100644
index 00000000..d99bfe1e
--- /dev/null
+++ b/manual/PRESENTATION_Prog/my_cmd.cc
@@ -0,0 +1,76 @@
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct MyPass : public Pass {
+ MyPass() : Pass("my_cmd", "just a simple test") { }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log("Arguments to my_cmd:\n");
+ for (auto &arg : args)
+ log(" %s\n", arg.c_str());
+
+ log("Modules in current design:\n");
+ for (auto mod : design->modules())
+ log(" %s (%zd wires, %zd cells)\n", log_id(mod),
+ GetSize(mod->wires()), GetSize(mod->cells()));
+ }
+} MyPass;
+
+
+struct Test1Pass : public Pass {
+ Test1Pass() : Pass("test1", "creating the absval module") { }
+ virtual void execute(std::vector<std::string>, RTLIL::Design *design)
+ {
+ if (design->has("\\absval") != 0)
+ log_error("A module with the name absval already exists!\n");
+
+ RTLIL::Module *module = design->addModule("\\absval");
+ log("Name of this module: %s\n", log_id(module));
+
+ RTLIL::Wire *a = module->addWire("\\a", 4);
+ a->port_input = true;
+ a->port_id = 1;
+
+ RTLIL::Wire *y = module->addWire("\\y", 4);
+ y->port_output = true;
+ y->port_id = 2;
+
+ RTLIL::Wire *a_inv = module->addWire(NEW_ID, 4);
+ module->addNeg(NEW_ID, a, a_inv, true);
+ module->addMux(NEW_ID, a, a_inv, RTLIL::SigSpec(a, 3), y);
+
+ module->fixup_ports();
+ }
+} Test1Pass;
+
+
+struct Test2Pass : public Pass {
+ Test2Pass() : Pass("test2", "demonstrating sigmap on test module") { }
+ virtual void execute(std::vector<std::string>, RTLIL::Design *design)
+ {
+ if (design->selection_stack.back().empty())
+ log_cmd_error("This command can't operator on an empty selection!\n");
+
+ RTLIL::Module *module = design->modules_.at("\\test");
+
+ RTLIL::SigSpec a(module->wire("\\a")), x(module->wire("\\x")), y(module->wire("\\y"));
+ log("%d %d %d\n", a == x, x == y, y == a); // will print "0 0 0"
+
+ SigMap sigmap(module);
+ log("%d %d %d\n", sigmap(a) == sigmap(x), sigmap(x) == sigmap(y),
+ sigmap(y) == sigmap(a)); // will print "1 1 1"
+
+ log("Mapped signal x: %s\n", log_signal(sigmap(x)));
+
+ log_header(design, "Doing important stuff!\n");
+ log_push();
+ for (int i = 0; i < 10; i++)
+ log("Log message #%d.\n", i);
+ log_pop();
+ }
+} Test2Pass;
+
+PRIVATE_NAMESPACE_END
diff --git a/manual/PRESENTATION_Prog/sigmap_test.v b/manual/PRESENTATION_Prog/sigmap_test.v
new file mode 100644
index 00000000..18dcf5eb
--- /dev/null
+++ b/manual/PRESENTATION_Prog/sigmap_test.v
@@ -0,0 +1,3 @@
+module test(input a, output x, y);
+assign x = a, y = a;
+endmodule
diff --git a/manual/appnotes.sh b/manual/appnotes.sh
new file mode 100755
index 00000000..0ae52862
--- /dev/null
+++ b/manual/appnotes.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+set -ex
+for job in APPNOTE_010_Verilog_to_BLIF APPNOTE_011_Design_Investigation APPNOTE_012_Verilog_to_BTOR
+do
+ [ -f $job.ok -a $job.ok -nt $job.tex ] && continue
+ if [ -f $job/make.sh ]; then
+ cd $job
+ bash make.sh
+ cd ..
+ fi
+ old_md5=$([ -f $job.aux ] && md5sum < $job.aux || true)
+ while
+ pdflatex -shell-escape -halt-on-error $job.tex || exit
+ new_md5=$(md5sum < $job.aux)
+ [ "$old_md5" != "$new_md5" ]
+ do
+ old_md5="$new_md5"
+ done
+ touch $job.ok
+done
+
diff --git a/manual/clean.sh b/manual/clean.sh
new file mode 100755
index 00000000..11c2e7bf
--- /dev/null
+++ b/manual/clean.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+for f in $( find . -name .gitignore ); do sed -re "s,^,find ${f%.gitignore} -name ',; s,$,' | xargs rm -f,;" $f; done | bash -v
diff --git a/manual/command-reference-manual.tex b/manual/command-reference-manual.tex
new file mode 100644
index 00000000..8af8ccdd
--- /dev/null
+++ b/manual/command-reference-manual.tex
@@ -0,0 +1,4466 @@
+% Generated using the yosys 'help -write-tex-command-reference-manual' command.
+
+\section{abc -- use ABC for technology mapping}
+\label{cmd:abc}
+\begin{lstlisting}[numbers=left,frame=single]
+ abc [options] [selection]
+
+This pass uses the ABC tool [1] for technology mapping of yosys's internal gate
+library to a target architecture.
+
+ -exe <command>
+ use the specified command instead of "<yosys-bindir>/yosys-abc" to execute ABC.
+ This can e.g. be used to call a specific version of ABC or a wrapper.
+
+ -script <file>
+ use the specified ABC script file instead of the default script.
+
+ if <file> starts with a plus sign (+), then the rest of the filename
+ string is interpreted as the command string to be passed to ABC. The
+ leading plus sign is removed and all commas (,) in the string are
+ replaced with blanks before the string is passed to ABC.
+
+ if no -script parameter is given, the following scripts are used:
+
+ for -liberty without -constr:
+ strash; dc2; scorr; ifraig; retime -o {D}; strash; dch -f;
+ map {D}
+
+ for -liberty with -constr:
+ strash; dc2; scorr; ifraig; retime -o {D}; strash; dch -f;
+ map {D}; buffer; upsize {D}; dnsize {D}; stime -p
+
+ for -lut/-luts (only one LUT size):
+ strash; dc2; scorr; ifraig; retime -o; strash; dch -f; if; mfs;
+ lutpack
+
+ for -lut/-luts (different LUT sizes):
+ strash; dc2; scorr; ifraig; retime -o; strash; dch -f; if; mfs
+
+ for -sop:
+ strash; dc2; scorr; ifraig; retime -o; strash; dch -f;
+ cover {I} {P}
+
+ otherwise:
+ strash; dc2; scorr; ifraig; retime -o; strash; dch -f; map
+
+ -fast
+ use different default scripts that are slightly faster (at the cost
+ of output quality):
+
+ for -liberty without -constr:
+ retime -o {D}; map {D}
+
+ for -liberty with -constr:
+ retime -o {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p
+
+ for -lut/-luts:
+ retime -o; if
+
+ for -sop:
+ retime -o; cover -I {I} -P {P}
+
+ otherwise:
+ retime -o; map
+
+ -liberty <file>
+ generate netlists for the specified cell library (using the liberty
+ file format).
+
+ -constr <file>
+ pass this file with timing constraints to ABC. use with -liberty.
+
+ a constr file contains two lines:
+ set_driving_cell <cell_name>
+ set_load <floating_point_number>
+
+ the set_driving_cell statement defines which cell type is assumed to
+ drive the primary inputs and the set_load statement sets the load in
+ femtofarads for each primary output.
+
+ -D <picoseconds>
+ set delay target. the string {D} in the default scripts above is
+ replaced by this option when used, and an empty string otherwise.
+
+ -I <num>
+ maximum number of SOP inputs.
+ (replaces {I} in the default scripts above)
+
+ -P <num>
+ maximum number of SOP products.
+ (replaces {P} in the default scripts above)
+
+ -lut <width>
+ generate netlist using luts of (max) the specified width.
+
+ -lut <w1>:<w2>
+ generate netlist using luts of (max) the specified width <w2>. All
+ luts with width <= <w1> have constant cost. for luts larger than <w1>
+ the area cost doubles with each additional input bit. the delay cost
+ is still constant for all lut widths.
+
+ -luts <cost1>,<cost2>,<cost3>,<sizeN>:<cost4-N>,..
+ generate netlist using luts. Use the specified costs for luts with 1,
+ 2, 3, .. inputs.
+
+ -sop
+ map to sum-of-product cells and inverters
+
+ -g type1,type2,...
+ Map the the specified list of gate types. Supported gates types are:
+ AND, NAND, OR, NOR, XOR, XNOR, MUX, AOI3, OAI3, AOI4, OAI4.
+ (The NOT gate is always added to this list automatically.)
+
+ -dff
+ also pass $_DFF_?_ and $_DFFE_??_ cells through ABC. modules with many
+ clock domains are automatically partitioned in clock domains and each
+ domain is passed through ABC independently.
+
+ -clk [!]<clock-signal-name>[,[!]<enable-signal-name>]
+ use only the specified clock domain. this is like -dff, but only FF
+ cells that belong to the specified clock domain are used.
+
+ -keepff
+ set the "keep" attribute on flip-flop output wires. (and thus preserve
+ them, for example for equivalence checking.)
+
+ -nocleanup
+ when this option is used, the temporary files created by this pass
+ are not removed. this is useful for debugging.
+
+ -showtmp
+ print the temp dir name in log. usually this is suppressed so that the
+ command output is identical across runs.
+
+ -markgroups
+ set a 'abcgroup' attribute on all objects created by ABC. The value of
+ this attribute is a unique integer for each ABC process started. This
+ is useful for debugging the partitioning of clock domains.
+
+When neither -liberty nor -lut is used, the Yosys standard cell library is
+loaded into ABC before the ABC script is executed.
+
+This pass does not operate on modules with unprocessed processes in it.
+(I.e. the 'proc' pass should be used first to convert processes to netlists.)
+
+[1] http://www.eecs.berkeley.edu/~alanmi/abc/
+\end{lstlisting}
+
+\section{add -- add objects to the design}
+\label{cmd:add}
+\begin{lstlisting}[numbers=left,frame=single]
+ add <command> [selection]
+
+This command adds objects to the design. It operates on all fully selected
+modules. So e.g. 'add -wire foo' will add a wire foo to all selected modules.
+
+
+ add {-wire|-input|-inout|-output} <name> <width> [selection]
+
+Add a wire (input, inout, output port) with the given name and width. The
+command will fail if the object exists already and has different properties
+than the object to be created.
+
+
+ add -global_input <name> <width> [selection]
+
+Like 'add -input', but also connect the signal between instances of the
+selected modules.
+\end{lstlisting}
+
+\section{aigmap -- map logic to and-inverter-graph circuit}
+\label{cmd:aigmap}
+\begin{lstlisting}[numbers=left,frame=single]
+ aigmap [options] [selection]
+
+Replace all logic cells with circuits made of only $_AND_ and
+$_NOT_ cells.
+
+ -nand
+ Enable creation of $_NAND_ cells
+\end{lstlisting}
+
+\section{alumacc -- extract ALU and MACC cells}
+\label{cmd:alumacc}
+\begin{lstlisting}[numbers=left,frame=single]
+ alumacc [selection]
+
+This pass translates arithmetic operations like $add, $mul, $lt, etc. to $alu
+and $macc cells.
+\end{lstlisting}
+
+\section{assertpmux -- convert internal signals to module ports}
+\label{cmd:assertpmux}
+\begin{lstlisting}[numbers=left,frame=single]
+ assertpmux [options] [selection]
+
+This command adds asserts to the design that assert that all parallel muxes
+($pmux cells) have a maximum of one of their inputs enable at any time.
+
+ -noinit
+ do not enforce the pmux condition during the init state
+
+ -always
+ usually the $pmux condition is only checked when the $pmux output
+ is used be the mux tree it drives. this option will deactivate this
+ additional constrained and check the $pmux condition always.
+\end{lstlisting}
+
+\section{attrmap -- renaming attributes}
+\label{cmd:attrmap}
+\begin{lstlisting}[numbers=left,frame=single]
+ attrmap [options] [selection]
+
+This command renames attributes and/or mapps key/value pairs to
+other key/value pairs.
+
+ -tocase <name>
+ Match attribute names case-insensitively and set it to the specified
+ name.
+
+ -rename <old_name> <new_name>
+ Rename attributes as specified
+
+ -map <old_name>=<old_value> <new_name>=<new_value>
+ Map key/value pairs as indicated.
+
+ -imap <old_name>=<old_value> <new_name>=<new_value>
+ Like -map, but use case-insensitive match for <old_value> when
+ it is a string value.
+
+ -remove <name>=<value>
+ Remove attributes matching this pattern.
+
+ -modattr
+ Operate on module attributes instead of attributes on wires and cells.
+
+For example, mapping Xilinx-style "keep" attributes to Yosys-style:
+
+ attrmap -tocase keep -imap keep="true" keep=1 \
+ -imap keep="false" keep=0 -remove keep=0
+\end{lstlisting}
+
+\section{attrmvcp -- move or copy attributes from wires to driving cells}
+\label{cmd:attrmvcp}
+\begin{lstlisting}[numbers=left,frame=single]
+ attrmvcp [options] [selection]
+
+Move or copy attributes on wires to the cells driving them.
+
+ -copy
+ By default, attributes are moved. This will only add
+ the attribute to the cell, without removing it from
+ the wire.
+
+ -purge
+ If no selected cell consumes the attribute, then it is
+ left on the wire by default. This option will cause the
+ attribute to be removed from the wire, even if no selected
+ cell takes it.
+
+ -driven
+ By default, attriburtes are moved to the cell driving the
+ wire. With this option set it will be moved to the cell
+ driven by the wire instead.
+
+ -attr <attrname>
+ Move or copy this attribute. This option can be used
+ multiple times.
+\end{lstlisting}
+
+\section{cd -- a shortcut for 'select -module <name>'}
+\label{cmd:cd}
+\begin{lstlisting}[numbers=left,frame=single]
+ cd <modname>
+
+This is just a shortcut for 'select -module <modname>'.
+
+
+ cd <cellname>
+
+When no module with the specified name is found, but there is a cell
+with the specified name in the current module, then this is equivalent
+to 'cd <celltype>'.
+
+ cd ..
+
+This is just a shortcut for 'select -clear'.
+\end{lstlisting}
+
+\section{check -- check for obvious problems in the design}
+\label{cmd:check}
+\begin{lstlisting}[numbers=left,frame=single]
+ check [options] [selection]
+
+This pass identifies the following problems in the current design:
+
+ - combinatorial loops
+
+ - two or more conflicting drivers for one wire
+
+ - used wires that do not have a driver
+
+When called with -noinit then this command also checks for wires which have
+the 'init' attribute set.
+
+When called with -assert then the command will produce an error if any
+problems are found in the current design.
+\end{lstlisting}
+
+\section{chparam -- re-evaluate modules with new parameters}
+\label{cmd:chparam}
+\begin{lstlisting}[numbers=left,frame=single]
+ chparam [ -set name value ]... [selection]
+
+Re-evaluate the selected modules with new parameters. String values must be
+passed in double quotes (").
+
+
+ chparam -list [selection]
+
+List the available parameters of the selected modules.
+\end{lstlisting}
+
+\section{clean -- remove unused cells and wires}
+\label{cmd:clean}
+\begin{lstlisting}[numbers=left,frame=single]
+ clean [options] [selection]
+
+This is identical to 'opt_clean', but less verbose.
+
+When commands are separated using the ';;' token, this command will be executed
+between the commands.
+
+When commands are separated using the ';;;' token, this command will be executed
+in -purge mode between the commands.
+\end{lstlisting}
+
+\section{clk2fflogic -- convert clocked FFs to generic \$ff cells}
+\label{cmd:clk2fflogic}
+\begin{lstlisting}[numbers=left,frame=single]
+ clk2fflogic [options] [selection]
+
+This command replaces clocked flip-flops with generic $ff cells that use the
+implicit global clock. This is useful for formal verification of designs with
+multiple clocks.
+\end{lstlisting}
+
+\section{connect -- create or remove connections}
+\label{cmd:connect}
+\begin{lstlisting}[numbers=left,frame=single]
+ connect [-nomap] [-nounset] -set <lhs-expr> <rhs-expr>
+
+Create a connection. This is equivalent to adding the statement 'assign
+<lhs-expr> = <rhs-expr>;' to the Verilog input. Per default, all existing
+drivers for <lhs-expr> are unconnected. This can be overwritten by using
+the -nounset option.
+
+
+ connect [-nomap] -unset <expr>
+
+Unconnect all existing drivers for the specified expression.
+
+
+ connect [-nomap] -port <cell> <port> <expr>
+
+Connect the specified cell port to the specified cell port.
+
+
+Per default signal alias names are resolved and all signal names are mapped
+the the signal name of the primary driver. Using the -nomap option deactivates
+this behavior.
+
+The connect command operates in one module only. Either only one module must
+be selected or an active module must be set using the 'cd' command.
+
+This command does not operate on module with processes.
+\end{lstlisting}
+
+\section{connwrappers -- replace undef values with defined constants}
+\label{cmd:connwrappers}
+\begin{lstlisting}[numbers=left,frame=single]
+ connwrappers [options] [selection]
+
+Wrappers are used in coarse-grain synthesis to wrap cells with smaller ports
+in wrapper cells with a (larger) constant port size. I.e. the upper bits
+of the wrapper output are signed/unsigned bit extended. This command uses this
+knowledge to rewire the inputs of the driven cells to match the output of
+the driving cell.
+
+ -signed <cell_type> <port_name> <width_param>
+ -unsigned <cell_type> <port_name> <width_param>
+ consider the specified signed/unsigned wrapper output
+
+ -port <cell_type> <port_name> <width_param> <sign_param>
+ use the specified parameter to decide if signed or unsigned
+
+The options -signed, -unsigned, and -port can be specified multiple times.
+\end{lstlisting}
+
+\section{copy -- copy modules in the design}
+\label{cmd:copy}
+\begin{lstlisting}[numbers=left,frame=single]
+ copy old_name new_name
+
+Copy the specified module. Note that selection patterns are not supported
+by this command.
+\end{lstlisting}
+
+\section{cover -- print code coverage counters}
+\label{cmd:cover}
+\begin{lstlisting}[numbers=left,frame=single]
+ cover [options] [pattern]
+
+Print the code coverage counters collected using the cover() macro in the Yosys
+C++ code. This is useful to figure out what parts of Yosys are utilized by a
+test bench.
+
+ -q
+ Do not print output to the normal destination (console and/or log file)
+
+ -o file
+ Write output to this file, truncate if exists.
+
+ -a file
+ Write output to this file, append if exists.
+
+ -d dir
+ Write output to a newly created file in the specified directory.
+
+When one or more pattern (shell wildcards) are specified, then only counters
+matching at least one pattern are printed.
+
+
+It is also possible to instruct Yosys to print the coverage counters on program
+exit to a file using environment variables:
+
+ YOSYS_COVER_DIR="{dir-name}" yosys {args}
+
+ This will create a file (with an auto-generated name) in this
+ directory and write the coverage counters to it.
+
+ YOSYS_COVER_FILE="{file-name}" yosys {args}
+
+ This will append the coverage counters to the specified file.
+
+
+Hint: Use the following AWK command to consolidate Yosys coverage files:
+
+ gawk '{ p[$3] = $1; c[$3] += $2; } END { for (i in p)
+ printf "%-60s %10d %s\n", p[i], c[i], i; }' {files} | sort -k3
+
+
+Coverage counters are only available in Yosys for Linux.
+\end{lstlisting}
+
+\section{delete -- delete objects in the design}
+\label{cmd:delete}
+\begin{lstlisting}[numbers=left,frame=single]
+ delete [selection]
+
+Deletes the selected objects. This will also remove entire modules, if the
+whole module is selected.
+
+
+ delete {-input|-output|-port} [selection]
+
+Does not delete any object but removes the input and/or output flag on the
+selected wires, thus 'deleting' module ports.
+\end{lstlisting}
+
+\section{deminout -- demote inout ports to input or output}
+\label{cmd:deminout}
+\begin{lstlisting}[numbers=left,frame=single]
+ deminout [options] [selection]
+
+"Demote" inout ports to input or output ports, if possible.
+\end{lstlisting}
+
+\section{design -- save, restore and reset current design}
+\label{cmd:design}
+\begin{lstlisting}[numbers=left,frame=single]
+ design -reset
+
+Clear the current design.
+
+
+ design -save <name>
+
+Save the current design under the given name.
+
+
+ design -stash <name>
+
+Save the current design under the given name and then clear the current design.
+
+
+ design -push
+
+Push the current design to the stack and then clear the current design.
+
+
+ design -pop
+
+Reset the current design and pop the last design from the stack.
+
+
+ design -load <name>
+
+Reset the current design and load the design previously saved under the given
+name.
+
+
+ design -copy-from <name> [-as <new_mod_name>] <selection>
+
+Copy modules from the specified design into the current one. The selection is
+evaluated in the other design.
+
+
+ design -copy-to <name> [-as <new_mod_name>] [selection]
+
+Copy modules from the current design into the specified one.
+\end{lstlisting}
+
+\section{dff2dffe -- transform \$dff cells to \$dffe cells}
+\label{cmd:dff2dffe}
+\begin{lstlisting}[numbers=left,frame=single]
+ dff2dffe [options] [selection]
+
+This pass transforms $dff cells driven by a tree of multiplexers with one or
+more feedback paths to $dffe cells. It also works on gate-level cells such as
+$_DFF_P_, $_DFF_N_ and $_MUX_.
+
+ -unmap
+ operate in the opposite direction: replace $dffe cells with combinations
+ of $dff and $mux cells. the options below are ignore in unmap mode.
+
+ -direct <internal_gate_type> <external_gate_type>
+ map directly to external gate type. <internal_gate_type> can
+ be any internal gate-level FF cell (except $_DFFE_??_). the
+ <external_gate_type> is the cell type name for a cell with an
+ identical interface to the <internal_gate_type>, except it
+ also has an high-active enable port 'E'.
+ Usually <external_gate_type> is an intermediate cell type
+ that is then translated to the final type using 'techmap'.
+
+ -direct-match <pattern>
+ like -direct for all DFF cell types matching the expression.
+ this will use $__DFFE_* as <external_gate_type> matching the
+ internal gate type $_DFF_*_, except for $_DFF_[NP]_, which is
+ converted to $_DFFE_[NP]_.
+\end{lstlisting}
+
+\section{dffinit -- set INIT param on FF cells}
+\label{cmd:dffinit}
+\begin{lstlisting}[numbers=left,frame=single]
+ dffinit [options] [selection]
+
+This pass sets an FF cell parameter to the the initial value of the net it
+drives. (This is primarily used in FPGA flows.)
+
+ -ff <cell_name> <output_port> <init_param>
+ operate on the specified cell type. this option can be used
+ multiple times.
+\end{lstlisting}
+
+\section{dfflibmap -- technology mapping of flip-flops}
+\label{cmd:dfflibmap}
+\begin{lstlisting}[numbers=left,frame=single]
+ dfflibmap [-prepare] -liberty <file> [selection]
+
+Map internal flip-flop cells to the flip-flop cells in the technology
+library specified in the given liberty file.
+
+This pass may add inverters as needed. Therefore it is recommended to
+first run this pass and then map the logic paths to the target technology.
+
+When called with -prepare, this command will convert the internal FF cells
+to the internal cell types that best match the cells found in the given
+liberty file.
+\end{lstlisting}
+
+\section{dffsr2dff -- convert DFFSR cells to simpler FF cell types}
+\label{cmd:dffsr2dff}
+\begin{lstlisting}[numbers=left,frame=single]
+ dffsr2dff [options] [selection]
+
+This pass converts DFFSR cells ($dffsr, $_DFFSR_???_) and ADFF cells ($adff,
+$_DFF_???_) to simpler FF cell types when any of the set/reset inputs is unused.
+\end{lstlisting}
+
+\section{dump -- print parts of the design in ilang format}
+\label{cmd:dump}
+\begin{lstlisting}[numbers=left,frame=single]
+ dump [options] [selection]
+
+Write the selected parts of the design to the console or specified file in
+ilang format.
+
+ -m
+ also dump the module headers, even if only parts of a single
+ module is selected
+
+ -n
+ only dump the module headers if the entire module is selected
+
+ -o <filename>
+ write to the specified file.
+
+ -a <filename>
+ like -outfile but append instead of overwrite
+\end{lstlisting}
+
+\section{echo -- turning echoing back of commands on and off}
+\label{cmd:echo}
+\begin{lstlisting}[numbers=left,frame=single]
+ echo on
+
+Print all commands to log before executing them.
+
+
+ echo off
+
+Do not print all commands to log before executing them. (default)
+\end{lstlisting}
+
+\section{edgetypes -- list all types of edges in selection}
+\label{cmd:edgetypes}
+\begin{lstlisting}[numbers=left,frame=single]
+ edgetypes [options] [selection]
+
+This command lists all unique types of 'edges' found in the selection. An 'edge'
+is a 4-tuple of source and sink cell type and port name.
+\end{lstlisting}
+
+\section{equiv\_add -- add a \$equiv cell}
+\label{cmd:equiv_add}
+\begin{lstlisting}[numbers=left,frame=single]
+ equiv_add [-try] gold_sig gate_sig
+
+This command adds an $equiv cell for the specified signals.
+
+
+ equiv_add [-try] -cell gold_cell gate_cell
+
+This command adds $equiv cells for the ports of the specified cells.
+\end{lstlisting}
+
+\section{equiv\_induct -- proving \$equiv cells using temporal induction}
+\label{cmd:equiv_induct}
+\begin{lstlisting}[numbers=left,frame=single]
+ equiv_induct [options] [selection]
+
+Uses a version of temporal induction to prove $equiv cells.
+
+Only selected $equiv cells are proven and only selected cells are used to
+perform the proof.
+
+ -undef
+ enable modelling of undef states
+
+ -seq <N>
+ the max. number of time steps to be considered (default = 4)
+
+This command is very effective in proving complex sequential circuits, when
+the internal state of the circuit quickly propagates to $equiv cells.
+
+However, this command uses a weak definition of 'equivalence': This command
+proves that the two circuits will not diverge after they produce equal
+outputs (observable points via $equiv) for at least <N> cycles (the <N>
+specified via -seq).
+
+Combined with simulation this is very powerful because simulation can give
+you confidence that the circuits start out synced for at least <N> cycles
+after reset.
+\end{lstlisting}
+
+\section{equiv\_make -- prepare a circuit for equivalence checking}
+\label{cmd:equiv_make}
+\begin{lstlisting}[numbers=left,frame=single]
+ equiv_make [options] gold_module gate_module equiv_module
+
+This creates a module annotated with $equiv cells from two presumably
+equivalent modules. Use commands such as 'equiv_simple' and 'equiv_status'
+to work with the created equivalent checking module.
+
+ -inames
+ Also match cells and wires with $... names.
+
+ -blacklist <file>
+ Do not match cells or signals that match the names in the file.
+
+ -encfile <file>
+ Match FSM encodings using the description from the file.
+ See 'help fsm_recode' for details.
+
+Note: The circuit created by this command is not a miter (with something like
+a trigger output), but instead uses $equiv cells to encode the equivalence
+checking problem. Use 'miter -equiv' if you want to create a miter circuit.
+\end{lstlisting}
+
+\section{equiv\_mark -- mark equivalence checking regions}
+\label{cmd:equiv_mark}
+\begin{lstlisting}[numbers=left,frame=single]
+ equiv_mark [options] [selection]
+
+This command marks the regions in an equivalence checking module. Region 0 is
+the proven part of the circuit. Regions with higher numbers are connected
+unproven subcricuits. The integer attribute 'equiv_region' is set on all
+wires and cells.
+\end{lstlisting}
+
+\section{equiv\_miter -- extract miter from equiv circuit}
+\label{cmd:equiv_miter}
+\begin{lstlisting}[numbers=left,frame=single]
+ equiv_miter [options] miter_module [selection]
+
+This creates a miter module for further analysis of the selected $equiv cells.
+
+ -trigger
+ Create a trigger output
+
+ -cmp
+ Create cmp_* outputs for individual unproven $equiv cells
+
+ -assert
+ Create a $assert cell for each unproven $equiv cell
+
+ -undef
+ Create compare logic that handles undefs correctly
+\end{lstlisting}
+
+\section{equiv\_purge -- purge equivalence checking module}
+\label{cmd:equiv_purge}
+\begin{lstlisting}[numbers=left,frame=single]
+ equiv_purge [options] [selection]
+
+This command removes the proven part of an equivalence checking module, leaving
+only the unproven segments in the design. This will also remove and add module
+ports as needed.
+\end{lstlisting}
+
+\section{equiv\_remove -- remove \$equiv cells}
+\label{cmd:equiv_remove}
+\begin{lstlisting}[numbers=left,frame=single]
+ equiv_remove [options] [selection]
+
+This command removes the selected $equiv cells. If neither -gold nor -gate is
+used then only proven cells are removed.
+
+ -gold
+ keep gold circuit
+
+ -gate
+ keep gate circuit
+\end{lstlisting}
+
+\section{equiv\_simple -- try proving simple \$equiv instances}
+\label{cmd:equiv_simple}
+\begin{lstlisting}[numbers=left,frame=single]
+ equiv_simple [options] [selection]
+
+This command tries to prove $equiv cells using a simple direct SAT approach.
+
+ -v
+ verbose output
+
+ -undef
+ enable modelling of undef states
+
+ -nogroup
+ disabling grouping of $equiv cells by output wire
+
+ -seq <N>
+ the max. number of time steps to be considered (default = 1)
+\end{lstlisting}
+
+\section{equiv\_status -- print status of equivalent checking module}
+\label{cmd:equiv_status}
+\begin{lstlisting}[numbers=left,frame=single]
+ equiv_status [options] [selection]
+
+This command prints status information for all selected $equiv cells.
+
+ -assert
+ produce an error if any unproven $equiv cell is found
+\end{lstlisting}
+
+\section{equiv\_struct -- structural equivalence checking}
+\label{cmd:equiv_struct}
+\begin{lstlisting}[numbers=left,frame=single]
+ equiv_struct [options] [selection]
+
+This command adds additional $equiv cells based on the assumption that the
+gold and gate circuit are structurally equivalent. Note that this can introduce
+bad $equiv cells in cases where the netlists are not structurally equivalent,
+for example when analyzing circuits with cells with commutative inputs. This
+command will also de-duplicate gates.
+
+ -fwd
+ by default this command performans forward sweeps until nothing can
+ be merged by forwards sweeps, then backward sweeps until forward
+ sweeps are effective again. with this option set only forward sweeps
+ are performed.
+
+ -fwonly <cell_type>
+ add the specified cell type to the list of cell types that are only
+ merged in forward sweeps and never in backward sweeps. $equiv is in
+ this list automatically.
+
+ -icells
+ by default, the internal RTL and gate cell types are ignored. add
+ this option to also process those cell types with this command.
+
+ -maxiter <N>
+ maximum number of iterations to run before aborting
+\end{lstlisting}
+
+\section{eval -- evaluate the circuit given an input}
+\label{cmd:eval}
+\begin{lstlisting}[numbers=left,frame=single]
+ eval [options] [selection]
+
+This command evaluates the value of a signal given the value of all required
+inputs.
+
+ -set <signal> <value>
+ set the specified signal to the specified value.
+
+ -set-undef
+ set all unspecified source signals to undef (x)
+
+ -table <signal>
+ create a truth table using the specified input signals
+
+ -show <signal>
+ show the value for the specified signal. if no -show option is passed
+ then all output ports of the current module are used.
+\end{lstlisting}
+
+\section{expose -- convert internal signals to module ports}
+\label{cmd:expose}
+\begin{lstlisting}[numbers=left,frame=single]
+ expose [options] [selection]
+
+This command exposes all selected internal signals of a module as additional
+outputs.
+
+ -dff
+ only consider wires that are directly driven by register cell.
+
+ -cut
+ when exposing a wire, create an input/output pair and cut the internal
+ signal path at that wire.
+
+ -shared
+ only expose those signals that are shared among the selected modules.
+ this is useful for preparing modules for equivalence checking.
+
+ -evert
+ also turn connections to instances of other modules to additional
+ inputs and outputs and remove the module instances.
+
+ -evert-dff
+ turn flip-flops to sets of inputs and outputs.
+
+ -sep <separator>
+ when creating new wire/port names, the original object name is suffixed
+ with this separator (default: '.') and the port name or a type
+ designator for the exposed signal.
+\end{lstlisting}
+
+\section{extract -- find subcircuits and replace them with cells}
+\label{cmd:extract}
+\begin{lstlisting}[numbers=left,frame=single]
+ extract -map <map_file> [options] [selection]
+ extract -mine <out_file> [options] [selection]
+
+This pass looks for subcircuits that are isomorphic to any of the modules
+in the given map file and replaces them with instances of this modules. The
+map file can be a Verilog source file (*.v) or an ilang file (*.il).
+
+ -map <map_file>
+ use the modules in this file as reference. This option can be used
+ multiple times.
+
+ -map %<design-name>
+ use the modules in this in-memory design as reference. This option can
+ be used multiple times.
+
+ -verbose
+ print debug output while analyzing
+
+ -constports
+ also find instances with constant drivers. this may be much
+ slower than the normal operation.
+
+ -nodefaultswaps
+ normally builtin port swapping rules for internal cells are used per
+ default. This turns that off, so e.g. 'a^b' does not match 'b^a'
+ when this option is used.
+
+ -compat <needle_type> <haystack_type>
+ Per default, the cells in the map file (needle) must have the
+ type as the cells in the active design (haystack). This option
+ can be used to register additional pairs of types that should
+ match. This option can be used multiple times.
+
+ -swap <needle_type> <port1>,<port2>[,...]
+ Register a set of swappable ports for a needle cell type.
+ This option can be used multiple times.
+
+ -perm <needle_type> <port1>,<port2>[,...] <portA>,<portB>[,...]
+ Register a valid permutation of swappable ports for a needle
+ cell type. This option can be used multiple times.
+
+ -cell_attr <attribute_name>
+ Attributes on cells with the given name must match.
+
+ -wire_attr <attribute_name>
+ Attributes on wires with the given name must match.
+
+ -ignore_parameters
+ Do not use parameters when matching cells.
+
+ -ignore_param <cell_type> <parameter_name>
+ Do not use this parameter when matching cells.
+
+This pass does not operate on modules with unprocessed processes in it.
+(I.e. the 'proc' pass should be used first to convert processes to netlists.)
+
+This pass can also be used for mining for frequent subcircuits. In this mode
+the following options are to be used instead of the -map option.
+
+ -mine <out_file>
+ mine for frequent subcircuits and write them to the given ilang file
+
+ -mine_cells_span <min> <max>
+ only mine for subcircuits with the specified number of cells
+ default value: 3 5
+
+ -mine_min_freq <num>
+ only mine for subcircuits with at least the specified number of matches
+ default value: 10
+
+ -mine_limit_matches_per_module <num>
+ when calculating the number of matches for a subcircuit, don't count
+ more than the specified number of matches per module
+
+ -mine_max_fanout <num>
+ don't consider internal signals with more than <num> connections
+
+The modules in the map file may have the attribute 'extract_order' set to an
+integer value. Then this value is used to determine the order in which the pass
+tries to map the modules to the design (ascending, default value is 0).
+
+See 'help techmap' for a pass that does the opposite thing.
+\end{lstlisting}
+
+\section{flatten -- flatten design}
+\label{cmd:flatten}
+\begin{lstlisting}[numbers=left,frame=single]
+ flatten [selection]
+
+This pass flattens the design by replacing cells by their implementation. This
+pass is very similar to the 'techmap' pass. The only difference is that this
+pass is using the current design as mapping library.
+
+Cells and/or modules with the 'keep_hierarchy' attribute set will not be
+flattened by this command.
+\end{lstlisting}
+
+\section{freduce -- perform functional reduction}
+\label{cmd:freduce}
+\begin{lstlisting}[numbers=left,frame=single]
+ freduce [options] [selection]
+
+This pass performs functional reduction in the circuit. I.e. if two nodes are
+equivalent, they are merged to one node and one of the redundant drivers is
+disconnected. A subsequent call to 'clean' will remove the redundant drivers.
+
+ -v, -vv
+ enable verbose or very verbose output
+
+ -inv
+ enable explicit handling of inverted signals
+
+ -stop <n>
+ stop after <n> reduction operations. this is mostly used for
+ debugging the freduce command itself.
+
+ -dump <prefix>
+ dump the design to <prefix>_<module>_<num>.il after each reduction
+ operation. this is mostly used for debugging the freduce command.
+
+This pass is undef-aware, i.e. it considers don't-care values for detecting
+equivalent nodes.
+
+All selected wires are considered for rewiring. The selected cells cover the
+circuit that is analyzed.
+\end{lstlisting}
+
+\section{fsm -- extract and optimize finite state machines}
+\label{cmd:fsm}
+\begin{lstlisting}[numbers=left,frame=single]
+ fsm [options] [selection]
+
+This pass calls all the other fsm_* passes in a useful order. This performs
+FSM extraction and optimization. It also calls opt_clean as needed:
+
+ fsm_detect unless got option -nodetect
+ fsm_extract
+
+ fsm_opt
+ opt_clean
+ fsm_opt
+
+ fsm_expand if got option -expand
+ opt_clean if got option -expand
+ fsm_opt if got option -expand
+
+ fsm_recode unless got option -norecode
+
+ fsm_info
+
+ fsm_export if got option -export
+ fsm_map unless got option -nomap
+
+Options:
+
+ -expand, -norecode, -export, -nomap
+ enable or disable passes as indicated above
+
+ -fullexpand
+ call expand with -full option
+
+ -encoding type
+ -fm_set_fsm_file file
+ -encfile file
+ passed through to fsm_recode pass
+\end{lstlisting}
+
+\section{fsm\_detect -- finding FSMs in design}
+\label{cmd:fsm_detect}
+\begin{lstlisting}[numbers=left,frame=single]
+ fsm_detect [selection]
+
+This pass detects finite state machines by identifying the state signal.
+The state signal is then marked by setting the attribute 'fsm_encoding'
+on the state signal to "auto".
+
+Existing 'fsm_encoding' attributes are not changed by this pass.
+
+Signals can be protected from being detected by this pass by setting the
+'fsm_encoding' attribute to "none".
+\end{lstlisting}
+
+\section{fsm\_expand -- expand FSM cells by merging logic into it}
+\label{cmd:fsm_expand}
+\begin{lstlisting}[numbers=left,frame=single]
+ fsm_expand [-full] [selection]
+
+The fsm_extract pass is conservative about the cells that belong to a finite
+state machine. This pass can be used to merge additional auxiliary gates into
+the finite state machine.
+
+By default, fsm_expand is still a bit conservative regarding merging larger
+word-wide cells. Call with -full to consider all cells for merging.
+\end{lstlisting}
+
+\section{fsm\_export -- exporting FSMs to KISS2 files}
+\label{cmd:fsm_export}
+\begin{lstlisting}[numbers=left,frame=single]
+ fsm_export [-noauto] [-o filename] [-origenc] [selection]
+
+This pass creates a KISS2 file for every selected FSM. For FSMs with the
+'fsm_export' attribute set, the attribute value is used as filename, otherwise
+the module and cell name is used as filename. If the parameter '-o' is given,
+the first exported FSM is written to the specified filename. This overwrites
+the setting as specified with the 'fsm_export' attribute. All other FSMs are
+exported to the default name as mentioned above.
+
+ -noauto
+ only export FSMs that have the 'fsm_export' attribute set
+
+ -o filename
+ filename of the first exported FSM
+
+ -origenc
+ use binary state encoding as state names instead of s0, s1, ...
+\end{lstlisting}
+
+\section{fsm\_extract -- extracting FSMs in design}
+\label{cmd:fsm_extract}
+\begin{lstlisting}[numbers=left,frame=single]
+ fsm_extract [selection]
+
+This pass operates on all signals marked as FSM state signals using the
+'fsm_encoding' attribute. It consumes the logic that creates the state signal
+and uses the state signal to generate control signal and replaces it with an
+FSM cell.
+
+The generated FSM cell still generates the original state signal with its
+original encoding. The 'fsm_opt' pass can be used in combination with the
+'opt_clean' pass to eliminate this signal.
+\end{lstlisting}
+
+\section{fsm\_info -- print information on finite state machines}
+\label{cmd:fsm_info}
+\begin{lstlisting}[numbers=left,frame=single]
+ fsm_info [selection]
+
+This pass dumps all internal information on FSM cells. It can be useful for
+analyzing the synthesis process and is called automatically by the 'fsm'
+pass so that this information is included in the synthesis log file.
+\end{lstlisting}
+
+\section{fsm\_map -- mapping FSMs to basic logic}
+\label{cmd:fsm_map}
+\begin{lstlisting}[numbers=left,frame=single]
+ fsm_map [selection]
+
+This pass translates FSM cells to flip-flops and logic.
+\end{lstlisting}
+
+\section{fsm\_opt -- optimize finite state machines}
+\label{cmd:fsm_opt}
+\begin{lstlisting}[numbers=left,frame=single]
+ fsm_opt [selection]
+
+This pass optimizes FSM cells. It detects which output signals are actually
+not used and removes them from the FSM. This pass is usually used in
+combination with the 'opt_clean' pass (see also 'help fsm').
+\end{lstlisting}
+
+\section{fsm\_recode -- recoding finite state machines}
+\label{cmd:fsm_recode}
+\begin{lstlisting}[numbers=left,frame=single]
+ fsm_recode [options] [selection]
+
+This pass reassign the state encodings for FSM cells. At the moment only
+one-hot encoding and binary encoding is supported.
+ -encoding <type>
+ specify the encoding scheme used for FSMs without the
+ 'fsm_encoding' attribute or with the attribute set to `auto'.
+
+ -fm_set_fsm_file <file>
+ generate a file containing the mapping from old to new FSM encoding
+ in form of Synopsys Formality set_fsm_* commands.
+
+ -encfile <file>
+ write the mappings from old to new FSM encoding to a file in the
+ following format:
+
+ .fsm <module_name> <state_signal>
+ .map <old_bitpattern> <new_bitpattern>
+\end{lstlisting}
+
+\section{greenpak4\_counters -- Extract GreenPak4 counter cells}
+\label{cmd:greenpak4_counters}
+\begin{lstlisting}[numbers=left,frame=single]
+ greenpak4_counters [options] [selection]
+
+This pass converts non-resettable or async resettable down counters to GreenPak4
+counter cells (All other GreenPak4 counter modes must be instantiated manually.)
+\end{lstlisting}
+
+\section{greenpak4\_dffinv -- merge greenpak4 inverters and DFFs}
+\label{cmd:greenpak4_dffinv}
+\begin{lstlisting}[numbers=left,frame=single]
+ greenpak4_dffinv [options] [selection]
+
+Merge GP_INV cells with GP_DFF* cells.
+\end{lstlisting}
+
+\section{help -- display help messages}
+\label{cmd:help}
+\begin{lstlisting}[numbers=left,frame=single]
+ help ................ list all commands
+ help <command> ...... print help message for given command
+ help -all ........... print complete command reference
+
+ help -cells .......... list all cell types
+ help <celltype> ..... print help message for given cell type
+ help <celltype>+ .... print verilog code for given cell type
+\end{lstlisting}
+
+\section{hierarchy -- check, expand and clean up design hierarchy}
+\label{cmd:hierarchy}
+\begin{lstlisting}[numbers=left,frame=single]
+ hierarchy [-check] [-top <module>]
+ hierarchy -generate <cell-types> <port-decls>
+
+In parametric designs, a module might exists in several variations with
+different parameter values. This pass looks at all modules in the current
+design an re-runs the language frontends for the parametric modules as
+needed.
+
+ -check
+ also check the design hierarchy. this generates an error when
+ an unknown module is used as cell type.
+
+ -purge_lib
+ by default the hierarchy command will not remove library (blackbox)
+ modules. use this option to also remove unused blackbox modules.
+
+ -libdir <directory>
+ search for files named <module_name>.v in the specified directory
+ for unknown modules and automatically run read_verilog for each
+ unknown module.
+
+ -keep_positionals
+ per default this pass also converts positional arguments in cells
+ to arguments using port names. this option disables this behavior.
+
+ -nokeep_asserts
+ per default this pass sets the "keep" attribute on all modules
+ that directly or indirectly contain one or more $assert cells. this
+ option disables this behavior.
+
+ -top <module>
+ use the specified top module to built a design hierarchy. modules
+ outside this tree (unused modules) are removed.
+
+ when the -top option is used, the 'top' attribute will be set on the
+ specified top module. otherwise a module with the 'top' attribute set
+ will implicitly be used as top module, if such a module exists.
+
+ -auto-top
+ automatically determine the top of the design hierarchy and mark it.
+
+In -generate mode this pass generates blackbox modules for the given cell
+types (wildcards supported). For this the design is searched for cells that
+match the given types and then the given port declarations are used to
+determine the direction of the ports. The syntax for a port declaration is:
+
+ {i|o|io}[@<num>]:<portname>
+
+Input ports are specified with the 'i' prefix, output ports with the 'o'
+prefix and inout ports with the 'io' prefix. The optional <num> specifies
+the position of the port in the parameter list (needed when instantiated
+using positional arguments). When <num> is not specified, the <portname> can
+also contain wildcard characters.
+
+This pass ignores the current selection and always operates on all modules
+in the current design.
+\end{lstlisting}
+
+\section{hilomap -- technology mapping of constant hi- and/or lo-drivers}
+\label{cmd:hilomap}
+\begin{lstlisting}[numbers=left,frame=single]
+ hilomap [options] [selection]
+
+Map constants to 'tielo' and 'tiehi' driver cells.
+
+ -hicell <celltype> <portname>
+ Replace constant hi bits with this cell.
+
+ -locell <celltype> <portname>
+ Replace constant lo bits with this cell.
+
+ -singleton
+ Create only one hi/lo cell and connect all constant bits
+ to that cell. Per default a separate cell is created for
+ each constant bit.
+\end{lstlisting}
+
+\section{history -- show last interactive commands}
+\label{cmd:history}
+\begin{lstlisting}[numbers=left,frame=single]
+ history
+
+This command prints all commands in the shell history buffer. This are
+all commands executed in an interactive session, but not the commands
+from executed scripts.
+\end{lstlisting}
+
+\section{ice40\_ffinit -- iCE40: handle FF init values}
+\label{cmd:ice40_ffinit}
+\begin{lstlisting}[numbers=left,frame=single]
+ ice40_ffinit [options] [selection]
+
+Remove zero init values for FF output signals. Add inverters to implement
+nonzero init values.
+\end{lstlisting}
+
+\section{ice40\_ffssr -- iCE40: merge synchronous set/reset into FF cells}
+\label{cmd:ice40_ffssr}
+\begin{lstlisting}[numbers=left,frame=single]
+ ice40_ffssr [options] [selection]
+
+Merge synchronous set/reset $_MUX_ cells into iCE40 FFs.
+\end{lstlisting}
+
+\section{ice40\_opt -- iCE40: perform simple optimizations}
+\label{cmd:ice40_opt}
+\begin{lstlisting}[numbers=left,frame=single]
+ ice40_opt [options] [selection]
+
+This command executes the following script:
+
+ do
+ <ice40 specific optimizations>
+ opt_expr -mux_undef -undriven [-full]
+ opt_merge
+ opt_rmdff
+ opt_clean
+ while <changed design>
+
+When called with the option -unlut, this command will transform all already
+mapped SB_LUT4 cells back to logic.
+\end{lstlisting}
+
+\section{insbuf -- insert buffer cells for connected wires}
+\label{cmd:insbuf}
+\begin{lstlisting}[numbers=left,frame=single]
+ insbuf [options] [selection]
+
+Insert buffer cells into the design for directly connected wires.
+
+ -buf <celltype> <in-portname> <out-portname>
+ Use the given cell type instead of $_BUF_. (Notice that the next
+ call to "clean" will remove all $_BUF_ in the design.)
+\end{lstlisting}
+
+\section{iopadmap -- technology mapping of i/o pads (or buffers)}
+\label{cmd:iopadmap}
+\begin{lstlisting}[numbers=left,frame=single]
+ iopadmap [options] [selection]
+
+Map module inputs/outputs to PAD cells from a library. This pass
+can only map to very simple PAD cells. Use 'techmap' to further map
+the resulting cells to more sophisticated PAD cells.
+
+ -inpad <celltype> <portname>[:<portname>]
+ Map module input ports to the given cell type with the
+ given output port name. if a 2nd portname is given, the
+ signal is passed through the pad call, using the 2nd
+ portname as the port facing the module port.
+
+ -outpad <celltype> <portname>[:<portname>]
+ -inoutpad <celltype> <portname>[:<portname>]
+ Similar to -inpad, but for output and inout ports.
+
+ -toutpad <celltype> <portname>:<portname>[:<portname>]
+ Merges $_TBUF_ cells into the output pad cell. This takes precedence
+ over the other -outpad cell. The first portname is the enable input
+ of the tristate driver.
+
+ -tinoutpad <celltype> <portname>:<portname>:<portname>[:<portname>]
+ Merges $_TBUF_ cells into the inout pad cell. This takes precedence
+ over the other -inoutpad cell. The first portname is the enable input
+ of the tristate driver and the 2nd portname is the internal output
+ buffering the external signal.
+
+ -widthparam <param_name>
+ Use the specified parameter name to set the port width.
+
+ -nameparam <param_name>
+ Use the specified parameter to set the port name.
+
+ -bits
+ create individual bit-wide buffers even for ports that
+ are wider. (the default behavior is to create word-wide
+ buffers using -widthparam to set the word size on the cell.)
+
+Tristate PADS (-toutpad, -tinoutpad) always operate in -bits mode.
+\end{lstlisting}
+
+\section{json -- write design in JSON format}
+\label{cmd:json}
+\begin{lstlisting}[numbers=left,frame=single]
+ json [options] [selection]
+
+Write a JSON netlist of all selected objects.
+
+ -o <filename>
+ write to the specified file.
+
+ -aig
+ also include AIG models for the different gate types
+
+See 'help write_json' for a description of the JSON format used.
+\end{lstlisting}
+
+\section{log -- print text and log files}
+\label{cmd:log}
+\begin{lstlisting}[numbers=left,frame=single]
+ log string
+
+Print the given string to the screen and/or the log file. This is useful for TCL
+scripts, because the TCL command "puts" only goes to stdout but not to
+logfiles.
+
+ -stdout
+ Print the output to stdout too. This is useful when all Yosys is executed
+ with a script and the -q (quiet operation) argument to notify the user.
+
+ -stderr
+ Print the output to stderr too.
+
+ -nolog
+ Don't use the internal log() command. Use either -stdout or -stderr,
+ otherwise no output will be generated at all.
+
+ -n
+ do not append a newline
+\end{lstlisting}
+
+\section{ls -- list modules or objects in modules}
+\label{cmd:ls}
+\begin{lstlisting}[numbers=left,frame=single]
+ ls [selection]
+
+When no active module is selected, this prints a list of modules.
+
+When an active module is selected, this prints a list of objects in the module.
+\end{lstlisting}
+
+\section{lut2mux -- convert \$lut to \$\_MUX\_}
+\label{cmd:lut2mux}
+\begin{lstlisting}[numbers=left,frame=single]
+ lut2mux [options] [selection]
+
+This pass converts $lut cells to $_MUX_ gates.
+\end{lstlisting}
+
+\section{maccmap -- mapping macc cells}
+\label{cmd:maccmap}
+\begin{lstlisting}[numbers=left,frame=single]
+ maccmap [-unmap] [selection]
+
+This pass maps $macc cells to yosys $fa and $alu cells. When the -unmap option
+is used then the $macc cell is mapped to $add, $sub, etc. cells instead.
+\end{lstlisting}
+
+\section{memory -- translate memories to basic cells}
+\label{cmd:memory}
+\begin{lstlisting}[numbers=left,frame=single]
+ memory [-nomap] [-nordff] [-memx] [-bram <bram_rules>] [selection]
+
+This pass calls all the other memory_* passes in a useful order:
+
+ memory_dff [-nordff] (-memx implies -nordff)
+ opt_clean
+ memory_share
+ opt_clean
+ memory_memx (when called with -memx)
+ memory_collect
+ memory_bram -rules <bram_rules> (when called with -bram)
+ memory_map (skipped if called with -nomap)
+
+This converts memories to word-wide DFFs and address decoders
+or multiport memory blocks if called with the -nomap option.
+\end{lstlisting}
+
+\section{memory\_bram -- map memories to block rams}
+\label{cmd:memory_bram}
+\begin{lstlisting}[numbers=left,frame=single]
+ memory_bram -rules <rule_file> [selection]
+
+This pass converts the multi-port $mem memory cells into block ram instances.
+The given rules file describes the available resources and how they should be
+used.
+
+The rules file contains a set of block ram description and a sequence of match
+rules. A block ram description looks like this:
+
+ bram RAMB1024X32 # name of BRAM cell
+ init 1 # set to '1' if BRAM can be initialized
+ abits 10 # number of address bits
+ dbits 32 # number of data bits
+ groups 2 # number of port groups
+ ports 1 1 # number of ports in each group
+ wrmode 1 0 # set to '1' if this groups is write ports
+ enable 4 1 # number of enable bits
+ transp 0 2 # transparent (for read ports)
+ clocks 1 2 # clock configuration
+ clkpol 2 2 # clock polarity configuration
+ endbram
+
+For the option 'transp' the value 0 means non-transparent, 1 means transparent
+and a value greater than 1 means configurable. All groups with the same
+value greater than 1 share the same configuration bit.
+
+For the option 'clocks' the value 0 means non-clocked, and a value greater
+than 0 means clocked. All groups with the same value share the same clock
+signal.
+
+For the option 'clkpol' the value 0 means negative edge, 1 means positive edge
+and a value greater than 1 means configurable. All groups with the same value
+greater than 1 share the same configuration bit.
+
+Using the same bram name in different bram blocks will create different variants
+of the bram. Verilog configuration parameters for the bram are created as needed.
+
+It is also possible to create variants by repeating statements in the bram block
+and appending '@<label>' to the individual statements.
+
+A match rule looks like this:
+
+ match RAMB1024X32
+ max waste 16384 # only use this bram if <= 16k ram bits are unused
+ min efficiency 80 # only use this bram if efficiency is at least 80%
+ endmatch
+
+It is possible to match against the following values with min/max rules:
+
+ words ........ number of words in memory in design
+ abits ........ number of address bits on memory in design
+ dbits ........ number of data bits on memory in design
+ wports ....... number of write ports on memory in design
+ rports ....... number of read ports on memory in design
+ ports ........ number of ports on memory in design
+ bits ......... number of bits in memory in design
+ dups .......... number of duplications for more read ports
+
+ awaste ....... number of unused address slots for this match
+ dwaste ....... number of unused data bits for this match
+ bwaste ....... number of unused bram bits for this match
+ waste ........ total number of unused bram bits (bwaste*dups)
+ efficiency ... total percentage of used and non-duplicated bits
+
+ acells ....... number of cells in 'address-direction'
+ dcells ....... number of cells in 'data-direction'
+ cells ........ total number of cells (acells*dcells*dups)
+
+The interface for the created bram instances is derived from the bram
+description. Use 'techmap' to convert the created bram instances into
+instances of the actual bram cells of your target architecture.
+
+A match containing the command 'or_next_if_better' is only used if it
+has a higher efficiency than the next match (and the one after that if
+the next also has 'or_next_if_better' set, and so forth).
+
+A match containing the command 'make_transp' will add external circuitry
+to simulate 'transparent read', if necessary.
+
+A match containing the command 'make_outreg' will add external flip-flops
+to implement synchronous read ports, if necessary.
+
+A match containing the command 'shuffle_enable A' will re-organize
+the data bits to accommodate the enable pattern of port A.
+\end{lstlisting}
+
+\section{memory\_collect -- creating multi-port memory cells}
+\label{cmd:memory_collect}
+\begin{lstlisting}[numbers=left,frame=single]
+ memory_collect [selection]
+
+This pass collects memories and memory ports and creates generic multiport
+memory cells.
+\end{lstlisting}
+
+\section{memory\_dff -- merge input/output DFFs into memories}
+\label{cmd:memory_dff}
+\begin{lstlisting}[numbers=left,frame=single]
+ memory_dff [options] [selection]
+
+This pass detects DFFs at memory ports and merges them into the memory port.
+I.e. it consumes an asynchronous memory port and the flip-flops at its
+interface and yields a synchronous memory port.
+
+ -nordfff
+ do not merge registers on read ports
+\end{lstlisting}
+
+\section{memory\_map -- translate multiport memories to basic cells}
+\label{cmd:memory_map}
+\begin{lstlisting}[numbers=left,frame=single]
+ memory_map [selection]
+
+This pass converts multiport memory cells as generated by the memory_collect
+pass to word-wide DFFs and address decoders.
+\end{lstlisting}
+
+\section{memory\_memx -- emulate vlog sim behavior for mem ports}
+\label{cmd:memory_memx}
+\begin{lstlisting}[numbers=left,frame=single]
+ memory_memx [selection]
+
+This pass adds additional circuitry that emulates the Verilog simulation
+behavior for out-of-bounds memory reads and writes.
+\end{lstlisting}
+
+\section{memory\_share -- consolidate memory ports}
+\label{cmd:memory_share}
+\begin{lstlisting}[numbers=left,frame=single]
+ memory_share [selection]
+
+This pass merges share-able memory ports into single memory ports.
+
+The following methods are used to consolidate the number of memory ports:
+
+ - When write ports are connected to async read ports accessing the same
+ address, then this feedback path is converted to a write port with
+ byte/part enable signals.
+
+ - When multiple write ports access the same address then this is converted
+ to a single write port with a more complex data and/or enable logic path.
+
+ - When multiple write ports are never accessed at the same time (a SAT
+ solver is used to determine this), then the ports are merged into a single
+ write port.
+
+Note that in addition to the algorithms implemented in this pass, the $memrd
+and $memwr cells are also subject to generic resource sharing passes (and other
+optimizations) such as "share" and "opt_merge".
+\end{lstlisting}
+
+\section{memory\_unpack -- unpack multi-port memory cells}
+\label{cmd:memory_unpack}
+\begin{lstlisting}[numbers=left,frame=single]
+ memory_unpack [selection]
+
+This pass converts the multi-port $mem memory cells into individual $memrd and
+$memwr cells. It is the counterpart to the memory_collect pass.
+\end{lstlisting}
+
+\section{miter -- automatically create a miter circuit}
+\label{cmd:miter}
+\begin{lstlisting}[numbers=left,frame=single]
+ miter -equiv [options] gold_name gate_name miter_name
+
+Creates a miter circuit for equivalence checking. The gold- and gate- modules
+must have the same interfaces. The miter circuit will have all inputs of the
+two source modules, prefixed with 'in_'. The miter circuit has a 'trigger'
+output that goes high if an output mismatch between the two source modules is
+detected.
+
+ -ignore_gold_x
+ a undef (x) bit in the gold module output will match any value in
+ the gate module output.
+
+ -make_outputs
+ also route the gold- and gate-outputs to 'gold_*' and 'gate_*' outputs
+ on the miter circuit.
+
+ -make_outcmp
+ also create a cmp_* output for each gold/gate output pair.
+
+ -make_assert
+ also create an 'assert' cell that checks if trigger is always low.
+
+ -flatten
+ call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.
+
+
+ miter -assert [options] module [miter_name]
+
+Creates a miter circuit for property checking. All input ports are kept,
+output ports are discarded. An additional output 'trigger' is created that
+goes high when an assert is violated. Without a miter_name, the existing
+module is modified.
+
+ -make_outputs
+ keep module output ports.
+
+ -flatten
+ call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.
+\end{lstlisting}
+
+\section{muxcover -- cover trees of MUX cells with wider MUXes}
+\label{cmd:muxcover}
+\begin{lstlisting}[numbers=left,frame=single]
+ muxcover [options] [selection]
+
+Cover trees of $_MUX_ cells with $_MUX{4,8,16}_ cells
+
+ -mux4, -mux8, -mux16
+ Use the specified types of MUXes. If none of those options are used,
+ the effect is the same as if all of them where used.
+
+ -nodecode
+ Do not insert decoder logic. This reduces the number of possible
+ substitutions, but guarantees that the resulting circuit is not
+ less efficient than the original circuit.
+\end{lstlisting}
+
+\section{nlutmap -- map to LUTs of different sizes}
+\label{cmd:nlutmap}
+\begin{lstlisting}[numbers=left,frame=single]
+ nlutmap [options] [selection]
+
+This pass uses successive calls to 'abc' to map to an architecture. That
+provides a small number of differently sized LUTs.
+
+ -luts N_1,N_2,N_3,...
+ The number of LUTs with 1, 2, 3, ... inputs that are
+ available in the target architecture.
+
+ -assert
+ Create an error if not all logic can be mapped
+
+Excess logic that does not fit into the specified LUTs is mapped back
+to generic logic gates ($_AND_, etc.).
+\end{lstlisting}
+
+\section{opt -- perform simple optimizations}
+\label{cmd:opt}
+\begin{lstlisting}[numbers=left,frame=single]
+ opt [options] [selection]
+
+This pass calls all the other opt_* passes in a useful order. This performs
+a series of trivial optimizations and cleanups. This pass executes the other
+passes in the following order:
+
+ opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]
+ opt_merge [-share_all] -nomux
+
+ do
+ opt_muxtree
+ opt_reduce [-fine] [-full]
+ opt_merge [-share_all]
+ opt_rmdff [-keepdc]
+ opt_clean [-purge]
+ opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]
+ while <changed design>
+
+When called with -fast the following script is used instead:
+
+ do
+ opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]
+ opt_merge [-share_all]
+ opt_rmdff [-keepdc]
+ opt_clean [-purge]
+ while <changed design in opt_rmdff>
+
+Note: Options in square brackets (such as [-keepdc]) are passed through to
+the opt_* commands when given to 'opt'.
+\end{lstlisting}
+
+\section{opt\_clean -- remove unused cells and wires}
+\label{cmd:opt_clean}
+\begin{lstlisting}[numbers=left,frame=single]
+ opt_clean [options] [selection]
+
+This pass identifies wires and cells that are unused and removes them. Other
+passes often remove cells but leave the wires in the design or reconnect the
+wires but leave the old cells in the design. This pass can be used to clean up
+after the passes that do the actual work.
+
+This pass only operates on completely selected modules without processes.
+
+ -purge
+ also remove internal nets if they have a public name
+\end{lstlisting}
+
+\section{opt\_expr -- perform const folding and simple expression rewriting}
+\label{cmd:opt_expr}
+\begin{lstlisting}[numbers=left,frame=single]
+ opt_expr [options] [selection]
+
+This pass performs const folding on internal cell types with constant inputs.
+It also performs some simple expression rewritring.
+
+ -mux_undef
+ remove 'undef' inputs from $mux, $pmux and $_MUX_ cells
+
+ -mux_bool
+ replace $mux cells with inverters or buffers when possible
+
+ -undriven
+ replace undriven nets with undef (x) constants
+
+ -clkinv
+ optimize clock inverters by changing FF types
+
+ -fine
+ perform fine-grain optimizations
+
+ -full
+ alias for -mux_undef -mux_bool -undriven -fine
+
+ -keepdc
+ some optimizations change the behavior of the circuit with respect to
+ don't-care bits. for example in 'a+0' a single x-bit in 'a' will cause
+ all result bits to be set to x. this behavior changes when 'a+0' is
+ replaced by 'a'. the -keepdc option disables all such optimizations.
+\end{lstlisting}
+
+\section{opt\_merge -- consolidate identical cells}
+\label{cmd:opt_merge}
+\begin{lstlisting}[numbers=left,frame=single]
+ opt_merge [options] [selection]
+
+This pass identifies cells with identical type and input signals. Such cells
+are then merged to one cell.
+
+ -nomux
+ Do not merge MUX cells.
+
+ -share_all
+ Operate on all cell types, not just built-in types.
+\end{lstlisting}
+
+\section{opt\_muxtree -- eliminate dead trees in multiplexer trees}
+\label{cmd:opt_muxtree}
+\begin{lstlisting}[numbers=left,frame=single]
+ opt_muxtree [selection]
+
+This pass analyzes the control signals for the multiplexer trees in the design
+and identifies inputs that can never be active. It then removes this dead
+branches from the multiplexer trees.
+
+This pass only operates on completely selected modules without processes.
+\end{lstlisting}
+
+\section{opt\_reduce -- simplify large MUXes and AND/OR gates}
+\label{cmd:opt_reduce}
+\begin{lstlisting}[numbers=left,frame=single]
+ opt_reduce [options] [selection]
+
+This pass performs two interlinked optimizations:
+
+1. it consolidates trees of large AND gates or OR gates and eliminates
+duplicated inputs.
+
+2. it identifies duplicated inputs to MUXes and replaces them with a single
+input with the original control signals OR'ed together.
+
+ -fine
+ perform fine-grain optimizations
+
+ -full
+ alias for -fine
+\end{lstlisting}
+
+\section{opt\_rmdff -- remove DFFs with constant inputs}
+\label{cmd:opt_rmdff}
+\begin{lstlisting}[numbers=left,frame=single]
+ opt_rmdff [-keepdc] [selection]
+
+This pass identifies flip-flops with constant inputs and replaces them with
+a constant driver.
+\end{lstlisting}
+
+\section{plugin -- load and list loaded plugins}
+\label{cmd:plugin}
+\begin{lstlisting}[numbers=left,frame=single]
+ plugin [options]
+
+Load and list loaded plugins.
+
+ -i <plugin_filename>
+ Load (install) the specified plugin.
+
+ -a <alias_name>
+ Register the specified alias name for the loaded plugin
+
+ -l
+ List loaded plugins
+\end{lstlisting}
+
+\section{pmuxtree -- transform \$pmux cells to trees of \$mux cells}
+\label{cmd:pmuxtree}
+\begin{lstlisting}[numbers=left,frame=single]
+ pmuxtree [options] [selection]
+
+This pass transforms $pmux cells to a trees of $mux cells.
+\end{lstlisting}
+
+\section{prep -- generic synthesis script}
+\label{cmd:prep}
+\begin{lstlisting}[numbers=left,frame=single]
+ prep [options]
+
+This command runs a conservative RTL synthesis. A typical application for this
+is the preparation stage of a verification flow. This command does not operate
+on partly selected designs.
+
+ -top <module>
+ use the specified module as top module (default='top')
+
+ -auto-top
+ automatically determine the top of the design hierarchy
+
+ -flatten
+ flatten the design before synthesis. this will pass '-auto-top' to
+ 'hierarchy' if no top module is specified.
+
+ -ifx
+ passed to 'proc'. uses verilog simulation behavior for verilog if/case
+ undef handling. this also prevents 'wreduce' from being run.
+
+ -memx
+ simulate verilog simulation behavior for out-of-bounds memory accesses
+ using the 'memory_memx' pass. This option implies -nordff.
+
+ -nomem
+ do not run any of the memory_* passes
+
+ -nordff
+ passed to 'memory_dff'. prohibits merging of FFs into memory read ports
+
+ -nokeepdc
+ do not call opt_* with -keepdc
+
+ -run <from_label>[:<to_label>]
+ only run the commands between the labels (see below). an empty
+ from label is synonymous to 'begin', and empty to label is
+ synonymous to the end of the command list.
+
+
+The following commands are executed by this synthesis command:
+
+ begin:
+ hierarchy -check [-top <top> | -auto-top]
+
+ coarse:
+ proc [-ifx]
+ flatten (if -flatten)
+ opt_expr -keepdc
+ opt_clean
+ check
+ opt -keepdc
+ wreduce [-memx]
+ memory_dff [-nordff]
+ memory_memx (if -memx)
+ opt_clean
+ memory_collect
+ opt -keepdc -fast
+
+ check:
+ stat
+ check
+\end{lstlisting}
+
+\section{proc -- translate processes to netlists}
+\label{cmd:proc}
+\begin{lstlisting}[numbers=left,frame=single]
+ proc [options] [selection]
+
+This pass calls all the other proc_* passes in the most common order.
+
+ proc_clean
+ proc_rmdead
+ proc_init
+ proc_arst
+ proc_mux
+ proc_dlatch
+ proc_dff
+ proc_clean
+
+This replaces the processes in the design with multiplexers,
+flip-flops and latches.
+
+The following options are supported:
+
+ -global_arst [!]<netname>
+ This option is passed through to proc_arst.
+
+ -ifx
+ This option is passed through to proc_mux. proc_rmdead is not
+ executed in -ifx mode.
+\end{lstlisting}
+
+\section{proc\_arst -- detect asynchronous resets}
+\label{cmd:proc_arst}
+\begin{lstlisting}[numbers=left,frame=single]
+ proc_arst [-global_arst [!]<netname>] [selection]
+
+This pass identifies asynchronous resets in the processes and converts them
+to a different internal representation that is suitable for generating
+flip-flop cells with asynchronous resets.
+
+ -global_arst [!]<netname>
+ In modules that have a net with the given name, use this net as async
+ reset for registers that have been assign initial values in their
+ declaration ('reg foobar = constant_value;'). Use the '!' modifier for
+ active low reset signals. Note: the frontend stores the default value
+ in the 'init' attribute on the net.
+\end{lstlisting}
+
+\section{proc\_clean -- remove empty parts of processes}
+\label{cmd:proc_clean}
+\begin{lstlisting}[numbers=left,frame=single]
+ proc_clean [selection]
+
+This pass removes empty parts of processes and ultimately removes a process
+if it contains only empty structures.
+\end{lstlisting}
+
+\section{proc\_dff -- extract flip-flops from processes}
+\label{cmd:proc_dff}
+\begin{lstlisting}[numbers=left,frame=single]
+ proc_dff [selection]
+
+This pass identifies flip-flops in the processes and converts them to
+d-type flip-flop cells.
+\end{lstlisting}
+
+\section{proc\_dlatch -- extract latches from processes}
+\label{cmd:proc_dlatch}
+\begin{lstlisting}[numbers=left,frame=single]
+ proc_dlatch [selection]
+
+This pass identifies latches in the processes and converts them to
+d-type latches.
+\end{lstlisting}
+
+\section{proc\_init -- convert initial block to init attributes}
+\label{cmd:proc_init}
+\begin{lstlisting}[numbers=left,frame=single]
+ proc_init [selection]
+
+This pass extracts the 'init' actions from processes (generated from Verilog
+'initial' blocks) and sets the initial value to the 'init' attribute on the
+respective wire.
+\end{lstlisting}
+
+\section{proc\_mux -- convert decision trees to multiplexers}
+\label{cmd:proc_mux}
+\begin{lstlisting}[numbers=left,frame=single]
+ proc_mux [options] [selection]
+
+This pass converts the decision trees in processes (originating from if-else
+and case statements) to trees of multiplexer cells.
+
+ -ifx
+ Use Verilog simulation behavior with respect to undef values in
+ 'case' expressions and 'if' conditions.
+\end{lstlisting}
+
+\section{proc\_rmdead -- eliminate dead trees in decision trees}
+\label{cmd:proc_rmdead}
+\begin{lstlisting}[numbers=left,frame=single]
+ proc_rmdead [selection]
+
+This pass identifies unreachable branches in decision trees and removes them.
+\end{lstlisting}
+
+\section{qwp -- quadratic wirelength placer}
+\label{cmd:qwp}
+\begin{lstlisting}[numbers=left,frame=single]
+ qwp [options] [selection]
+
+This command runs quadratic wirelength placement on the selected modules and
+annotates the cells in the design with 'qwp_position' attributes.
+
+ -ltr
+ Add left-to-right constraints: constrain all inputs on the left border
+ outputs to the right border.
+
+ -alpha
+ Add constraints for inputs/outputs to be placed in alphanumerical
+ order along the y-axis (top-to-bottom).
+
+ -grid N
+ Number of grid divisions in x- and y-direction. (default=16)
+
+ -dump <html_file_name>
+ Dump a protocol of the placement algorithm to the html file.
+
+ -v
+ Verbose solver output for profiling or debugging
+
+Note: This implementation of a quadratic wirelength placer uses exact
+dense matrix operations. It is only a toy-placer for small circuits.
+\end{lstlisting}
+
+\section{read\_blif -- read BLIF file}
+\label{cmd:read_blif}
+\begin{lstlisting}[numbers=left,frame=single]
+ read_blif [filename]
+
+Load modules from a BLIF file into the current design.
+
+ -sop
+ Create $sop cells instead of $lut cells
+\end{lstlisting}
+
+\section{read\_ilang -- read modules from ilang file}
+\label{cmd:read_ilang}
+\begin{lstlisting}[numbers=left,frame=single]
+ read_ilang [filename]
+
+Load modules from an ilang file to the current design. (ilang is a text
+representation of a design in yosys's internal format.)
+\end{lstlisting}
+
+\section{read\_liberty -- read cells from liberty file}
+\label{cmd:read_liberty}
+\begin{lstlisting}[numbers=left,frame=single]
+ read_liberty [filename]
+
+Read cells from liberty file as modules into current design.
+
+ -lib
+ only create empty blackbox modules
+
+ -ignore_redef
+ ignore re-definitions of modules. (the default behavior is to
+ create an error message.)
+
+ -ignore_miss_func
+ ignore cells with missing function specification of outputs
+
+ -ignore_miss_dir
+ ignore cells with a missing or invalid direction
+ specification on a pin
+
+ -setattr <attribute_name>
+ set the specified attribute (to the value 1) on all loaded modules
+\end{lstlisting}
+
+\section{read\_verilog -- read modules from Verilog file}
+\label{cmd:read_verilog}
+\begin{lstlisting}[numbers=left,frame=single]
+ read_verilog [options] [filename]
+
+Load modules from a Verilog file to the current design. A large subset of
+Verilog-2005 is supported.
+
+ -sv
+ enable support for SystemVerilog features. (only a small subset
+ of SystemVerilog is supported)
+
+ -formal
+ enable support for SystemVerilog assertions and some Yosys extensions
+ replace the implicit -D SYNTHESIS with -D FORMAL
+
+ -norestrict
+ ignore restrict() assertions
+
+ -assume-asserts
+ treat all assert() statements like assume() statements
+
+ -dump_ast1
+ dump abstract syntax tree (before simplification)
+
+ -dump_ast2
+ dump abstract syntax tree (after simplification)
+
+ -dump_vlog
+ dump ast as Verilog code (after simplification)
+
+ -dump_rtlil
+ dump generated RTLIL netlist
+
+ -yydebug
+ enable parser debug output
+
+ -nolatches
+ usually latches are synthesized into logic loops
+ this option prohibits this and sets the output to 'x'
+ in what would be the latches hold condition
+
+ this behavior can also be achieved by setting the
+ 'nolatches' attribute on the respective module or
+ always block.
+
+ -nomem2reg
+ under certain conditions memories are converted to registers
+ early during simplification to ensure correct handling of
+ complex corner cases. this option disables this behavior.
+
+ this can also be achieved by setting the 'nomem2reg'
+ attribute on the respective module or register.
+
+ This is potentially dangerous. Usually the front-end has good
+ reasons for converting an array to a list of registers.
+ Prohibiting this step will likely result in incorrect synthesis
+ results.
+
+ -mem2reg
+ always convert memories to registers. this can also be
+ achieved by setting the 'mem2reg' attribute on the respective
+ module or register.
+
+ -nomeminit
+ do not infer $meminit cells and instead convert initialized
+ memories to registers directly in the front-end.
+
+ -ppdump
+ dump Verilog code after pre-processor
+
+ -nopp
+ do not run the pre-processor
+
+ -nodpi
+ disable DPI-C support
+
+ -lib
+ only create empty blackbox modules. This implies -DBLACKBOX.
+
+ -noopt
+ don't perform basic optimizations (such as const folding) in the
+ high-level front-end.
+
+ -icells
+ interpret cell types starting with '$' as internal cell types
+
+ -ignore_redef
+ ignore re-definitions of modules. (the default behavior is to
+ create an error message.)
+
+ -defer
+ only read the abstract syntax tree and defer actual compilation
+ to a later 'hierarchy' command. Useful in cases where the default
+ parameters of modules yield invalid or not synthesizable code.
+
+ -noautowire
+ make the default of `default_nettype be "none" instead of "wire".
+
+ -setattr <attribute_name>
+ set the specified attribute (to the value 1) on all loaded modules
+
+ -Dname[=definition]
+ define the preprocessor symbol 'name' and set its optional value
+ 'definition'
+
+ -Idir
+ add 'dir' to the directories which are used when searching include
+ files
+
+The command 'verilog_defaults' can be used to register default options for
+subsequent calls to 'read_verilog'.
+
+Note that the Verilog frontend does a pretty good job of processing valid
+verilog input, but has not very good error reporting. It generally is
+recommended to use a simulator (for example Icarus Verilog) for checking
+the syntax of the code, rather than to rely on read_verilog for that.
+
+See the Yosys README file for a list of non-standard Verilog features
+supported by the Yosys Verilog front-end.
+\end{lstlisting}
+
+\section{rename -- rename object in the design}
+\label{cmd:rename}
+\begin{lstlisting}[numbers=left,frame=single]
+ rename old_name new_name
+
+Rename the specified object. Note that selection patterns are not supported
+by this command.
+
+
+ rename -enumerate [-pattern <pattern>] [selection]
+
+Assign short auto-generated names to all selected wires and cells with private
+names. The -pattern option can be used to set the pattern for the new names.
+The character % in the pattern is replaced with a integer number. The default
+pattern is '_%_'.
+
+ rename -hide [selection]
+
+Assign private names (the ones with $-prefix) to all selected wires and cells
+with public names. This ignores all selected ports.
+
+ rename -top new_name
+
+Rename top module.
+\end{lstlisting}
+
+\section{sat -- solve a SAT problem in the circuit}
+\label{cmd:sat}
+\begin{lstlisting}[numbers=left,frame=single]
+ sat [options] [selection]
+
+This command solves a SAT problem defined over the currently selected circuit
+and additional constraints passed as parameters.
+
+ -all
+ show all solutions to the problem (this can grow exponentially, use
+ -max <N> instead to get <N> solutions)
+
+ -max <N>
+ like -all, but limit number of solutions to <N>
+
+ -enable_undef
+ enable modeling of undef value (aka 'x-bits')
+ this option is implied by -set-def, -set-undef et. cetera
+
+ -max_undef
+ maximize the number of undef bits in solutions, giving a better
+ picture of which input bits are actually vital to the solution.
+
+ -set <signal> <value>
+ set the specified signal to the specified value.
+
+ -set-def <signal>
+ add a constraint that all bits of the given signal must be defined
+
+ -set-any-undef <signal>
+ add a constraint that at least one bit of the given signal is undefined
+
+ -set-all-undef <signal>
+ add a constraint that all bits of the given signal are undefined
+
+ -set-def-inputs
+ add -set-def constraints for all module inputs
+
+ -show <signal>
+ show the model for the specified signal. if no -show option is
+ passed then a set of signals to be shown is automatically selected.
+
+ -show-inputs, -show-outputs, -show-ports
+ add all module (input/output) ports to the list of shown signals
+
+ -show-regs, -show-public, -show-all
+ show all registers, show signals with 'public' names, show all signals
+
+ -ignore_div_by_zero
+ ignore all solutions that involve a division by zero
+
+ -ignore_unknown_cells
+ ignore all cells that can not be matched to a SAT model
+
+The following options can be used to set up a sequential problem:
+
+ -seq <N>
+ set up a sequential problem with <N> time steps. The steps will
+ be numbered from 1 to N.
+
+ note: for large <N> it can be significantly faster to use
+ -tempinduct-baseonly -maxsteps <N> instead of -seq <N>.
+
+ -set-at <N> <signal> <value>
+ -unset-at <N> <signal>
+ set or unset the specified signal to the specified value in the
+ given timestep. this has priority over a -set for the same signal.
+
+ -set-assumes
+ set all assumptions provided via $assume cells
+
+ -set-def-at <N> <signal>
+ -set-any-undef-at <N> <signal>
+ -set-all-undef-at <N> <signal>
+ add undef constraints in the given timestep.
+
+ -set-init <signal> <value>
+ set the initial value for the register driving the signal to the value
+
+ -set-init-undef
+ set all initial states (not set using -set-init) to undef
+
+ -set-init-def
+ do not force a value for the initial state but do not allow undef
+
+ -set-init-zero
+ set all initial states (not set using -set-init) to zero
+
+ -dump_vcd <vcd-file-name>
+ dump SAT model (counter example in proof) to VCD file
+
+ -dump_json <json-file-name>
+ dump SAT model (counter example in proof) to a WaveJSON file.
+
+ -dump_cnf <cnf-file-name>
+ dump CNF of SAT problem (in DIMACS format). in temporal induction
+ proofs this is the CNF of the first induction step.
+
+The following additional options can be used to set up a proof. If also -seq
+is passed, a temporal induction proof is performed.
+
+ -tempinduct
+ Perform a temporal induction proof. In a temporal induction proof it is
+ proven that the condition holds forever after the number of time steps
+ specified using -seq.
+
+ -tempinduct-def
+ Perform a temporal induction proof. Assume an initial state with all
+ registers set to defined values for the induction step.
+
+ -tempinduct-baseonly
+ Run only the basecase half of temporal induction (requires -maxsteps)
+
+ -tempinduct-inductonly
+ Run only the induction half of temporal induction
+
+ -tempinduct-skip <N>
+ Skip the first <N> steps of the induction proof.
+
+ note: this will assume that the base case holds for <N> steps.
+ this must be proven independently with "-tempinduct-baseonly
+ -maxsteps <N>". Use -initsteps if you just want to set a
+ minimal induction length.
+
+ -prove <signal> <value>
+ Attempt to proof that <signal> is always <value>.
+
+ -prove-x <signal> <value>
+ Like -prove, but an undef (x) bit in the lhs matches any value on
+ the right hand side. Useful for equivalence checking.
+
+ -prove-asserts
+ Prove that all asserts in the design hold.
+
+ -prove-skip <N>
+ Do not enforce the prove-condition for the first <N> time steps.
+
+ -maxsteps <N>
+ Set a maximum length for the induction.
+
+ -initsteps <N>
+ Set initial length for the induction.
+ This will speed up the search of the right induction length
+ for deep induction proofs.
+
+ -stepsize <N>
+ Increase the size of the induction proof in steps of <N>.
+ This will speed up the search of the right induction length
+ for deep induction proofs.
+
+ -timeout <N>
+ Maximum number of seconds a single SAT instance may take.
+
+ -verify
+ Return an error and stop the synthesis script if the proof fails.
+
+ -verify-no-timeout
+ Like -verify but do not return an error for timeouts.
+
+ -falsify
+ Return an error and stop the synthesis script if the proof succeeds.
+
+ -falsify-no-timeout
+ Like -falsify but do not return an error for timeouts.
+\end{lstlisting}
+
+\section{scatter -- add additional intermediate nets}
+\label{cmd:scatter}
+\begin{lstlisting}[numbers=left,frame=single]
+ scatter [selection]
+
+This command adds additional intermediate nets on all cell ports. This is used
+for testing the correct use of the SigMap helper in passes. If you don't know
+what this means: don't worry -- you only need this pass when testing your own
+extensions to Yosys.
+
+Use the opt_clean command to get rid of the additional nets.
+\end{lstlisting}
+
+\section{scc -- detect strongly connected components (logic loops)}
+\label{cmd:scc}
+\begin{lstlisting}[numbers=left,frame=single]
+ scc [options] [selection]
+
+This command identifies strongly connected components (aka logic loops) in the
+design.
+
+ -expect <num>
+ expect to find exactly <num> SSCs. A different number of SSCs will
+ produce an error.
+
+ -max_depth <num>
+ limit to loops not longer than the specified number of cells. This
+ can e.g. be useful in identifying small local loops in a module that
+ implements one large SCC.
+
+ -nofeedback
+ do not count cells that have their output fed back into one of their
+ inputs as single-cell scc.
+
+ -all_cell_types
+ Usually this command only considers internal non-memory cells. With
+ this option set, all cells are considered. For unknown cells all ports
+ are assumed to be bidirectional 'inout' ports.
+
+ -set_attr <name> <value>
+ -set_cell_attr <name> <value>
+ -set_wire_attr <name> <value>
+ set the specified attribute on all cells and/or wires that are part of
+ a logic loop. the special token {} in the value is replaced with a
+ unique identifier for the logic loop.
+
+ -select
+ replace the current selection with a selection of all cells and wires
+ that are part of a found logic loop
+\end{lstlisting}
+
+\section{script -- execute commands from script file}
+\label{cmd:script}
+\begin{lstlisting}[numbers=left,frame=single]
+ script <filename> [<from_label>:<to_label>]
+
+This command executes the yosys commands in the specified file.
+
+The 2nd argument can be used to only execute the section of the
+file between the specified labels. An empty from label is synonymous
+for the beginning of the file and an empty to label is synonymous
+for the end of the file.
+
+If only one label is specified (without ':') then only the block
+marked with that label (until the next label) is executed.
+\end{lstlisting}
+
+\section{select -- modify and view the list of selected objects}
+\label{cmd:select}
+\begin{lstlisting}[numbers=left,frame=single]
+ select [ -add | -del | -set <name> ] {-read <filename> | <selection>}
+ select [ <assert_option> ] {-read <filename> | <selection>}
+ select [ -list | -write <filename> | -count | -clear ]
+ select -module <modname>
+
+Most commands use the list of currently selected objects to determine which part
+of the design to operate on. This command can be used to modify and view this
+list of selected objects.
+
+Note that many commands support an optional [selection] argument that can be
+used to override the global selection for the command. The syntax of this
+optional argument is identical to the syntax of the <selection> argument
+described here.
+
+ -add, -del
+ add or remove the given objects to the current selection.
+ without this options the current selection is replaced.
+
+ -set <name>
+ do not modify the current selection. instead save the new selection
+ under the given name (see @<name> below). to save the current selection,
+ use "select -set <name> %"
+
+ -assert-none
+ do not modify the current selection. instead assert that the given
+ selection is empty. i.e. produce an error if any object matching the
+ selection is found.
+
+ -assert-any
+ do not modify the current selection. instead assert that the given
+ selection is non-empty. i.e. produce an error if no object matching
+ the selection is found.
+
+ -assert-count N
+ do not modify the current selection. instead assert that the given
+ selection contains exactly N objects.
+
+ -assert-max N
+ do not modify the current selection. instead assert that the given
+ selection contains less than or exactly N objects.
+
+ -assert-min N
+ do not modify the current selection. instead assert that the given
+ selection contains at least N objects.
+
+ -list
+ list all objects in the current selection
+
+ -write <filename>
+ like -list but write the output to the specified file
+
+ -read <filename>
+ read the specified file (written by -write)
+
+ -count
+ count all objects in the current selection
+
+ -clear
+ clear the current selection. this effectively selects the whole
+ design. it also resets the selected module (see -module). use the
+ command 'select *' to select everything but stay in the current module.
+
+ -none
+ create an empty selection. the current module is unchanged.
+
+ -module <modname>
+ limit the current scope to the specified module.
+ the difference between this and simply selecting the module
+ is that all object names are interpreted relative to this
+ module after this command until the selection is cleared again.
+
+When this command is called without an argument, the current selection
+is displayed in a compact form (i.e. only the module name when a whole module
+is selected).
+
+The <selection> argument itself is a series of commands for a simple stack
+machine. Each element on the stack represents a set of selected objects.
+After this commands have been executed, the union of all remaining sets
+on the stack is computed and used as selection for the command.
+
+Pushing (selecting) object when not in -module mode:
+
+ <mod_pattern>
+ select the specified module(s)
+
+ <mod_pattern>/<obj_pattern>
+ select the specified object(s) from the module(s)
+
+Pushing (selecting) object when in -module mode:
+
+ <obj_pattern>
+ select the specified object(s) from the current module
+
+A <mod_pattern> can be a module name, wildcard expression (*, ?, [..])
+matching module names, or one of the following:
+
+ A:<pattern>, A:<pattern>=<pattern>
+ all modules with an attribute matching the given pattern
+ in addition to = also <, <=, >=, and > are supported
+
+An <obj_pattern> can be an object name, wildcard expression, or one of
+the following:
+
+ w:<pattern>
+ all wires with a name matching the given wildcard pattern
+
+ i:<pattern>, o:<pattern>, x:<pattern>
+ all inputs (i:), outputs (o:) or any ports (x:) with matching names
+
+ s:<size>, s:<min>:<max>
+ all wires with a matching width
+
+ m:<pattern>
+ all memories with a name matching the given pattern
+
+ c:<pattern>
+ all cells with a name matching the given pattern
+
+ t:<pattern>
+ all cells with a type matching the given pattern
+
+ p:<pattern>
+ all processes with a name matching the given pattern
+
+ a:<pattern>
+ all objects with an attribute name matching the given pattern
+
+ a:<pattern>=<pattern>
+ all objects with a matching attribute name-value-pair.
+ in addition to = also <, <=, >=, and > are supported
+
+ r:<pattern>, r:<pattern>=<pattern>
+ cells with matching parameters. also with <, <=, >= and >.
+
+ n:<pattern>
+ all objects with a name matching the given pattern
+ (i.e. 'n:' is optional as it is the default matching rule)
+
+ @<name>
+ push the selection saved prior with 'select -set <name> ...'
+
+The following actions can be performed on the top sets on the stack:
+
+ %
+ push a copy of the current selection to the stack
+
+ %%
+ replace the stack with a union of all elements on it
+
+ %n
+ replace top set with its invert
+
+ %u
+ replace the two top sets on the stack with their union
+
+ %i
+ replace the two top sets on the stack with their intersection
+
+ %d
+ pop the top set from the stack and subtract it from the new top
+
+ %D
+ like %d but swap the roles of two top sets on the stack
+
+ %c
+ create a copy of the top set from the stack and push it
+
+ %x[<num1>|*][.<num2>][:<rule>[:<rule>..]]
+ expand top set <num1> num times according to the specified rules.
+ (i.e. select all cells connected to selected wires and select all
+ wires connected to selected cells) The rules specify which cell
+ ports to use for this. the syntax for a rule is a '-' for exclusion
+ and a '+' for inclusion, followed by an optional comma separated
+ list of cell types followed by an optional comma separated list of
+ cell ports in square brackets. a rule can also be just a cell or wire
+ name that limits the expansion (is included but does not go beyond).
+ select at most <num2> objects. a warning message is printed when this
+ limit is reached. When '*' is used instead of <num1> then the process
+ is repeated until no further object are selected.
+
+ %ci[<num1>|*][.<num2>][:<rule>[:<rule>..]]
+ %co[<num1>|*][.<num2>][:<rule>[:<rule>..]]
+ similar to %x, but only select input (%ci) or output cones (%co)
+
+ %xe[...] %cie[...] %coe
+ like %x, %ci, and %co but only consider combinatorial cells
+
+ %a
+ expand top set by selecting all wires that are (at least in part)
+ aliases for selected wires.
+
+ %s
+ expand top set by adding all modules that implement cells in selected
+ modules
+
+ %m
+ expand top set by selecting all modules that contain selected objects
+
+ %M
+ select modules that implement selected cells
+
+ %C
+ select cells that implement selected modules
+
+ %R[<num>]
+ select <num> random objects from top selection (default 1)
+
+Example: the following command selects all wires that are connected to a
+'GATE' input of a 'SWITCH' cell:
+
+ select */t:SWITCH %x:+[GATE] */t:SWITCH %d
+\end{lstlisting}
+
+\section{setattr -- set/unset attributes on objects}
+\label{cmd:setattr}
+\begin{lstlisting}[numbers=left,frame=single]
+ setattr [ -mod ] [ -set name value | -unset name ]... [selection]
+
+Set/unset the given attributes on the selected objects. String values must be
+passed in double quotes (").
+
+When called with -mod, this command will set and unset attributes on modules
+instead of objects within modules.
+\end{lstlisting}
+
+\section{setparam -- set/unset parameters on objects}
+\label{cmd:setparam}
+\begin{lstlisting}[numbers=left,frame=single]
+ setparam [ -type cell_type ] [ -set name value | -unset name ]... [selection]
+
+Set/unset the given parameters on the selected cells. String values must be
+passed in double quotes (").
+
+The -type option can be used to change the cell type of the selected cells.
+\end{lstlisting}
+
+\section{setundef -- replace undef values with defined constants}
+\label{cmd:setundef}
+\begin{lstlisting}[numbers=left,frame=single]
+ setundef [options] [selection]
+
+This command replaced undef (x) constants with defined (0/1) constants.
+
+ -undriven
+ also set undriven nets to constant values
+
+ -zero
+ replace with bits cleared (0)
+
+ -one
+ replace with bits set (1)
+
+ -random <seed>
+ replace with random bits using the specified integer als seed
+ value for the random number generator.
+
+ -init
+ also create/update init values for flip-flops
+\end{lstlisting}
+
+\section{share -- perform sat-based resource sharing}
+\label{cmd:share}
+\begin{lstlisting}[numbers=left,frame=single]
+ share [options] [selection]
+
+This pass merges shareable resources into a single resource. A SAT solver
+is used to determine if two resources are share-able.
+
+ -force
+ Per default the selection of cells that is considered for sharing is
+ narrowed using a list of cell types. With this option all selected
+ cells are considered for resource sharing.
+
+ IMPORTANT NOTE: If the -all option is used then no cells with internal
+ state must be selected!
+
+ -aggressive
+ Per default some heuristics are used to reduce the number of cells
+ considered for resource sharing to only large resources. This options
+ turns this heuristics off, resulting in much more cells being considered
+ for resource sharing.
+
+ -fast
+ Only consider the simple part of the control logic in SAT solving, resulting
+ in much easier SAT problems at the cost of maybe missing some opportunities
+ for resource sharing.
+
+ -limit N
+ Only perform the first N merges, then stop. This is useful for debugging.
+\end{lstlisting}
+
+\section{shell -- enter interactive command mode}
+\label{cmd:shell}
+\begin{lstlisting}[numbers=left,frame=single]
+ shell
+
+This command enters the interactive command mode. This can be useful
+in a script to interrupt the script at a certain point and allow for
+interactive inspection or manual synthesis of the design at this point.
+
+The command prompt of the interactive shell indicates the current
+selection (see 'help select'):
+
+ yosys>
+ the entire design is selected
+
+ yosys*>
+ only part of the design is selected
+
+ yosys [modname]>
+ the entire module 'modname' is selected using 'select -module modname'
+
+ yosys [modname]*>
+ only part of current module 'modname' is selected
+
+When in interactive shell, some errors (e.g. invalid command arguments)
+do not terminate yosys but return to the command prompt.
+
+This command is the default action if nothing else has been specified
+on the command line.
+
+Press Ctrl-D or type 'exit' to leave the interactive shell.
+\end{lstlisting}
+
+\section{show -- generate schematics using graphviz}
+\label{cmd:show}
+\begin{lstlisting}[numbers=left,frame=single]
+ show [options] [selection]
+
+Create a graphviz DOT file for the selected part of the design and compile it
+to a graphics file (usually SVG or PostScript).
+
+ -viewer <viewer>
+ Run the specified command with the graphics file as parameter.
+
+ -format <format>
+ Generate a graphics file in the specified format. Use 'dot' to just
+ generate a .dot file, or other <format> strings such as 'svg' or 'ps'
+ to generate files in other formats (this calls the 'dot' command).
+
+ -lib <verilog_or_ilang_file>
+ Use the specified library file for determining whether cell ports are
+ inputs or outputs. This option can be used multiple times to specify
+ more than one library.
+
+ note: in most cases it is better to load the library before calling
+ show with 'read_verilog -lib <filename>'. it is also possible to
+ load liberty files with 'read_liberty -lib <filename>'.
+
+ -prefix <prefix>
+ generate <prefix>.* instead of ~/.yosys_show.*
+
+ -color <color> <object>
+ assign the specified color to the specified object. The object can be
+ a single selection wildcard expressions or a saved set of objects in
+ the @<name> syntax (see "help select" for details).
+
+ -label <text> <object>
+ assign the specified label text to the specified object. The object can
+ be a single selection wildcard expressions or a saved set of objects in
+ the @<name> syntax (see "help select" for details).
+
+ -colors <seed>
+ Randomly assign colors to the wires. The integer argument is the seed
+ for the random number generator. Change the seed value if the colored
+ graph still is ambiguous. A seed of zero deactivates the coloring.
+
+ -colorattr <attribute_name>
+ Use the specified attribute to assign colors. A unique color is
+ assigned to each unique value of this attribute.
+
+ -width
+ annotate busses with a label indicating the width of the bus.
+
+ -signed
+ mark ports (A, B) that are declared as signed (using the [AB]_SIGNED
+ cell parameter) with an asterisk next to the port name.
+
+ -stretch
+ stretch the graph so all inputs are on the left side and all outputs
+ (including inout ports) are on the right side.
+
+ -pause
+ wait for the use to press enter to before returning
+
+ -enum
+ enumerate objects with internal ($-prefixed) names
+
+ -long
+ do not abbreviate objects with internal ($-prefixed) names
+
+ -notitle
+ do not add the module name as graph title to the dot file
+
+When no <format> is specified, 'dot' is used. When no <format> and <viewer> is
+specified, 'xdot' is used to display the schematic.
+
+The generated output files are '~/.yosys_show.dot' and '~/.yosys_show.<format>',
+unless another prefix is specified using -prefix <prefix>.
+
+Yosys on Windows and YosysJS use different defaults: The output is written
+to 'show.dot' in the current directory and new viewer is launched each time
+the 'show' command is executed.
+\end{lstlisting}
+
+\section{shregmap -- map shift registers}
+\label{cmd:shregmap}
+\begin{lstlisting}[numbers=left,frame=single]
+ shregmap [options] [selection]
+
+This pass converts chains of $_DFF_[NP]_ gates to target specific shift register
+primitives. The generated shift register will be of type $__SHREG_DFF_[NP]_ and
+will use the same interface as the original $_DFF_*_ cells. The cell parameter
+'DEPTH' will contain the depth of the shift register. Use a target-specific
+'techmap' map file to convert those cells to the actual target cells.
+
+ -minlen N
+ minimum length of shift register (default = 2)
+ (this is the length after -keep_before and -keep_after)
+
+ -maxlen N
+ maximum length of shift register (default = no limit)
+ larger chains will be mapped to multiple shift register instances
+
+ -keep_before N
+ number of DFFs to keep before the shift register (default = 0)
+
+ -keep_after N
+ number of DFFs to keep after the shift register (default = 0)
+
+ -clkpol pos|neg|any
+ limit match to only positive or negative edge clocks. (default = any)
+
+ -enpol pos|neg|none|any_or_none|any
+ limit match to FFs with the specified enable polarity. (default = none)
+
+ -match <cell_type>[:<d_port_name>:<q_port_name>]
+ match the specified cells instead of $_DFF_N_ and $_DFF_P_. If
+ ':<d_port_name>:<q_port_name>' is omitted then 'D' and 'Q' is used
+ by default. E.g. the option '-clkpol pos' is just an alias for
+ '-match $_DFF_P_', which is an alias for '-match $_DFF_P_:D:Q'.
+
+ -params
+ instead of encoding the clock and enable polarity in the cell name by
+ deriving from the original cell name, simply name all generated cells
+ $__SHREG_ and use CLKPOL and ENPOL parameters. An ENPOL value of 2 is
+ used to denote cells without enable input. The ENPOL parameter is
+ omitted when '-enpol none' (or no -enpol option) is passed.
+
+ -zinit
+ assume the shift register is automatically zero-initialized, so it
+ becomes legal to merge zero initialized FFs into the shift register.
+
+ -init
+ map initialized registers to the shift reg, add an INIT parameter to
+ generated cells with the initialization value. (first bit to shift out
+ in LSB position)
+
+ -tech greenpak4
+ map to greenpak4 shift registers.
+\end{lstlisting}
+
+\section{simplemap -- mapping simple coarse-grain cells}
+\label{cmd:simplemap}
+\begin{lstlisting}[numbers=left,frame=single]
+ simplemap [selection]
+
+This pass maps a small selection of simple coarse-grain cells to yosys gate
+primitives. The following internal cell types are mapped by this pass:
+
+ $not, $pos, $and, $or, $xor, $xnor
+ $reduce_and, $reduce_or, $reduce_xor, $reduce_xnor, $reduce_bool
+ $logic_not, $logic_and, $logic_or, $mux, $tribuf
+ $sr, $ff, $dff, $dffsr, $adff, $dlatch
+\end{lstlisting}
+
+\section{singleton -- create singleton modules}
+\label{cmd:singleton}
+\begin{lstlisting}[numbers=left,frame=single]
+ singleton [selection]
+
+By default, a module that is instantiated by several other modules is only
+kept once in the design. This preserves the original modularity of the design
+and reduces the overall size of the design in memory. But it prevents certain
+optimizations and other operations on the design. This pass creates singleton
+modules for all selected cells. The created modules are marked with the
+'singleton' attribute.
+
+This commands only operates on modules that by themself have the 'singleton'
+attribute set (the 'top' module is a singleton implicitly).
+\end{lstlisting}
+
+\section{splice -- create explicit splicing cells}
+\label{cmd:splice}
+\begin{lstlisting}[numbers=left,frame=single]
+ splice [options] [selection]
+
+This command adds $slice and $concat cells to the design to make the splicing
+of multi-bit signals explicit. This for example is useful for coarse grain
+synthesis, where dedicated hardware is needed to splice signals.
+
+ -sel_by_cell
+ only select the cell ports to rewire by the cell. if the selection
+ contains a cell, than all cell inputs are rewired, if necessary.
+
+ -sel_by_wire
+ only select the cell ports to rewire by the wire. if the selection
+ contains a wire, than all cell ports driven by this wire are wired,
+ if necessary.
+
+ -sel_any_bit
+ it is sufficient if the driver of any bit of a cell port is selected.
+ by default all bits must be selected.
+
+ -wires
+ also add $slice and $concat cells to drive otherwise unused wires.
+
+ -no_outputs
+ do not rewire selected module outputs.
+
+ -port <name>
+ only rewire cell ports with the specified name. can be used multiple
+ times. implies -no_output.
+
+ -no_port <name>
+ do not rewire cell ports with the specified name. can be used multiple
+ times. can not be combined with -port <name>.
+
+By default selected output wires and all cell ports of selected cells driven
+by selected wires are rewired.
+\end{lstlisting}
+
+\section{splitnets -- split up multi-bit nets}
+\label{cmd:splitnets}
+\begin{lstlisting}[numbers=left,frame=single]
+ splitnets [options] [selection]
+
+This command splits multi-bit nets into single-bit nets.
+
+ -format char1[char2[char3]]
+ the first char is inserted between the net name and the bit index, the
+ second char is appended to the netname. e.g. -format () creates net
+ names like 'mysignal(42)'. the 3rd character is the range separation
+ character when creating multi-bit wires. the default is '[]:'.
+
+ -ports
+ also split module ports. per default only internal signals are split.
+
+ -driver
+ don't blindly split nets in individual bits. instead look at the driver
+ and split nets so that no driver drives only part of a net.
+\end{lstlisting}
+
+\section{stat -- print some statistics}
+\label{cmd:stat}
+\begin{lstlisting}[numbers=left,frame=single]
+ stat [options] [selection]
+
+Print some statistics (number of objects) on the selected portion of the
+design.
+
+ -top <module>
+ print design hierarchy with this module as top. if the design is fully
+ selected and a module has the 'top' attribute set, this module is used
+ default value for this option.
+
+ -liberty <liberty_file>
+ use cell area information from the provided liberty file
+
+ -width
+ annotate internal cell types with their word width.
+ e.g. $add_8 for an 8 bit wide $add cell.
+\end{lstlisting}
+
+\section{submod -- moving part of a module to a new submodule}
+\label{cmd:submod}
+\begin{lstlisting}[numbers=left,frame=single]
+ submod [-copy] [selection]
+
+This pass identifies all cells with the 'submod' attribute and moves them to
+a newly created module. The value of the attribute is used as name for the
+cell that replaces the group of cells with the same attribute value.
+
+This pass can be used to create a design hierarchy in flat design. This can
+be useful for analyzing or reverse-engineering a design.
+
+This pass only operates on completely selected modules with no processes
+or memories.
+
+
+ submod -name <name> [-copy] [selection]
+
+As above, but don't use the 'submod' attribute but instead use the selection.
+Only objects from one module might be selected. The value of the -name option
+is used as the value of the 'submod' attribute above.
+
+By default the cells are 'moved' from the source module and the source module
+will use an instance of the new module after this command is finished. Call
+with -copy to not modify the source module.
+\end{lstlisting}
+
+\section{synth -- generic synthesis script}
+\label{cmd:synth}
+\begin{lstlisting}[numbers=left,frame=single]
+ synth [options]
+
+This command runs the default synthesis script. This command does not operate
+on partly selected designs.
+
+ -top <module>
+ use the specified module as top module (default='top')
+
+ -auto-top
+ automatically determine the top of the design hierarchy
+
+ -flatten
+ flatten the design before synthesis. this will pass '-auto-top' to
+ 'hierarchy' if no top module is specified.
+
+ -encfile <file>
+ passed to 'fsm_recode' via 'fsm'
+
+ -nofsm
+ do not run FSM optimization
+
+ -noabc
+ do not run abc (as if yosys was compiled without ABC support)
+
+ -noalumacc
+ do not run 'alumacc' pass. i.e. keep arithmetic operators in
+ their direct form ($add, $sub, etc.).
+
+ -nordff
+ passed to 'memory'. prohibits merging of FFs into memory read ports
+
+ -run <from_label>[:<to_label>]
+ only run the commands between the labels (see below). an empty
+ from label is synonymous to 'begin', and empty to label is
+ synonymous to the end of the command list.
+
+
+The following commands are executed by this synthesis command:
+
+ begin:
+ hierarchy -check [-top <top> | -auto-top]
+
+ coarse:
+ proc
+ flatten (if -flatten)
+ opt_expr
+ opt_clean
+ check
+ opt
+ wreduce
+ alumacc
+ share
+ opt
+ fsm
+ opt -fast
+ memory -nomap
+ opt_clean
+
+ fine:
+ opt -fast -full
+ memory_map
+ opt -full
+ techmap
+ opt -fast
+ abc -fast
+ opt -fast
+
+ check:
+ hierarchy -check
+ stat
+ check
+\end{lstlisting}
+
+\section{synth\_gowin -- synthesis for Gowin FPGAs}
+\label{cmd:synth_gowin}
+\begin{lstlisting}[numbers=left,frame=single]
+ synth_gowin [options]
+
+This command runs synthesis for Gowin FPGAs. This work is experimental.
+
+ -top <module>
+ use the specified module as top module (default='top')
+
+ -vout <file>
+ write the design to the specified Verilog netlist file. writing of an
+ output file is omitted if this parameter is not specified.
+
+ -run <from_label>:<to_label>
+ only run the commands between the labels (see below). an empty
+ from label is synonymous to 'begin', and empty to label is
+ synonymous to the end of the command list.
+
+ -retime
+ run 'abc' with -dff option
+
+
+The following commands are executed by this synthesis command:
+
+ begin:
+ read_verilog -lib +/gowin/cells_sim.v
+ hierarchy -check -top <top>
+
+ flatten:
+ proc
+ flatten
+ tribuf -logic
+ deminout
+
+ coarse:
+ synth -run coarse
+
+ fine:
+ opt -fast -mux_undef -undriven -fine
+ memory_map
+ opt -undriven -fine
+ techmap
+ clean -purge
+ splitnets -ports
+ setundef -undriven -zero
+ abc -dff (only if -retime)
+
+ map_luts:
+ abc -lut 4
+ clean
+
+ map_cells:
+ techmap -map +/gowin/cells_map.v
+ hilomap -hicell VCC V -locell GND G
+ iopadmap -inpad IBUF O:I -outpad OBUF I:O
+ clean -purge
+
+ check:
+ hierarchy -check
+ stat
+ check -noinit
+
+ vout:
+ write_verilog -attr2comment -defparam -renameprefix gen <file-name>
+\end{lstlisting}
+
+\section{synth\_greenpak4 -- synthesis for GreenPAK4 FPGAs}
+\label{cmd:synth_greenpak4}
+\begin{lstlisting}[numbers=left,frame=single]
+ synth_greenpak4 [options]
+
+This command runs synthesis for GreenPAK4 FPGAs. This work is experimental.
+
+ -top <module>
+ use the specified module as top module (default='top')
+
+ -part <part>
+ synthesize for the specified part. Valid values are SLG46140V,
+ SLG46620V, and SLG46621V (default).
+
+ -json <file>
+ write the design to the specified JSON file. writing of an output file
+ is omitted if this parameter is not specified.
+
+ -run <from_label>:<to_label>
+ only run the commands between the labels (see below). an empty
+ from label is synonymous to 'begin', and empty to label is
+ synonymous to the end of the command list.
+
+ -noflatten
+ do not flatten design before synthesis
+
+ -retime
+ run 'abc' with -dff option
+
+
+The following commands are executed by this synthesis command:
+
+ begin:
+ read_verilog -lib +/greenpak4/cells_sim.v
+ hierarchy -check -top <top>
+
+ flatten: (unless -noflatten)
+ proc
+ flatten
+ tribuf -logic
+
+ coarse:
+ synth -run coarse
+
+ fine:
+ greenpak4_counters
+ clean
+ opt -fast -mux_undef -undriven -fine
+ memory_map
+ opt -undriven -fine
+ techmap
+ dfflibmap -prepare -liberty +/greenpak4/gp_dff.lib
+ opt -fast
+ abc -dff (only if -retime)
+
+ map_luts:
+ nlutmap -assert -luts 0,6,8,2 (for -part SLG46140V)
+ nlutmap -assert -luts 2,8,16,2 (for -part SLG46620V)
+ nlutmap -assert -luts 2,8,16,2 (for -part SLG46621V)
+ clean
+
+ map_cells:
+ shregmap -tech greenpak4
+ dfflibmap -liberty +/greenpak4/gp_dff.lib
+ dffinit -ff GP_DFF Q INIT
+ dffinit -ff GP_DFFR Q INIT
+ dffinit -ff GP_DFFS Q INIT
+ dffinit -ff GP_DFFSR Q INIT
+ iopadmap -bits -inpad GP_IBUF OUT:IN -outpad GP_OBUF IN:OUT -inoutpad GP_OBUF OUT:IN -toutpad GP_OBUFT OE:IN:OUT -tinoutpad GP_IOBUF OE:OUT:IN:IO
+ attrmvcp -attr src -attr LOC t:GP_OBUF t:GP_OBUFT t:GP_IOBUF n:*
+ attrmvcp -attr src -attr LOC -driven t:GP_IBUF n:*
+ techmap -map +/greenpak4/cells_map.v
+ greenpak4_dffinv
+ clean
+
+ check:
+ hierarchy -check
+ stat
+ check -noinit
+
+ json:
+ write_json <file-name>
+\end{lstlisting}
+
+\section{synth\_ice40 -- synthesis for iCE40 FPGAs}
+\label{cmd:synth_ice40}
+\begin{lstlisting}[numbers=left,frame=single]
+ synth_ice40 [options]
+
+This command runs synthesis for iCE40 FPGAs.
+
+ -top <module>
+ use the specified module as top module (default='top')
+
+ -blif <file>
+ write the design to the specified BLIF file. writing of an output file
+ is omitted if this parameter is not specified.
+
+ -edif <file>
+ write the design to the specified edif file. writing of an output file
+ is omitted if this parameter is not specified.
+
+ -run <from_label>:<to_label>
+ only run the commands between the labels (see below). an empty
+ from label is synonymous to 'begin', and empty to label is
+ synonymous to the end of the command list.
+
+ -noflatten
+ do not flatten design before synthesis
+
+ -retime
+ run 'abc' with -dff option
+
+ -nocarry
+ do not use SB_CARRY cells in output netlist
+
+ -nobram
+ do not use SB_RAM40_4K* cells in output netlist
+
+ -abc2
+ run two passes of 'abc' for slightly improved logic density
+
+
+The following commands are executed by this synthesis command:
+
+ begin:
+ read_verilog -lib +/ice40/cells_sim.v
+ hierarchy -check -top <top>
+
+ flatten: (unless -noflatten)
+ proc
+ flatten
+ tribuf -logic
+ deminout
+
+ coarse:
+ synth -run coarse
+
+ bram: (skip if -nobram)
+ memory_bram -rules +/ice40/brams.txt
+ techmap -map +/ice40/brams_map.v
+
+ fine:
+ opt -fast -mux_undef -undriven -fine
+ memory_map
+ opt -undriven -fine
+ techmap -map +/techmap.v -map +/ice40/arith_map.v
+ abc -dff (only if -retime)
+ ice40_opt
+
+ map_ffs:
+ dffsr2dff
+ dff2dffe -direct-match $_DFF_*
+ techmap -map +/ice40/cells_map.v
+ opt_expr -mux_undef
+ simplemap
+ ice40_ffinit
+ ice40_ffssr
+ ice40_opt -full
+
+ map_luts:
+ abc (only if -abc2)
+ ice40_opt (only if -abc2)
+ techmap -map +/ice40/latches_map.v
+ abc -lut 4
+ clean
+
+ map_cells:
+ techmap -map +/ice40/cells_map.v
+ clean
+
+ check:
+ hierarchy -check
+ stat
+ check -noinit
+
+ blif:
+ write_blif -gates -attr -param <file-name>
+
+ edif:
+ write_edif <file-name>
+\end{lstlisting}
+
+\section{synth\_xilinx -- synthesis for Xilinx FPGAs}
+\label{cmd:synth_xilinx}
+\begin{lstlisting}[numbers=left,frame=single]
+ synth_xilinx [options]
+
+This command runs synthesis for Xilinx FPGAs. This command does not operate on
+partly selected designs. At the moment this command creates netlists that are
+compatible with 7-Series Xilinx devices.
+
+ -top <module>
+ use the specified module as top module
+
+ -edif <file>
+ write the design to the specified edif file. writing of an output file
+ is omitted if this parameter is not specified.
+
+ -run <from_label>:<to_label>
+ only run the commands between the labels (see below). an empty
+ from label is synonymous to 'begin', and empty to label is
+ synonymous to the end of the command list.
+
+ -flatten
+ flatten design before synthesis
+
+ -retime
+ run 'abc' with -dff option
+
+
+The following commands are executed by this synthesis command:
+
+ begin:
+ read_verilog -lib +/xilinx/cells_sim.v
+ read_verilog -lib +/xilinx/cells_xtra.v
+ read_verilog -lib +/xilinx/brams_bb.v
+ read_verilog -lib +/xilinx/drams_bb.v
+ hierarchy -check -top <top>
+
+ flatten: (only if -flatten)
+ proc
+ flatten
+
+ coarse:
+ synth -run coarse
+
+ bram:
+ memory_bram -rules +/xilinx/brams.txt
+ techmap -map +/xilinx/brams_map.v
+
+ dram:
+ memory_bram -rules +/xilinx/drams.txt
+ techmap -map +/xilinx/drams_map.v
+
+ fine:
+ opt -fast -full
+ memory_map
+ dffsr2dff
+ dff2dffe
+ opt -full
+ techmap -map +/techmap.v -map +/xilinx/arith_map.v
+ opt -fast
+
+ map_luts:
+ abc -luts 2:2,3,6:5,10,20 [-dff]
+ clean
+
+ map_cells:
+ techmap -map +/xilinx/cells_map.v
+ dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT
+ clean
+
+ check:
+ hierarchy -check
+ stat
+ check -noinit
+
+ edif: (only if -edif)
+ write_edif <file-name>
+\end{lstlisting}
+
+\section{tcl -- execute a TCL script file}
+\label{cmd:tcl}
+\begin{lstlisting}[numbers=left,frame=single]
+ tcl <filename>
+
+This command executes the tcl commands in the specified file.
+Use 'yosys cmd' to run the yosys command 'cmd' from tcl.
+
+The tcl command 'yosys -import' can be used to import all yosys
+commands directly as tcl commands to the tcl shell. The yosys
+command 'proc' is wrapped using the tcl command 'procs' in order
+to avoid a name collision with the tcl builtin command 'proc'.
+\end{lstlisting}
+
+\section{techmap -- generic technology mapper}
+\label{cmd:techmap}
+\begin{lstlisting}[numbers=left,frame=single]
+ techmap [-map filename] [selection]
+
+This pass implements a very simple technology mapper that replaces cells in
+the design with implementations given in form of a Verilog or ilang source
+file.
+
+ -map filename
+ the library of cell implementations to be used.
+ without this parameter a builtin library is used that
+ transforms the internal RTL cells to the internal gate
+ library.
+
+ -map %<design-name>
+ like -map above, but with an in-memory design instead of a file.
+
+ -extern
+ load the cell implementations as separate modules into the design
+ instead of inlining them.
+
+ -max_iter <number>
+ only run the specified number of iterations.
+
+ -recursive
+ instead of the iterative breadth-first algorithm use a recursive
+ depth-first algorithm. both methods should yield equivalent results,
+ but may differ in performance.
+
+ -autoproc
+ Automatically call "proc" on implementations that contain processes.
+
+ -assert
+ this option will cause techmap to exit with an error if it can't map
+ a selected cell. only cell types that end on an underscore are accepted
+ as final cell types by this mode.
+
+ -D <define>, -I <incdir>
+ this options are passed as-is to the Verilog frontend for loading the
+ map file. Note that the Verilog frontend is also called with the
+ '-ignore_redef' option set.
+
+When a module in the map file has the 'techmap_celltype' attribute set, it will
+match cells with a type that match the text value of this attribute. Otherwise
+the module name will be used to match the cell.
+
+When a module in the map file has the 'techmap_simplemap' attribute set, techmap
+will use 'simplemap' (see 'help simplemap') to map cells matching the module.
+
+When a module in the map file has the 'techmap_maccmap' attribute set, techmap
+will use 'maccmap' (see 'help maccmap') to map cells matching the module.
+
+When a module in the map file has the 'techmap_wrap' attribute set, techmap
+will create a wrapper for the cell and then run the command string that the
+attribute is set to on the wrapper module.
+
+All wires in the modules from the map file matching the pattern _TECHMAP_*
+or *._TECHMAP_* are special wires that are used to pass instructions from
+the mapping module to the techmap command. At the moment the following special
+wires are supported:
+
+ _TECHMAP_FAIL_
+ When this wire is set to a non-zero constant value, techmap will not
+ use this module and instead try the next module with a matching
+ 'techmap_celltype' attribute.
+
+ When such a wire exists but does not have a constant value after all
+ _TECHMAP_DO_* commands have been executed, an error is generated.
+
+ _TECHMAP_DO_*
+ This wires are evaluated in alphabetical order. The constant text value
+ of this wire is a yosys command (or sequence of commands) that is run
+ by techmap on the module. A common use case is to run 'proc' on modules
+ that are written using always-statements.
+
+ When such a wire has a non-constant value at the time it is to be
+ evaluated, an error is produced. That means it is possible for such a
+ wire to start out as non-constant and evaluate to a constant value
+ during processing of other _TECHMAP_DO_* commands.
+
+ A _TECHMAP_DO_* command may start with the special token 'CONSTMAP; '.
+ in this case techmap will create a copy for each distinct configuration
+ of constant inputs and shorted inputs at this point and import the
+ constant and connected bits into the map module. All further commands
+ are executed in this copy. This is a very convenient way of creating
+ optimized specializations of techmap modules without using the special
+ parameters described below.
+
+ A _TECHMAP_DO_* command may start with the special token 'RECURSION; '.
+ then techmap will recursively replace the cells in the module with their
+ implementation. This is not affected by the -max_iter option.
+
+ It is possible to combine both prefixes to 'RECURSION; CONSTMAP; '.
+
+In addition to this special wires, techmap also supports special parameters in
+modules in the map file:
+
+ _TECHMAP_CELLTYPE_
+ When a parameter with this name exists, it will be set to the type name
+ of the cell that matches the module.
+
+ _TECHMAP_CONSTMSK_<port-name>_
+ _TECHMAP_CONSTVAL_<port-name>_
+ When this pair of parameters is available in a module for a port, then
+ former has a 1-bit for each constant input bit and the latter has the
+ value for this bit. The unused bits of the latter are set to undef (x).
+
+ _TECHMAP_BITS_CONNMAP_
+ _TECHMAP_CONNMAP_<port-name>_
+ For an N-bit port, the _TECHMAP_CONNMAP_<port-name>_ parameter, if it
+ exists, will be set to an N*_TECHMAP_BITS_CONNMAP_ bit vector containing
+ N words (of _TECHMAP_BITS_CONNMAP_ bits each) that assign each single
+ bit driver a unique id. The values 0-3 are reserved for 0, 1, x, and z.
+ This can be used to detect shorted inputs.
+
+When a module in the map file has a parameter where the according cell in the
+design has a port, the module from the map file is only used if the port in
+the design is connected to a constant value. The parameter is then set to the
+constant value.
+
+A cell with the name _TECHMAP_REPLACE_ in the map file will inherit the name
+of the cell that is being replaced.
+
+See 'help extract' for a pass that does the opposite thing.
+
+See 'help flatten' for a pass that does flatten the design (which is
+essentially techmap but using the design itself as map library).
+\end{lstlisting}
+
+\section{tee -- redirect command output to file}
+\label{cmd:tee}
+\begin{lstlisting}[numbers=left,frame=single]
+ tee [-q] [-o logfile|-a logfile] cmd
+
+Execute the specified command, optionally writing the commands output to the
+specified logfile(s).
+
+ -q
+ Do not print output to the normal destination (console and/or log file)
+
+ -o logfile
+ Write output to this file, truncate if exists.
+
+ -a logfile
+ Write output to this file, append if exists.
+
+ +INT, -INT
+ Add/subract INT from the -v setting for this command.
+\end{lstlisting}
+
+\section{test\_abcloop -- automatically test handling of loops in abc command}
+\label{cmd:test_abcloop}
+\begin{lstlisting}[numbers=left,frame=single]
+ test_abcloop [options]
+
+Test handling of logic loops in ABC.
+
+ -n {integer}
+ create this number of circuits and test them (default = 100).
+
+ -s {positive_integer}
+ use this value as rng seed value (default = unix time).
+\end{lstlisting}
+
+\section{test\_autotb -- generate simple test benches}
+\label{cmd:test_autotb}
+\begin{lstlisting}[numbers=left,frame=single]
+ test_autotb [options] [filename]
+
+Automatically create primitive Verilog test benches for all modules in the
+design. The generated testbenches toggle the input pins of the module in
+a semi-random manner and dumps the resulting output signals.
+
+This can be used to check the synthesis results for simple circuits by
+comparing the testbench output for the input files and the synthesis results.
+
+The backend automatically detects clock signals. Additionally a signal can
+be forced to be interpreted as clock signal by setting the attribute
+'gentb_clock' on the signal.
+
+The attribute 'gentb_constant' can be used to force a signal to a constant
+value after initialization. This can e.g. be used to force a reset signal
+low in order to explore more inner states in a state machine.
+
+ -n <int>
+ number of iterations the test bench should run (default = 1000)
+\end{lstlisting}
+
+\section{test\_cell -- automatically test the implementation of a cell type}
+\label{cmd:test_cell}
+\begin{lstlisting}[numbers=left,frame=single]
+ test_cell [options] {cell-types}
+
+Tests the internal implementation of the given cell type (for example '$add')
+by comparing SAT solver, EVAL and TECHMAP implementations of the cell types..
+
+Run with 'all' instead of a cell type to run the test on all supported
+cell types. Use for example 'all /$add' for all cell types except $add.
+
+ -n {integer}
+ create this number of cell instances and test them (default = 100).
+
+ -s {positive_integer}
+ use this value as rng seed value (default = unix time).
+
+ -f {ilang_file}
+ don't generate circuits. instead load the specified ilang file.
+
+ -w {filename_prefix}
+ don't test anything. just generate the circuits and write them
+ to ilang files with the specified prefix
+
+ -map {filename}
+ pass this option to techmap.
+
+ -simlib
+ use "techmap -D SIMLIB_NOCHECKS -map +/simlib.v -max_iter 2 -autoproc"
+
+ -aigmap
+ instead of calling "techmap", call "aigmap"
+
+ -muxdiv
+ when creating test benches with dividers, create an additional mux
+ to mask out the division-by-zero case
+
+ -script {script_file}
+ instead of calling "techmap", call "script {script_file}".
+
+ -const
+ set some input bits to random constant values
+
+ -nosat
+ do not check SAT model or run SAT equivalence checking
+
+ -noeval
+ do not check const-eval models
+
+ -edges
+ test cell edges db creator against sat-based implementation
+
+ -v
+ print additional debug information to the console
+
+ -vlog {filename}
+ create a Verilog test bench to test simlib and write_verilog
+\end{lstlisting}
+
+\section{torder -- print cells in topological order}
+\label{cmd:torder}
+\begin{lstlisting}[numbers=left,frame=single]
+ torder [options] [selection]
+
+This command prints the selected cells in topological order.
+
+ -stop <cell_type> <cell_port>
+ do not use the specified cell port in topological sorting
+
+ -noautostop
+ by default Q outputs of internal FF cells and memory read port outputs
+ are not used in topological sorting. this option deactivates that.
+\end{lstlisting}
+
+\section{trace -- redirect command output to file}
+\label{cmd:trace}
+\begin{lstlisting}[numbers=left,frame=single]
+ trace cmd
+
+Execute the specified command, logging all changes the command performs on
+the design in real time.
+\end{lstlisting}
+
+\section{tribuf -- infer tri-state buffers}
+\label{cmd:tribuf}
+\begin{lstlisting}[numbers=left,frame=single]
+ tribuf [options] [selection]
+
+This pass transforms $mux cells with 'z' inputs to tristate buffers.
+
+ -merge
+ merge multiple tri-state buffers driving the same net
+ into a single buffer.
+
+ -logic
+ convert tri-state buffers that do not drive output ports
+ to non-tristate logic. this option implies -merge.
+\end{lstlisting}
+
+\section{verific -- load Verilog and VHDL designs using Verific}
+\label{cmd:verific}
+\begin{lstlisting}[numbers=left,frame=single]
+ verific {-vlog95|-vlog2k|-sv2005|-sv2009|-sv} <verilog-file>..
+
+Load the specified Verilog/SystemVerilog files into Verific.
+
+
+ verific {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008} <vhdl-file>..
+
+Load the specified VHDL files into Verific.
+
+
+ verific -import [-gates] {-all | <top-module>..}
+
+Elaborate the design for the specified top modules, import to Yosys and
+reset the internal state of Verific. A gate-level netlist is created
+when called with -gates.
+
+Visit http://verific.com/ for more information on Verific.
+\end{lstlisting}
+
+\section{verilog\_defaults -- set default options for read\_verilog}
+\label{cmd:verilog_defaults}
+\begin{lstlisting}[numbers=left,frame=single]
+ verilog_defaults -add [options]
+
+Add the specified options to the list of default options to read_verilog.
+
+
+ verilog_defaults -clear
+
+Clear the list of Verilog default options.
+
+
+ verilog_defaults -push
+ verilog_defaults -pop
+
+Push or pop the list of default options to a stack. Note that -push does
+not imply -clear.
+\end{lstlisting}
+
+\section{vhdl2verilog -- importing VHDL designs using vhdl2verilog}
+\label{cmd:vhdl2verilog}
+\begin{lstlisting}[numbers=left,frame=single]
+ vhdl2verilog [options] <vhdl-file>..
+
+This command reads VHDL source files using the 'vhdl2verilog' tool and the
+Yosys Verilog frontend.
+
+ -out <out_file>
+ do not import the vhdl2verilog output. instead write it to the
+ specified file.
+
+ -vhdl2verilog_dir <directory>
+ do use the specified vhdl2verilog installation. this is the directory
+ that contains the setup_env.sh file. when this option is not present,
+ it is assumed that vhdl2verilog is in the PATH environment variable.
+
+ -top <top-entity-name>
+ The name of the top entity. This option is mandatory.
+
+The following options are passed as-is to vhdl2verilog:
+
+ -arch <architecture_name>
+ -unroll_generate
+ -nogenericeval
+ -nouniquify
+ -oldparser
+ -suppress <list>
+ -quiet
+ -nobanner
+ -mapfile <file>
+
+vhdl2verilog can be obtained from:
+http://www.edautils.com/vhdl2verilog.html
+\end{lstlisting}
+
+\section{wreduce -- reduce the word size of operations if possible}
+\label{cmd:wreduce}
+\begin{lstlisting}[numbers=left,frame=single]
+ wreduce [options] [selection]
+
+This command reduces the word size of operations. For example it will replace
+the 32 bit adders in the following code with adders of more appropriate widths:
+
+ module test(input [3:0] a, b, c, output [7:0] y);
+ assign y = a + b + c + 1;
+ endmodule
+
+Options:
+
+ -memx
+ Do not change the width of memory address ports. Use this options in
+ flows that use the 'memory_memx' pass.
+\end{lstlisting}
+
+\section{write\_blif -- write design to BLIF file}
+\label{cmd:write_blif}
+\begin{lstlisting}[numbers=left,frame=single]
+ write_blif [options] [filename]
+
+Write the current design to an BLIF file.
+
+ -top top_module
+ set the specified module as design top module
+
+ -buf <cell-type> <in-port> <out-port>
+ use cells of type <cell-type> with the specified port names for buffers
+
+ -unbuf <cell-type> <in-port> <out-port>
+ replace buffer cells with the specified name and port names with
+ a .names statement that models a buffer
+
+ -true <cell-type> <out-port>
+ -false <cell-type> <out-port>
+ -undef <cell-type> <out-port>
+ use the specified cell types to drive nets that are constant 1, 0, or
+ undefined. when '-' is used as <cell-type>, then <out-port> specifies
+ the wire name to be used for the constant signal and no cell driving
+ that wire is generated. when '+' is used as <cell-type>, then <out-port>
+ specifies the wire name to be used for the constant signal and a .names
+ statement is generated to drive the wire.
+
+ -noalias
+ if a net name is aliasing another net name, then by default a net
+ without fanout is created that is driven by the other net. This option
+ suppresses the generation of this nets without fanout.
+
+The following options can be useful when the generated file is not going to be
+read by a BLIF parser but a custom tool. It is recommended to not name the output
+file *.blif when any of this options is used.
+
+ -icells
+ do not translate Yosys's internal gates to generic BLIF logic
+ functions. Instead create .subckt or .gate lines for all cells.
+
+ -gates
+ print .gate instead of .subckt lines for all cells that are not
+ instantiations of other modules from this design.
+
+ -conn
+ do not generate buffers for connected wires. instead use the
+ non-standard .conn statement.
+
+ -attr
+ use the non-standard .attr statement to write cell attributes
+
+ -param
+ use the non-standard .param statement to write cell parameters
+
+ -cname
+ use the non-standard .cname statement to write cell names
+
+ -blackbox
+ write blackbox cells with .blackbox statement.
+
+ -impltf
+ do not write definitions for the $true, $false and $undef wires.
+\end{lstlisting}
+
+\section{write\_btor -- write design to BTOR file}
+\label{cmd:write_btor}
+\begin{lstlisting}[numbers=left,frame=single]
+ write_btor [filename]
+
+Write the current design to an BTOR file.
+\end{lstlisting}
+
+\section{write\_edif -- write design to EDIF netlist file}
+\label{cmd:write_edif}
+\begin{lstlisting}[numbers=left,frame=single]
+ write_edif [options] [filename]
+
+Write the current design to an EDIF netlist file.
+
+ -top top_module
+ set the specified module as design top module
+
+ -nogndvcc
+ do not create "GND" and "VCC" cells. (this will produce an error
+ if the design contains constant nets. use "hilomap" to map to custom
+ constant drivers first)
+
+Unfortunately there are different "flavors" of the EDIF file format. This
+command generates EDIF files for the Xilinx place&route tools. It might be
+necessary to make small modifications to this command when a different tool
+is targeted.
+\end{lstlisting}
+
+\section{write\_file -- write a text to a file}
+\label{cmd:write_file}
+\begin{lstlisting}[numbers=left,frame=single]
+ write_file [options] output_file [input_file]
+
+Write the text from the input file to the output file.
+
+ -a
+ Append to output file (instead of overwriting)
+
+
+Inside a script the input file can also can a here-document:
+
+ write_file hello.txt <<EOT
+ Hello World!
+ EOT
+\end{lstlisting}
+
+\section{write\_ilang -- write design to ilang file}
+\label{cmd:write_ilang}
+\begin{lstlisting}[numbers=left,frame=single]
+ write_ilang [filename]
+
+Write the current design to an 'ilang' file. (ilang is a text representation
+of a design in yosys's internal format.)
+
+ -selected
+ only write selected parts of the design.
+\end{lstlisting}
+
+\section{write\_intersynth -- write design to InterSynth netlist file}
+\label{cmd:write_intersynth}
+\begin{lstlisting}[numbers=left,frame=single]
+ write_intersynth [options] [filename]
+
+Write the current design to an 'intersynth' netlist file. InterSynth is
+a tool for Coarse-Grain Example-Driven Interconnect Synthesis.
+
+ -notypes
+ do not generate celltypes and conntypes commands. i.e. just output
+ the netlists. this is used for postsilicon synthesis.
+
+ -lib <verilog_or_ilang_file>
+ Use the specified library file for determining whether cell ports are
+ inputs or outputs. This option can be used multiple times to specify
+ more than one library.
+
+ -selected
+ only write selected modules. modules must be selected entirely or
+ not at all.
+
+http://www.clifford.at/intersynth/
+\end{lstlisting}
+
+\section{write\_json -- write design to a JSON file}
+\label{cmd:write_json}
+\begin{lstlisting}[numbers=left,frame=single]
+ write_json [options] [filename]
+
+Write a JSON netlist of the current design.
+
+ -aig
+ include AIG models for the different gate types
+
+
+The general syntax of the JSON output created by this command is as follows:
+
+ {
+ "modules": {
+ <module_name>: {
+ "ports": {
+ <port_name>: <port_details>,
+ ...
+ },
+ "cells": {
+ <cell_name>: <cell_details>,
+ ...
+ },
+ "netnames": {
+ <net_name>: <net_details>,
+ ...
+ }
+ }
+ },
+ "models": {
+ ...
+ },
+ }
+
+Where <port_details> is:
+
+ {
+ "direction": <"input" | "output" | "inout">,
+ "bits": <bit_vector>
+ }
+
+And <cell_details> is:
+
+ {
+ "hide_name": <1 | 0>,
+ "type": <cell_type>,
+ "parameters": {
+ <parameter_name>: <parameter_value>,
+ ...
+ },
+ "attributes": {
+ <attribute_name>: <attribute_value>,
+ ...
+ },
+ "port_directions": {
+ <port_name>: <"input" | "output" | "inout">,
+ ...
+ },
+ "connections": {
+ <port_name>: <bit_vector>,
+ ...
+ },
+ }
+
+And <net_details> is:
+
+ {
+ "hide_name": <1 | 0>,
+ "bits": <bit_vector>
+ }
+
+The "hide_name" fields are set to 1 when the name of this cell or net is
+automatically created and is likely not of interest for a regular user.
+
+The "port_directions" section is only included for cells for which the
+interface is known.
+
+Module and cell ports and nets can be single bit wide or vectors of multiple
+bits. Each individual signal bit is assigned a unique integer. The <bit_vector>
+values referenced above are vectors of this integers. Signal bits that are
+connected to a constant driver are denoted as string "0" or "1" instead of
+a number.
+
+For example the following Verilog code:
+
+ module test(input x, y);
+ (* keep *) foo #(.P(42), .Q(1337))
+ foo_inst (.A({x, y}), .B({y, x}), .C({4'd10, {4{x}}}));
+ endmodule
+
+Translates to the following JSON output:
+
+ {
+ "modules": {
+ "test": {
+ "ports": {
+ "x": {
+ "direction": "input",
+ "bits": [ 2 ]
+ },
+ "y": {
+ "direction": "input",
+ "bits": [ 3 ]
+ }
+ },
+ "cells": {
+ "foo_inst": {
+ "hide_name": 0,
+ "type": "foo",
+ "parameters": {
+ "Q": 1337,
+ "P": 42
+ },
+ "attributes": {
+ "keep": 1,
+ "src": "test.v:2"
+ },
+ "connections": {
+ "C": [ 2, 2, 2, 2, "0", "1", "0", "1" ],
+ "B": [ 2, 3 ],
+ "A": [ 3, 2 ]
+ }
+ }
+ },
+ "netnames": {
+ "y": {
+ "hide_name": 0,
+ "bits": [ 3 ],
+ "attributes": {
+ "src": "test.v:1"
+ }
+ },
+ "x": {
+ "hide_name": 0,
+ "bits": [ 2 ],
+ "attributes": {
+ "src": "test.v:1"
+ }
+ }
+ }
+ }
+ }
+ }
+
+The models are given as And-Inverter-Graphs (AIGs) in the following form:
+
+ "models": {
+ <model_name>: [
+ /* 0 */ [ <node-spec> ],
+ /* 1 */ [ <node-spec> ],
+ /* 2 */ [ <node-spec> ],
+ ...
+ ],
+ ...
+ },
+
+The following node-types may be used:
+
+ [ "port", <portname>, <bitindex>, <out-list> ]
+ - the value of the specified input port bit
+
+ [ "nport", <portname>, <bitindex>, <out-list> ]
+ - the inverted value of the specified input port bit
+
+ [ "and", <node-index>, <node-index>, <out-list> ]
+ - the ANDed value of the specified nodes
+
+ [ "nand", <node-index>, <node-index>, <out-list> ]
+ - the inverted ANDed value of the specified nodes
+
+ [ "true", <out-list> ]
+ - the constant value 1
+
+ [ "false", <out-list> ]
+ - the constant value 0
+
+All nodes appear in topological order. I.e. only nodes with smaller indices
+are referenced by "and" and "nand" nodes.
+
+The optional <out-list> at the end of a node specification is a list of
+output portname and bitindex pairs, specifying the outputs driven by this node.
+
+For example, the following is the model for a 3-input 3-output $reduce_and cell
+inferred by the following code:
+
+ module test(input [2:0] in, output [2:0] out);
+ assign in = &out;
+ endmodule
+
+ "$reduce_and:3U:3": [
+ /* 0 */ [ "port", "A", 0 ],
+ /* 1 */ [ "port", "A", 1 ],
+ /* 2 */ [ "and", 0, 1 ],
+ /* 3 */ [ "port", "A", 2 ],
+ /* 4 */ [ "and", 2, 3, "Y", 0 ],
+ /* 5 */ [ "false", "Y", 1, "Y", 2 ]
+ ]
+
+Future version of Yosys might add support for additional fields in the JSON
+format. A program processing this format must ignore all unknown fields.
+\end{lstlisting}
+
+\section{write\_smt2 -- write design to SMT-LIBv2 file}
+\label{cmd:write_smt2}
+\begin{lstlisting}[numbers=left,frame=single]
+ write_smt2 [options] [filename]
+
+Write a SMT-LIBv2 [1] description of the current design. For a module with name
+'<mod>' this will declare the sort '<mod>_s' (state of the module) and the
+functions operating on that state.
+
+The '<mod>_s' sort represents a module state. Additional '<mod>_n' functions
+are provided that can be used to access the values of the signals in the module.
+By default only ports, registers, and wires with the 'keep' attribute set are
+made available via such functions. With the -nobv option, multi-bit wires are
+exported as separate functions of type Bool for the individual bits. Without
+-nobv multi-bit wires are exported as single functions of type BitVec.
+
+The '<mod>_t' function evaluates to 'true' when the given pair of states
+describes a valid state transition.
+
+The '<mod>_a' function evaluates to 'true' when the given state satisfies
+the asserts in the module.
+
+The '<mod>_u' function evaluates to 'true' when the given state satisfies
+the assumptions in the module.
+
+The '<mod>_i' function evaluates to 'true' when the given state conforms
+to the initial state. Furthermore the '<mod>_is' function should be asserted
+to be true for initial states in addition to '<mod>_i', and should be
+asserted to be false for non-initial states.
+
+For hierarchical designs, the '<mod>_h' function must be asserted for each
+state to establish the design hierarchy. The '<mod>_h <cellname>' function
+evaluates to the state corresponding to the given cell within <mod>.
+
+ -verbose
+ this will print the recursive walk used to export the modules.
+
+ -nobv
+ disable support for BitVec (FixedSizeBitVectors theory). without this
+ option multi-bit wires are represented using the BitVec sort and
+ support for coarse grain cells (incl. arithmetic) is enabled.
+
+ -nomem
+ disable support for memories (via ArraysEx theory). this option is
+ implied by -nobv. only $mem cells without merged registers in
+ read ports are supported. call "memory" with -nordff to make sure
+ that no registers are merged into $mem read ports. '<mod>_m' functions
+ will be generated for accessing the arrays that are used to represent
+ memories.
+
+ -wires
+ create '<mod>_n' functions for all public wires. by default only ports,
+ registers, and wires with the 'keep' attribute are exported.
+
+ -tpl <template_file>
+ use the given template file. the line containing only the token '%%'
+ is replaced with the regular output of this command.
+
+[1] For more information on SMT-LIBv2 visit http://smt-lib.org/ or read David
+R. Cok's tutorial: http://www.grammatech.com/resources/smt/SMTLIBTutorial.pdf
+
+---------------------------------------------------------------------------
+
+Example:
+
+Consider the following module (test.v). We want to prove that the output can
+never transition from a non-zero value to a zero value.
+
+ module test(input clk, output reg [3:0] y);
+ always @(posedge clk)
+ y <= (y << 1) | ^y;
+ endmodule
+
+For this proof we create the following template (test.tpl).
+
+ ; we need QF_UFBV for this poof
+ (set-logic QF_UFBV)
+
+ ; insert the auto-generated code here
+ %%
+
+ ; declare two state variables s1 and s2
+ (declare-fun s1 () test_s)
+ (declare-fun s2 () test_s)
+
+ ; state s2 is the successor of state s1
+ (assert (test_t s1 s2))
+
+ ; we are looking for a model with y non-zero in s1
+ (assert (distinct (|test_n y| s1) #b0000))
+
+ ; we are looking for a model with y zero in s2
+ (assert (= (|test_n y| s2) #b0000))
+
+ ; is there such a model?
+ (check-sat)
+
+The following yosys script will create a 'test.smt2' file for our proof:
+
+ read_verilog test.v
+ hierarchy -check; proc; opt; check -assert
+ write_smt2 -bv -tpl test.tpl test.smt2
+
+Running 'cvc4 test.smt2' will print 'unsat' because y can never transition
+from non-zero to zero in the test design.
+\end{lstlisting}
+
+\section{write\_smv -- write design to SMV file}
+\label{cmd:write_smv}
+\begin{lstlisting}[numbers=left,frame=single]
+ write_smv [options] [filename]
+
+Write an SMV description of the current design.
+
+ -verbose
+ this will print the recursive walk used to export the modules.
+
+ -tpl <template_file>
+ use the given template file. the line containing only the token '%%'
+ is replaced with the regular output of this command.
+
+THIS COMMAND IS UNDER CONSTRUCTION
+\end{lstlisting}
+
+\section{write\_spice -- write design to SPICE netlist file}
+\label{cmd:write_spice}
+\begin{lstlisting}[numbers=left,frame=single]
+ write_spice [options] [filename]
+
+Write the current design to an SPICE netlist file.
+
+ -big_endian
+ generate multi-bit ports in MSB first order
+ (default is LSB first)
+
+ -neg net_name
+ set the net name for constant 0 (default: Vss)
+
+ -pos net_name
+ set the net name for constant 1 (default: Vdd)
+
+ -nc_prefix
+ prefix for not-connected nets (default: _NC)
+
+ -inames
+ include names of internal ($-prefixed) nets in outputs
+ (default is to use net numbers instead)
+
+ -top top_module
+ set the specified module as design top module
+\end{lstlisting}
+
+\section{write\_verilog -- write design to Verilog file}
+\label{cmd:write_verilog}
+\begin{lstlisting}[numbers=left,frame=single]
+ write_verilog [options] [filename]
+
+Write the current design to a Verilog file.
+
+ -norename
+ without this option all internal object names (the ones with a dollar
+ instead of a backslash prefix) are changed to short names in the
+ format '_<number>_'.
+
+ -renameprefix <prefix>
+ insert this prefix in front of auto-generated instance names
+
+ -noattr
+ with this option no attributes are included in the output
+
+ -attr2comment
+ with this option attributes are included as comments in the output
+
+ -noexpr
+ without this option all internal cells are converted to Verilog
+ expressions.
+
+ -nodec
+ 32-bit constant values are by default dumped as decimal numbers,
+ not bit pattern. This option decativates this feature and instead
+ will write out all constants in binary.
+
+ -nostr
+ Parameters and attributes that are specified as strings in the
+ original input will be output as strings by this back-end. This
+ decativates this feature and instead will write string constants
+ as binary numbers.
+
+ -defparam
+ Use 'defparam' statements instead of the Verilog-2001 syntax for
+ cell parameters.
+
+ -blackboxes
+ usually modules with the 'blackbox' attribute are ignored. with
+ this option set only the modules with the 'blackbox' attribute
+ are written to the output file.
+
+ -selected
+ only write selected modules. modules must be selected entirely or
+ not at all.
+
+ -v
+ verbose output (print new names of all renamed wires and cells)
+
+Note that RTLIL processes can't always be mapped directly to Verilog
+always blocks. This frontend should only be used to export an RTLIL
+netlist, i.e. after the "proc" pass has been used to convert all
+processes to logic networks and registers. A warning is generated when
+this command is called on a design with RTLIL processes.
+\end{lstlisting}
+
+\section{zinit -- add inverters so all FF are zero-initialized}
+\label{cmd:zinit}
+\begin{lstlisting}[numbers=left,frame=single]
+ zinit [options] [selection]
+
+Add inverters as needed to make all FFs zero-initialized.
+
+ -all
+ also add zero initialization to uninitialized FFs
+\end{lstlisting}
+
diff --git a/manual/literature.bib b/manual/literature.bib
new file mode 100644
index 00000000..372e882a
--- /dev/null
+++ b/manual/literature.bib
@@ -0,0 +1,163 @@
+
+@inproceedings{intersynth,
+ title={Example-driven interconnect synthesis for heterogeneous coarse-grain reconfigurable logic},
+ author={Clifford Wolf and Johann Glaser and Florian Schupfer and Jan Haase and Christoph Grimm},
+ booktitle={FDL Proceeding of the 2012 Forum on Specification and Design Languages},
+ pages={194--201},
+ year={2012}
+}
+
+@incollection{intersynthFdlBookChapter,
+ title={Methodology and Example-Driven Interconnect Synthesis for Designing Heterogeneous Coarse-Grain Reconfigurable Architectures},
+ author={Johann Glaser and Clifford Wolf},
+ booktitle={Advances in Models, Methods, and Tools for Complex Chip Design --- Selected contributions from FDL'12},
+ editor={Jan Haase},
+ publisher={Springer},
+ year={2013},
+ note={to appear}
+}
+
+@unpublished{BACC,
+ author = {Clifford Wolf},
+ title = {Design and Implementation of the Yosys Open SYnthesis Suite},
+ note = {Bachelor Thesis, Vienna University of Technology},
+ year = {2013}
+}
+
+@unpublished{VerilogFossEval,
+ author = {Clifford Wolf},
+ title = {Evaluation of Open Source Verilog Synthesis Tools for Feature-Completeness and Extensibility},
+ note = {Unpublished Student Research Paper, Vienna University of Technology},
+ year = {2012}
+}
+
+@article{ABEL,
+ title={A High-Level Design Language for Programmable Logic Devices},
+ author={Kyu Y. Lee and Michael Holley and Mary Bailey and Walter Bright},
+ journal={VLSI Design (Manhasset NY: CPM Publications)},
+ year={June 1985},
+ pages={50-62}
+}
+
+@MISC{Cheng93vl2mv:a,
+ author = {S-T Cheng and G York and R K Brayton},
+ title = {VL2MV: A Compiler from Verilog to BLIF-MV},
+ year = {1993}
+}
+
+@MISC{Odin,
+ author = {Peter Jamieson and Jonathan Rose},
+ title = {A VERILOG RTL SYNTHESIS TOOL FOR HETEROGENEOUS FPGAS},
+ year = {2005}
+}
+
+@inproceedings{vtr2012,
+ title={The VTR Project: Architecture and CAD for FPGAs from Verilog to Routing},
+ author={Jonathan Rose and Jason Luu and Chi Wai Yu and Opal Densmore and Jeff Goeders and Andrew Somerville and Kenneth B. Kent and Peter Jamieson and Jason Anderson},
+ booktitle={Proceedings of the 20th ACM/SIGDA International Symposium on Field-Programmable Gate Arrays},
+ pages={77--86},
+ year={2012},
+ organization={ACM}
+}
+
+@MISC{LogicSynthesis,
+ author = {G D Hachtel and F Somenzi},
+ title = {Logic Synthesis and Verification Algorithms},
+ year = {1996}
+}
+
+@ARTICLE{Verilog2005,
+ journal={IEEE Std 1364-2005 (Revision of IEEE Std 1364-2001)},
+ title={IEEE Standard for Verilog Hardware Description Language},
+ year={2006},
+ doi={10.1109/IEEESTD.2006.99495}
+}
+
+@ARTICLE{VerilogSynth,
+ journal={IEEE Std 1364.1-2002},
+ title={IEEE Standard for Verilog Register Transfer Level Synthesis},
+ year={2002},
+ doi={10.1109/IEEESTD.2002.94220}
+}
+
+@ARTICLE{VHDL,
+ journal={IEEE Std 1076-2008 (Revision of IEEE Std 1076-2002)}, title={IEEE Standard VHDL Language Reference Manual},
+ year={2009},
+ month={26},
+ doi={10.1109/IEEESTD.2009.4772740}
+}
+
+@ARTICLE{VHDLSynth,
+ journal={IEEE Std 1076.6-2004 (Revision of IEEE Std 1076.6-1999)}, title={IEEE Standard for VHDL Register Transfer Level (RTL) Synthesis},
+ year={2004},
+ doi={10.1109/IEEESTD.2004.94802}
+}
+
+@ARTICLE{IP-XACT,
+journal={IEEE Std 1685-2009}, title={IEEE Standard for IP-XACT, Standard Structure for Packaging, Integrating, and Reusing IP within Tools Flows},
+year={2010},
+pages={C1-360},
+keywords={abstraction definitions, address space specification, bus definitions, design environment, EDA, electronic design automation, electronic system level, ESL, implementation constraints, IP-XACT, register transfer level, RTL, SCRs, semantic consistency rules, TGI, tight generator interface, tool and data interoperability, use models, XML design meta-data, XML schema},
+doi={10.1109/IEEESTD.2010.5417309},}
+
+@book{Dragonbook,
+author = {Aho, Alfred V. and Sethi, Ravi and Ullman, Jeffrey D.},
+title = {Compilers: principles, techniques, and tools},
+year = {1986},
+isbn = {0-201-10088-6},
+publisher = {Addison-Wesley Longman Publishing Co., Inc.},
+address = {Boston, MA, USA},
+}
+
+@INPROCEEDINGS{Cummings00,
+author = {Clifford E. Cummings and Sunburst Design Inc},
+title = {Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill},
+booktitle = {SNUG (Synopsys Users Group) 2000 User Papers, section-MC1 (1 st paper},
+year = {2000}
+}
+
+@ARTICLE{MURPHY,
+ author={D. L. Klipstein},
+ journal={Cahners Publishing Co., EEE Magazine, Vol. 15, No. 8},
+ title={The Contributions of Edsel Murphy to the Understanding of the Behavior of Inanimate Objects},
+ year={August 1967}
+}
+
+@INPROCEEDINGS{fsmextract,
+author={Yiqiong Shi and Chan Wai Ting and Bah-Hwee Gwee and Ye Ren},
+booktitle={Circuits and Systems (ISCAS), Proceedings of 2010 IEEE International Symposium on},
+title={A highly efficient method for extracting FSMs from flattened gate-level netlist},
+year={2010},
+pages={2610-2613},
+keywords={circuit CAD;finite state machines;microcontrollers;FSM;control-intensive circuits;finite state machines;flattened gate-level netlist;state register elimination technique;Automata;Circuit synthesis;Continuous wavelet transforms;Design automation;Digital circuits;Hardware design languages;Logic;Microcontrollers;Registers;Signal processing},
+doi={10.1109/ISCAS.2010.5537093},}
+
+@ARTICLE{MultiLevelLogicSynth,
+author={Brayton, R.K. and Hachtel, G.D. and Sangiovanni-Vincentelli, A.L.},
+journal={Proceedings of the IEEE},
+title={Multilevel logic synthesis},
+year={1990},
+volume={78},
+number={2},
+pages={264-300},
+keywords={circuit layout CAD;integrated logic circuits;logic CAD;capsule summaries;definitions;detailed analysis;in-depth background;logic decomposition;logic minimisation;logic synthesis;logic synthesis techniques;multilevel combinational logic;multilevel logic synthesis;notation;perspective;survey;synthesis methods;technology mapping;testing;Application specific integrated circuits;Design automation;Integrated circuit synthesis;Logic design;Logic devices;Logic testing;Network synthesis;Programmable logic arrays;Signal synthesis;Silicon},
+doi={10.1109/5.52213},
+ISSN={0018-9219},}
+
+@article{UllmannSubgraphIsomorphism,
+ author = {Ullmann, J. R.},
+ title = {An Algorithm for Subgraph Isomorphism},
+ journal = {J. ACM},
+ issue_date = {Jan. 1976},
+ volume = {23},
+ number = {1},
+ month = jan,
+ year = {1976},
+ issn = {0004-5411},
+ pages = {31--42},
+ numpages = {12},
+ doi = {10.1145/321921.321925},
+ acmid = {321925},
+ publisher = {ACM},
+ address = {New York, NY, USA},
+}
diff --git a/manual/manual.sh b/manual/manual.sh
new file mode 100755
index 00000000..c4673938
--- /dev/null
+++ b/manual/manual.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+fast_mode=false
+update_mode=false
+
+set -- $(getopt fu "$@")
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -f)
+ fast_mode=true
+ ;;
+ -u)
+ update_mode=true
+ ;;
+ --)
+ shift
+ break
+ ;;
+ -*)
+ echo "$0: error - unrecognized option $1" 1>&2
+ exit 1
+ ;;
+ *)
+ break
+ esac
+ shift
+done
+
+PDFTEX_OPT="-shell-escape -halt-on-error"
+
+if $update_mode; then
+ make -C ..
+ ../yosys -p 'help -write-tex-command-reference-manual'
+fi
+
+if ! $fast_mode; then
+ md5sum *.aux *.bbl *.blg > autoloop.old
+fi
+
+set -ex
+
+pdflatex $PDFTEX_OPT manual.tex
+
+if ! $fast_mode; then
+ bibtex manual.aux
+ bibtex weblink.aux
+
+ while
+ md5sum *.aux *.bbl *.blg > autoloop.new
+ ! cmp autoloop.old autoloop.new
+ do
+ cp autoloop.new autoloop.old
+ pdflatex $PDFTEX_OPT manual.tex
+ done
+
+ rm -f autoloop.old
+ rm -f autoloop.new
+fi
+
diff --git a/manual/manual.tex b/manual/manual.tex
new file mode 100644
index 00000000..67982cbc
--- /dev/null
+++ b/manual/manual.tex
@@ -0,0 +1,226 @@
+\documentclass[oneside,a4paper]{book}
+
+\usepackage[T1]{fontenc} % required for luximono!
+\usepackage{lmodern}
+\usepackage[scaled=0.8]{luximono} % typewriter font with bold face
+
+% To install the luximono font files:
+% getnonfreefonts-sys --all or
+% getnonfreefonts-sys luximono
+%
+% when there are trouble you might need to:
+% - Create /etc/texmf/updmap.d/99local-luximono.cfg
+% containing the single line: Map ul9.map
+% - Run update-updmap followed by mktexlsr and updmap-sys
+%
+% This commands must be executed as root with a root environment
+% (i.e. run "sudo su" and then execute the commands in the root
+% shell, don't just prefix the commands with "sudo").
+
+% formats the text according the set language
+\usepackage[english]{babel}
+\usepackage[table,usenames]{xcolor}
+% generates indices with the "\index" command
+\usepackage{makeidx}
+% enables import of graphics. We use pdflatex here so do the pdf optimisation.
+%\usepackage[dvips]{graphicx}
+\usepackage[pdftex]{graphicx}
+\usepackage{pdfpages}
+% includes floating objects like tables and figures.
+\usepackage{float}
+% for generating subfigures with ohne indented captions
+\usepackage[hang]{subfigure}
+% redefines and smartens captions of figures and tables (indentation, smaller and boldface)
+\usepackage[hang,small,bf,center]{caption}
+% enables tabstops and the numeration of lines
+\usepackage{moreverb}
+% enables user defined header and footer lines (former "fancyheadings")
+\usepackage{fancyhdr}
+% Some smart mathematical stuff
+\usepackage{amsmath}
+% Package for rotating several objects
+\usepackage{rotating}
+\usepackage{natbib}
+\usepackage{epsf}
+\usepackage{dsfont}
+\usepackage[algochapter, boxruled, vlined]{algorithm2e}
+%Activating and setting of character protruding - if you like
+%\usepackage[activate,DVIoutput]{pdfcprot}
+% If you really need special chars...
+\usepackage[latin1]{inputenc}
+% Hyperlinks
+\usepackage[colorlinks,hyperindex,plainpages=false,%
+pdftitle={Yosys Manual},%
+pdfauthor={Clifford Wolf},%
+%pdfkeywords={keyword},%
+pdfpagelabels,%
+pagebackref,%
+bookmarksopen=false%
+]{hyperref}
+% For the two different reference lists ...
+\usepackage{multibib}
+\usepackage{multirow}
+\usepackage{booktabs}
+\usepackage{pdfpages}
+
+\usepackage{listings}
+\usepackage{pifont}
+\usepackage{skull}
+% \usepackage{draftwatermark}
+
+\usepackage{tikz}
+\usetikzlibrary{calc}
+\usetikzlibrary{arrows}
+\usetikzlibrary{scopes}
+\usetikzlibrary{through}
+\usetikzlibrary{shapes.geometric}
+
+\lstset{basicstyle=\ttfamily}
+
+\def\B#1{{\tt\textbackslash{}#1}}
+\def\C#1{\lstinline[language=C++]{#1}}
+\def\V#1{\lstinline[language=Verilog]{#1}}
+
+\newsavebox{\fixmebox}
+\newenvironment{fixme}%
+{\newcommand\colboxcolor{FFBBBB}%
+\begin{lrbox}{\fixmebox}%
+\begin{minipage}{\dimexpr\columnwidth-2\fboxsep\relax}}
+{\end{minipage}\end{lrbox}\textbf{FIXME: }\\%
+\colorbox[HTML]{\colboxcolor}{\usebox{\fixmebox}}}
+
+\newcites{weblink}{Internet References}
+
+\setcounter{secnumdepth}{3}
+\makeindex
+
+\setlength{\oddsidemargin}{4mm}
+\setlength{\evensidemargin}{-6mm}
+\setlength{\textwidth}{162mm}
+\setlength{\textheight}{230mm}
+\setlength{\topmargin}{-5mm}
+
+\setlength{\parskip}{1.5ex plus 1ex minus 0.5ex}
+\setlength{\parindent}{0pt}
+
+\lstdefinelanguage{liberty}{
+ morecomment=[s]{/*}{*/},
+ morekeywords={library,cell,area,pin,direction,function,clocked_on,next_state,clock,ff},
+ morestring=[b]",
+}
+
+\lstdefinelanguage{rtlil}{
+ morecomment=[l]{\#},
+ morekeywords={module,attribute,parameter,wire,memory,auto,width,offset,size,input,output,inout,cell,connect,switch,case,assign,sync,low,high,posedge,negedge,edge,always,update,process,end},
+ morestring=[b]",
+}
+
+
+
+\begin{document}
+
+\fancypagestyle{mypagestyle}{%
+\fancyhf{}%
+\fancyhead[C]{\leftmark}%
+\fancyfoot[C]{\thepage}%
+\renewcommand{\headrulewidth}{0pt}%
+\renewcommand{\footrulewidth}{0pt}}
+\pagestyle{mypagestyle}
+
+\thispagestyle{empty}
+\null\vfil
+
+\begin{center}
+\bf\Huge Yosys Manual
+
+\bigskip
+\large Clifford Wolf
+\end{center}
+
+\vfil\null
+\eject
+
+\chapter*{Abstract}
+Most of today's digital design is done in HDL code (mostly Verilog or VHDL) and
+with the help of HDL synthesis tools.
+
+In special cases such as synthesis for coarse-grain cell libraries or when
+testing new synthesis algorithms it might be necessary to write a custom HDL
+synthesis tool or add new features to an existing one. It this cases the
+availability of a Free and Open Source (FOSS) synthesis tool that can be used
+as basis for custom tools would be helpful.
+
+In the absence of such a tool, the Yosys Open SYnthesis Suite (Yosys) was
+developed. This document covers the design and implementation of this tool.
+At the moment the main focus of Yosys lies on the high-level aspects of
+digital synthesis. The pre-existing FOSS logic-synthesis tool ABC is used
+by Yosys to perform advanced gate-level optimizations.
+
+An evaluation of Yosys based on real-world designs is included. It is shown
+that Yosys can be used as-is to synthesize such designs. The results produced
+by Yosys in this tests where successfully verified using formal verification
+and are comparable in quality to the results produced by a commercial
+synthesis tool.
+
+\bigskip
+
+This document was originally published as bachelor thesis at the Vienna
+University of Technology \cite{BACC}.
+
+\chapter*{Abbreviations}
+\begin{tabular}{ll}
+AIG & And-Inverter-Graph \\
+ASIC & Application-Specific Integrated Circuit \\
+AST & Abstract Syntax Tree \\
+BDD & Binary Decision Diagram \\
+BLIF & Berkeley Logic Interchange Format \\
+EDA & Electronic Design Automation \\
+EDIF & Electronic Design Interchange Format \\
+ER Diagram & Entity-Relationship Diagram \\
+FOSS & Free and Open-Source Software \\
+FPGA & Field-Programmable Gate Array \\
+FSM & Finite-state machine \\
+HDL & Hardware Description Language \\
+LPM & Library of Parameterized Modules \\
+RTLIL & RTL Intermediate Language \\
+RTL & Register Transfer Level \\
+SAT & Satisfiability Problem \\
+% SSA & Static Single Assignment Form \\
+VHDL & VHSIC Hardware Description Language \\
+VHSIC & Very-High-Speed Integrated Circuit \\
+YOSYS & Yosys Open SYnthesis Suite \\
+\end{tabular}
+
+\tableofcontents
+
+\include{CHAPTER_Intro}
+\include{CHAPTER_Basics}
+\include{CHAPTER_Approach}
+\include{CHAPTER_Overview}
+\include{CHAPTER_CellLib}
+\include{CHAPTER_Prog}
+
+\include{CHAPTER_Verilog}
+\include{CHAPTER_Optimize}
+\include{CHAPTER_Techmap}
+% \include{CHAPTER_Eval}
+
+\appendix
+
+\include{CHAPTER_Auxlibs}
+\include{CHAPTER_Auxprogs}
+
+\chapter{Command Reference Manual}
+\label{commandref}
+\input{command-reference-manual}
+
+\include{CHAPTER_Appnotes}
+% \include{CHAPTER_StateOfTheArt}
+
+\bibliography{literature}
+\bibliographystyle{alphadin}
+
+\bibliographyweblink{weblinks}
+\bibliographystyleweblink{abbrv}
+
+\end{document}
diff --git a/manual/presentation.sh b/manual/presentation.sh
new file mode 100755
index 00000000..ca8a6c93
--- /dev/null
+++ b/manual/presentation.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+fast_mode=false
+
+set -- $(getopt fu "$@")
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -f)
+ fast_mode=true
+ ;;
+ --)
+ shift
+ break
+ ;;
+ -*)
+ echo "$0: error - unrecognized option $1" 1>&2
+ exit 1
+ ;;
+ *)
+ break
+ esac
+ shift
+done
+
+PDFTEX_OPT="-shell-escape -halt-on-error"
+
+set -ex
+
+if ! $fast_mode; then
+ ! md5sum *.aux *.snm *.nav *.toc > autoloop.old
+ make -C PRESENTATION_Intro
+ make -C PRESENTATION_ExSyn
+ make -C PRESENTATION_ExAdv
+ make -C PRESENTATION_ExOth
+ make -C PRESENTATION_Prog
+fi
+
+set -ex
+
+pdflatex $PDFTEX_OPT presentation.tex
+
+if ! $fast_mode; then
+ while
+ md5sum *.aux *.snm *.nav *.toc > autoloop.new
+ ! cmp autoloop.old autoloop.new
+ do
+ cp autoloop.new autoloop.old
+ pdflatex $PDFTEX_OPT presentation.tex
+ done
+
+ rm -f autoloop.old
+ rm -f autoloop.new
+fi
+
diff --git a/manual/presentation.tex b/manual/presentation.tex
new file mode 100644
index 00000000..63b963bb
--- /dev/null
+++ b/manual/presentation.tex
@@ -0,0 +1,162 @@
+\documentclass{beamer}
+\hypersetup{bookmarksdepth=5}
+
+\usepackage[T1]{fontenc} % required for luximono!
+\usepackage{lmodern}
+\usepackage[scaled=0.8]{luximono} % typewriter font with bold face
+
+% To install the luximono font files:
+% getnonfreefonts-sys --all or
+% getnonfreefonts-sys luximono
+%
+% when there are trouble you might need to:
+% - Create /etc/texmf/updmap.d/99local-luximono.cfg
+% containing the single line: Map ul9.map
+% - Run update-updmap followed by mktexlsr and updmap-sys
+%
+% This commands must be executed as root with a root environment
+% (i.e. run "sudo su" and then execute the commands in the root
+% shell, don't just prefix the commands with "sudo").
+
+% formats the text according the set language
+\usepackage[english]{babel}
+\usepackage{amsmath}
+\usepackage{multirow}
+\usepackage{booktabs}
+\usepackage{listings}
+\usepackage{setspace}
+\usepackage{skull}
+\usepackage{units}
+
+\usepackage{tikz}
+\usetikzlibrary{calc}
+\usetikzlibrary{arrows}
+\usetikzlibrary{scopes}
+\usetikzlibrary{through}
+\usetikzlibrary{shapes.geometric}
+
+\lstset{basicstyle=\ttfamily}
+
+\def\B#1{{\tt\textbackslash{}#1}}
+\def\C#1{\lstinline[language=C++]{#1}}
+\def\V#1{\lstinline[language=Verilog]{#1}}
+
+\lstdefinelanguage{liberty}{
+ morecomment=[s]{/*}{*/},
+ morekeywords={library,cell,area,pin,direction,function,clocked_on,next_state,clock,ff},
+ morestring=[b]",
+}
+
+\lstdefinelanguage{rtlil}{
+ morecomment=[l]{\#},
+ morekeywords={module,attribute,parameter,wire,memory,auto,width,offset,size,input,output,inout,cell,connect,switch,case,assign,sync,low,high,posedge,negedge,edge,always,update,process,end},
+ morestring=[b]",
+}
+
+\lstdefinelanguage{ys}{
+ morecomment=[l]{\#},
+}
+
+\lstset{
+ commentstyle=\color{YosysGreen},
+}
+
+\newenvironment{boxalertenv}{\begin{altenv}%
+{\usebeamertemplate{alerted text begin}\usebeamercolor[fg]{alerted text}\usebeamerfont{alerted text}\setlength{\fboxsep}{1pt}\colorbox{bg}}
+{\usebeamertemplate{alerted text end}}{\color{.}}{}}{\end{altenv}}
+
+\newcommand<>{\boxalert}[1]{{%
+\begin{boxalertenv}#2{#1}\end{boxalertenv}%
+}}
+
+\newcommand{\subsectionpagesuffix}{
+\vfill\begin{centering}
+{\usebeamerfont{subsection name}\usebeamercolor[fg]{subsection name}of \sectionname~\insertsectionnumber}
+\vskip1em\par
+\setbeamercolor{graybox}{bg=gray}
+\begin{beamercolorbox}[sep=8pt,center]{graybox}
+\usebeamerfont{subsection title}\insertsection\par
+\end{beamercolorbox}
+\end{centering}}
+
+\title{Yosys Open SYnthesis Suite}
+\author{Clifford Wolf}
+\institute{http://www.clifford.at/yosys/}
+
+\usetheme{Madrid}
+\usecolortheme{seagull}
+\beamertemplatenavigationsymbolsempty
+
+\definecolor{YosysGreen}{RGB}{85,136,102}
+\definecolor{MyBlue}{RGB}{85,130,180}
+
+\setbeamercolor{title}{fg=black,bg=YosysGreen!70}
+\setbeamercolor{titlelike}{fg=black,bg=YosysGreen!70}
+\setbeamercolor{frametitle}{fg=black,bg=YosysGreen!70}
+\setbeamercolor{block title}{fg=black,bg=YosysGreen!70}
+\setbeamercolor{item projected}{fg=black,bg=YosysGreen}
+
+\begin{document}
+
+\begin{frame}
+\titlepage
+\end{frame}
+
+\setcounter{section}{-3}
+
+\section{Abstract}
+\begin{frame}{Abstract}
+Yosys is the first full-featured open source software for Verilog HDL
+synthesis. It supports most of Verilog-2005 and is well tested with
+real-world designs from the ASIC and FPGA world.
+
+\bigskip
+Learn how to use Yosys to create your own custom synthesis flows and
+discover why open source HDL synthesis is important for researchers,
+hobbyists, educators and engineers alike.
+
+\bigskip
+This presentation covers basic concepts of Yosys, writing synthesis scripts
+for a wide range of applications, creating Yosys scripts for various
+non-synthesis applications (such as formal equivalence checking) and
+writing extensions to Yosys using the C++ API.
+\end{frame}
+
+\section{About me}
+\begin{frame}{About me}
+Hi! I'm Clifford Wolf.
+
+\bigskip
+I like writing open source software. For example:
+\begin{itemize}
+\item Yosys
+\item OpenSCAD (now maintained by Marius Kintel)
+\item SPL (a not very popular scripting language)
+\item EmbedVM (a very simple compiler+vm for 8 bit micros)
+\item Lib(X)SVF (a library to play SVF/XSVF files over JTAG)
+\item ROCK Linux (discontinued since 2010)
+\end{itemize}
+\end{frame}
+
+\section{Outline}
+\begin{frame}{Outline}
+Yosys is an Open Source Verilog synthesis tool, and more.
+
+\bigskip
+Outline of this presentation:
+\begin{itemize}
+\item Introduction to the field and Yosys
+\item Yosys by example: synthesis
+\item Yosys by example: advanced synthesis
+\item Yosys by example: beyond synthesis
+\item Writing Yosys extensions in C++
+\end{itemize}
+\end{frame}
+
+\include{PRESENTATION_Intro}
+\include{PRESENTATION_ExSyn}
+\include{PRESENTATION_ExAdv}
+\include{PRESENTATION_ExOth}
+\include{PRESENTATION_Prog}
+
+\end{document}
diff --git a/manual/weblinks.bib b/manual/weblinks.bib
new file mode 100644
index 00000000..d5f83315
--- /dev/null
+++ b/manual/weblinks.bib
@@ -0,0 +1,134 @@
+
+@misc{YosysGit,
+ author = {Clifford Wolf},
+ title = {{Yosys Open SYnthesis Suite (YOSYS)}},
+ note = {\url{http://github.com/cliffordwolf/yosys}}
+}
+
+@misc{YosysTestsGit,
+ author = {Clifford Wolf},
+ title = {{Yosys Test Bench}},
+ note = {\url{http://github.com/cliffordwolf/yosys-tests}}
+}
+
+@misc{VlogHammer,
+ author = {Clifford Wolf},
+ title = {{VlogHammer Verilog Synthesis Regression Tests}},
+ note = {\url{http://github.com/cliffordwolf/VlogHammer}}
+}
+
+@misc{Icarus,
+ author = {Stephen Williams},
+ title = {{Icarus Verilog}},
+ note = {Version 0.8.7, \url{http://iverilog.icarus.com/}}
+}
+
+@misc{VTR,
+ author= {Jonathan Rose and Jason Luu and Chi Wai Yu and Opal Densmore and Jeff Goeders and Andrew Somerville and Kenneth B. Kent and Peter Jamieson and Jason Anderson},
+ title = {{The Verilog-to-Routing (VTR) Project for FPGAs}},
+ note = {Version 1.0, \url{https://code.google.com/p/vtr-verilog-to-routing/}}
+}
+
+@misc{HANA,
+ author = {Parvez Ahmad},
+ title = {{HDL Analyzer and Netlist Architect (HANA)}},
+ note = {Verison linux64-1.0-alpha (2012-10-14), \url{http://sourceforge.net/projects/sim-sim/}}
+}
+
+@misc{MVSIS,
+ author = {MVSIS group at Berkeley studies logic synthesis and verification for VLSI design},
+ title = {{MVSIS: Logic Synthesis and Verification}},
+ note = {Version 3.0, \url{http://embedded.eecs.berkeley.edu/mvsis/}}
+}
+
+@misc{VIS,
+ author = {{The VIS group}},
+ title = {{VIS: A system for Verification and Synthesis}},
+ note = {Version 2.4, \url{http://vlsi.colorado.edu/~vis/}}
+}
+
+@misc{ABC,
+ author = {{Berkeley Logic Synthesis and Verification Group}},
+ title = {{ABC: A System for Sequential Synthesis and Verification}},
+ note = {HQ Rev b5750272659f, 2012-10-28, \url{http://www.eecs.berkeley.edu/~alanmi/abc/}}
+}
+
+@misc{AIGER,
+ author = {{Armin Biere, Johannes Kepler University Linz, Austria}},
+ title = {{AIGER}},
+ note = {\url{http://fmv.jku.at/aiger/}}
+}
+
+@misc{XilinxWebPACK,
+ author = {{Xilinx, Inc.}},
+ title = {{ISE WebPACK Design Software}},
+ note = {\url{http://www.xilinx.com/products/design-tools/ise-design-suite/ise-webpack.htm}}
+}
+
+@misc{QuartusWeb,
+ author = {{Altera, Inc.}},
+ title = {{Quartus II Web Edition Software}},
+ note = {\url{http://www.altera.com/products/software/quartus-ii/web-edition/qts-we-index.html}}
+}
+
+@misc{OR1200,
+ title = {{OpenRISC 1200 CPU}},
+ note = {\url{http://opencores.org/or1k/OR1200\_OpenRISC\_Processor}}
+}
+
+@misc{openMSP430,
+ title = {{openMSP430 CPU}},
+ note = {\url{http://opencores.org/project,openmsp430}}
+}
+
+@misc{i2cmaster,
+ title = {{OpenCores I$^2$C Core}},
+ note = {\url{http://opencores.org/project,i2c}}
+}
+
+@misc{k68,
+ title = {{OpenCores k68 Core}},
+ note = {\url{http://opencores.org/project,k68}}
+}
+
+@misc{bison,
+ title = {{GNU Bison}},
+ note = {\url{http://www.gnu.org/software/bison/}}
+}
+
+@misc{flex,
+ title = {{Flex}},
+ note = {\url{http://flex.sourceforge.net/}}
+}
+
+@misc{C_to_Verilog,
+ title = {{C-to-Verilog}},
+ note = {\url{http://www.c-to-verilog.com/}}
+}
+
+@misc{LegUp,
+ title = {{LegUp}},
+ note = {\url{http://legup.eecg.utoronto.ca/}}
+}
+
+@misc{LibertyFormat,
+ title = {{The Liberty Library Modeling Standard}},
+ note = {\url{http://www.opensourceliberty.org/}}
+}
+
+@misc{ASIC-WORLD,
+ title = {{World of ASIC}},
+ note = {\url{http://www.asic-world.com/}}
+}
+
+@misc{Formality,
+ title = {{Synopsys Formality Equivalence Checking}},
+ note = {\url{http://www.synopsys.com/Tools/Verification/FormalEquivalence/Pages/Formality.aspx}},
+}
+
+@misc{bigint,
+ author = {Matt McCutchen},
+ title = {{C++ Big Integer Library}},
+ note = {\url{http://mattmccutchen.net/bigint/}}
+}
+
diff --git a/misc/create_vcxsrc.sh b/misc/create_vcxsrc.sh
new file mode 100644
index 00000000..215e27c5
--- /dev/null
+++ b/misc/create_vcxsrc.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+set -ex
+vcxsrc="$1-$2"
+yosysver="$2"
+gitsha="$3"
+
+rm -rf YosysVS-Tpl-v1.zip YosysVS
+wget http://www.clifford.at/yosys/nogit/YosysVS-Tpl-v1.zip
+
+unzip YosysVS-Tpl-v1.zip
+rm -f YosysVS-Tpl-v1.zip
+mv YosysVS "$vcxsrc"
+
+{
+ n=$(grep -B999 '<ItemGroup>' "$vcxsrc"/YosysVS/YosysVS.vcxproj | wc -l)
+ head -n$n "$vcxsrc"/YosysVS/YosysVS.vcxproj
+ egrep '\.(h|hh|hpp|inc)$' srcfiles.txt | sed 's,.*,<ClInclude Include="../yosys/&" />,'
+ egrep -v '\.(h|hh|hpp|inc)$' srcfiles.txt | sed 's,.*,<ClCompile Include="../yosys/&" />,'
+ echo '<ClCompile Include="../yosys/kernel/version.cc" />'
+ tail -n +$((n+1)) "$vcxsrc"/YosysVS/YosysVS.vcxproj
+} > "$vcxsrc"/YosysVS/YosysVS.vcxproj.new
+
+mv "$vcxsrc"/YosysVS/YosysVS.vcxproj.new "$vcxsrc"/YosysVS/YosysVS.vcxproj
+
+mkdir -p "$vcxsrc"/yosys
+tar -cf - -T srcfiles.txt | tar -xf - -C "$vcxsrc"/yosys
+cp -r share "$vcxsrc"/
+
+echo "namespace Yosys { extern const char *yosys_version_str; const char *yosys_version_str=\"Yosys" \
+ "$yosysver (git sha1 $gitsha, Visual Studio)\"; }" > "$vcxsrc"/yosys/kernel/version.cc
+
+cat > "$vcxsrc"/readme-git.txt << EOT
+Want to use a git working copy for the yosys source code?
+Open "Git Bash" in this directory and run:
+
+ mv yosys yosys.bak
+ git clone https://github.com/cliffordwolf/yosys.git yosys
+ cd yosys
+ git checkout -B master $(git rev-parse HEAD | cut -c1-10)
+ unzip ../genfiles.zip
+EOT
+
+cat > "$vcxsrc"/readme-abc.txt << EOT
+Yosys is using "ABC" for gate-level optimizations and technology
+mapping. Download yosys-win32-mxebin-$yosysver.zip and copy the
+following files from it into this directory:
+
+ pthreadVC2.dll
+ yosys-abc.exe
+EOT
+
+sed -i 's/$/\r/; s/\r\r*/\r/g;' "$vcxsrc"/YosysVS/YosysVS.vcxproj "$vcxsrc"/readme-git.txt "$vcxsrc"/readme-abc.txt
+
diff --git a/misc/yosys-config.in b/misc/yosys-config.in
new file mode 100644
index 00000000..1473948c
--- /dev/null
+++ b/misc/yosys-config.in
@@ -0,0 +1,107 @@
+#!/bin/bash
+
+help() {
+ {
+ echo ""
+ echo "Usage: $0 [--exec] [--prefix pf] args.."
+ echo " $0 --build modname.so cppsources.."
+ echo ""
+ echo "Replecement args:"
+ echo " --cxx @CXX@"
+ echo " --cxxflags $( echo '@CXXFLAGS@' | fmt -w60 | sed ':a;N;$!ba;s/\n/ \\\n /g' )"
+ echo " --ldflags @LDFLAGS@"
+ echo " --ldlibs @LDLIBS@"
+ echo " --bindir @BINDIR@"
+ echo " --datdir @DATDIR@"
+ echo ""
+ echo "All other args are passed through as they are."
+ echo ""
+ echo "Use --exec to call a command instead of generating output. Example usage:"
+ echo ""
+ echo " yosys-config --exec --cxx --cxxflags --ldflags -o plugin.so -shared plugin.cc --ldlibs"
+ echo ""
+ echo "The above command can be abbreviated as:"
+ echo ""
+ echo " yosys-config --build plugin.so plugin.cc"
+ echo ""
+ echo "Use --prefix to change the prefix for the special args from '--' to"
+ echo "something else. Example:"
+ echo ""
+ echo " yosys-config --prefix @ bindir: @bindir"
+ echo ""
+ echo "The args --bindir and --datdir can be directly followed by a slash and"
+ echo "additional text. Example:"
+ echo ""
+ echo " yosys-config --datdir/simlib.v"
+ echo ""
+ } >&2
+ exit 1
+}
+
+if [ $# -eq 0 ]; then
+ help
+fi
+
+if [ "$1" == "--build" ]; then
+ modname="$2"; shift 2
+ set -- --exec --cxx --cxxflags --ldflags -o "$modname" -shared "$@" --ldlibs
+fi
+
+prefix="--"
+get_prefix=false
+exec_mode=false
+declare -a tokens=()
+
+for opt; do
+ if $get_prefix; then
+ prefix="$opt"
+ get_prefix=false
+ continue
+ fi
+ case "$opt" in
+ "$prefix"cxx)
+ tokens=( "${tokens[@]}" @CXX@ ) ;;
+ "$prefix"cxxflags)
+ tokens=( "${tokens[@]}" @CXXFLAGS@ ) ;;
+ "$prefix"ldflags)
+ tokens=( "${tokens[@]}" @LDFLAGS@ ) ;;
+ "$prefix"ldlibs)
+ tokens=( "${tokens[@]}" @LDLIBS@ ) ;;
+ "$prefix"bindir)
+ tokens=( "${tokens[@]}" '@BINDIR@' ) ;;
+ "$prefix"datdir)
+ tokens=( "${tokens[@]}" '@DATDIR@' ) ;;
+ "$prefix"bindir/*)
+ tokens=( "${tokens[@]}" '@BINDIR@'"${opt#${prefix}bindir}" ) ;;
+ "$prefix"datdir/*)
+ tokens=( "${tokens[@]}" '@DATDIR@'"${opt#${prefix}datdir}" ) ;;
+ --help|-\?|-h)
+ if [ ${#tokens[@]} -eq 0 ]; then
+ help
+ else
+ tokens=( "${tokens[@]}" "$opt" )
+ fi ;;
+ --exec)
+ if [ ${#tokens[@]} -eq 0 ]; then
+ exec_mode=true
+ else
+ tokens=( "${tokens[@]}" "$opt" )
+ fi ;;
+ --prefix)
+ if [ ${#tokens[@]} -eq 0 ]; then
+ get_prefix=true
+ else
+ tokens=( "${tokens[@]}" "$opt" )
+ fi ;;
+ *)
+ tokens=( "${tokens[@]}" "$opt" )
+ esac
+done
+
+if $exec_mode; then
+ exec "${tokens[@]}"
+fi
+
+echo "${tokens[@]}"
+exit 0
+
diff --git a/misc/yosysjs/demo01.html b/misc/yosysjs/demo01.html
new file mode 100644
index 00000000..3f9f737e
--- /dev/null
+++ b/misc/yosysjs/demo01.html
@@ -0,0 +1,197 @@
+<html><head>
+ <title>YosysJS Example Application #01</title>
+ <script type="text/javascript" src="yosysjs.js"></script>
+</head><body onload="document.getElementById('command').focus()">
+ <h1>YosysJS Example Application #01</h1>
+ <table width="100%"><tr><td><div id="tabs"></div></td><td align="right"><tt>[ <span onclick="load_example()">load example</span> ]</tt></td></tr></table>
+ <svg id="svg" style="display: none; position: absolute; padding: 10px; width: calc(100% - 40px); height: 480px;"></svg>
+ <div><textarea id="output" style="width: 100%; height: 500px"></textarea></div>
+ <div id="wait" style="display: block"><br/><b><span id="waitmsg">Loading...</span></b></div>
+ <div id="input" style="display: none"><form onsubmit="window.setTimeout(run_command); return false"><br/><tt><span id="prompt">
+ </span></tt><input id="command" type="text" onkeydown="history(event)" style="font-family: monospace; font-weight: bold;" size="100"></form></div>
+ <script type='text/javascript'>
+ function print_output(text) {
+ var el = document.getElementById('output');
+ el.value += text + "\n";
+ }
+
+ YosysJS.load_viz();
+ var ys = YosysJS.create("", function() {
+ print_output(ys.print_buffer);
+
+ document.getElementById('wait').style.display = 'none';
+ document.getElementById('input').style.display = 'block';
+ document.getElementById('waitmsg').textContent = 'Waiting for yosys.js...';
+ document.getElementById('prompt').textContent = ys.prompt();
+
+ update_tabs();
+ });
+
+ ys.echo = true;
+
+ var history_log = [];
+ var history_index = 0;
+ var history_bak = "";
+
+ function history(ev) {
+ if (ev.keyCode == 38) {
+ el = document.getElementById('command');
+ if (history_index == history_log.length)
+ history_bak = el.value
+ if (history_index > 0)
+ el.value = history_log[--history_index];
+ }
+ if (ev.keyCode == 40) {
+ if (history_index < history_log.length) {
+ el = document.getElementById('command');
+ if (++history_index < history_log.length)
+ el.value = history_log[history_index];
+ else
+ el.value = history_bak;
+ }
+ }
+ }
+
+ var current_file = "";
+ var console_messages = "";
+ var svg_cache = { };
+
+ function update_tabs() {
+ var f, html = "", flist = ys.read_dir('.');
+ if (current_file == "") {
+ html += '<tt>[ <b>Console</b>';
+ } else {
+ html += '<tt>[ <span onclick="open_file(\'\')">Console</span>';
+ }
+ for (i in flist) {
+ f = flist[i]
+ if (f == "." || f == "..")
+ continue;
+ if (current_file == f) {
+ html += ' | <b>' + f + '</b>';
+ } else {
+ html += ' | <span onclick="open_file(\'' + f + '\')">' + f + '</span>';
+ }
+ }
+ html += ' | <span onclick="open_file(prompt(\'Filename:\'))">new file</span> ]</tt>';
+ document.getElementById('tabs').innerHTML = html;
+ if (current_file == "" || /\.dot$/.test(current_file)) {
+ var element = document.getElementById('output');
+ element.readOnly = true;
+ element.scrollTop = element.scrollHeight; // focus on bottom
+ document.getElementById('command').focus();
+ } else {
+ document.getElementById('output').readOnly = false;
+ document.getElementById('output').focus();
+ }
+ }
+
+ function open_file(filename)
+ {
+ if (current_file == "")
+ console_messages = document.getElementById('output').value;
+ else if (!/\.dot$/.test(current_file))
+ ys.write_file(current_file, document.getElementById('output').value);
+
+ if (filename == "") {
+ document.getElementById('output').value = console_messages;
+ } else {
+ try {
+ document.getElementById('output').value = ys.read_file(filename);
+ } catch (e) {
+ document.getElementById('output').value = "";
+ ys.write_file(filename, document.getElementById('output').value);
+ }
+ }
+
+ if (/\.dot$/.test(filename)) {
+ dot = document.getElementById('output').value;
+ YosysJS.dot_into_svg(dot, 'svg');
+ document.getElementById('svg').style.display = 'block';
+ document.getElementById('output').value = '';
+ } else {
+ document.getElementById('svg').innerHTML = '';
+ document.getElementById('svg').style.display = 'none';
+ }
+
+ current_file = filename;
+ update_tabs()
+ }
+
+ function startup() {
+ }
+
+ function load_example() {
+ open_file('')
+
+ var txt = "";
+ txt += "// a simple yosys.js example. run \"script example.ys\".\n";
+ txt += "\n";
+ txt += "module example(input clk, input rst, input inc, output reg [3:0] cnt);\n";
+ txt += " always @(posedge clk) begin\n";
+ txt += " if (rst)\n";
+ txt += " cnt <= 0;\n";
+ txt += " else if (inc)\n";
+ txt += " cnt <= cnt + 1;\n";
+ txt += " end\n";
+ txt += "endmodule\n";
+ txt += "\n";
+ ys.write_file('example.v', txt);
+
+ var txt = "";
+ txt += "# a simple yosys.js example. run \"script example.ys\".\n";
+ txt += "\n";
+ txt += "design -reset\n";
+ txt += "read_verilog example.v\n";
+ txt += "proc\n";
+ txt += "opt\n";
+ txt += "show\n";
+ txt += "\n";
+ ys.write_file('example.ys', txt);
+
+ open_file('example.ys')
+ document.getElementById('command').focus();
+ }
+
+ function run_command() {
+ var cmd = document.getElementById('command').value;
+ document.getElementById('command').value = '';
+ if (history_log.length == 0 || history_log[history_log.length-1] != cmd)
+ history_log.push(cmd);
+ history_index = history_log.length;
+
+ var show_dot_before = "";
+ try { show_dot_before = ys.read_file('show.dot'); } catch (e) { }
+
+ open_file('');
+
+ document.getElementById('wait').style.display = 'block';
+ document.getElementById('input').style.display = 'none';
+
+ function run_command_bh() {
+ try {
+ ys.run(cmd);
+ } catch (e) {
+ ys.write('Caught JavaScript exception. (see JavaScript console for details.)');
+ console.log(e);
+ }
+ print_output(ys.print_buffer);
+
+ document.getElementById('wait').style.display = 'none';
+ document.getElementById('input').style.display = 'block';
+ document.getElementById('prompt').textContent = ys.prompt();
+
+ var show_dot_after = "";
+ try { show_dot_after = ys.read_file('show.dot'); } catch (e) { }
+
+ if (show_dot_before != show_dot_after)
+ open_file('show.dot');
+
+ update_tabs();
+ }
+
+ window.setTimeout(run_command_bh, 50);
+ return false;
+ }
+ </script>
+</body></html>
diff --git a/misc/yosysjs/demo02.html b/misc/yosysjs/demo02.html
new file mode 100644
index 00000000..9191db98
--- /dev/null
+++ b/misc/yosysjs/demo02.html
@@ -0,0 +1,103 @@
+<html><head>
+ <title>YosysJS Example Application #02</title>
+ <script type="text/javascript" src="yosysjs.js"></script>
+</head><body>
+ <div id="popup" style="position: fixed; left: 0; top: 0; width:100%; height:100%; text-align:center; z-index: 1000;"><div
+ style="width:300px; margin: 200px auto; background-color: #88f; border:3px dashed #000;
+ padding:15px; text-align:center;"><span id="popupmsg">Loading...</span></div>
+ </div>
+ <h1>YosysJS Example Application #02</h1>
+ <textarea id="code" style="width: 800px; height: 300px;">
+// borrowed with some modifications from
+// http://www.ee.ed.ac.uk/~gerard/Teach/Verilog/manual/Example/lrgeEx2/cooley.html
+module up3down5(clock, data_in, up, down, carry_out, borrow_out, count_out, parity_out);
+
+input [8:0] data_in;
+input clock, up, down;
+
+output reg [8:0] count_out;
+output reg carry_out, borrow_out, parity_out;
+
+reg [9:0] cnt_up, cnt_dn;
+reg [8:0] count_nxt;
+
+always @(posedge clock)
+begin
+ cnt_dn = count_out - 3'b 101;
+ cnt_up = count_out + 2'b 11;
+
+ case ({up,down})
+ 2'b 00 : count_nxt = data_in;
+ 2'b 01 : count_nxt = cnt_dn;
+ 2'b 10 : count_nxt = cnt_up;
+ 2'b 11 : count_nxt = count_out;
+ default : count_nxt = 9'bX;
+ endcase
+
+ parity_out &lt;= ^count_nxt;
+ carry_out &lt;= up &amp; cnt_up[9];
+ borrow_out &lt;= down &amp; cnt_dn[9];
+ count_out &lt;= count_nxt;
+end
+
+endmodule
+ </textarea><p/>
+ <input type="button" value="Before Behavioral Synth" onclick="synth1()">
+ <input type="button" value="After Behavioral Synth" onclick="synth2()">
+ <input type="button" value="After RTL Synth" onclick="synth3()">
+ <input type="button" value="After Gate-Level Synth" onclick="synth4()"><p/>
+ <svg id="svg" width="800"></svg>
+ </td></tr></table>
+ <script type="text/javascript">
+ YosysJS.load_viz();
+ function on_ys_ready() {
+ document.getElementById('popup').style.visibility = 'hidden';
+ document.getElementById('popupmsg').textContent = 'Please wait..';
+ }
+ function handle_run_errors(logmsg, errmsg) {
+ if (errmsg != "") {
+ window.alert(errmsg);
+ document.getElementById('popup').style.visibility = 'hidden';
+ }
+ }
+ function synth1() {
+ document.getElementById('popup').style.visibility = 'visible';
+ ys.write_file("input.v", document.getElementById('code').value);
+ ys.run('design -reset; read_verilog input.v; show -stretch', handle_run_errors);
+ ys.read_file('show.dot', (function(text){
+ console.log(ys.errmsg);
+ if (ys.errmsg == "") YosysJS.dot_into_svg(text, 'svg');
+ document.getElementById('popup').style.visibility = 'hidden';
+ }));
+ }
+ function synth2() {
+ document.getElementById('popup').style.visibility = 'visible';
+ ys.write_file("input.v", document.getElementById('code').value);
+ ys.run('design -reset; read_verilog input.v; proc; opt_clean; show -stretch', handle_run_errors);
+ ys.read_file('show.dot', (function(text){
+ if (ys.errmsg == "") YosysJS.dot_into_svg(text, 'svg');
+ document.getElementById('popup').style.visibility = 'hidden';
+ }));
+ }
+ function synth3() {
+ document.getElementById('popup').style.visibility = 'visible';
+ ys.write_file("input.v", document.getElementById('code').value);
+ ys.run('design -reset; read_verilog input.v; synth -run coarse; show -stretch', handle_run_errors);
+ ys.read_file('show.dot', (function(text){
+ if (ys.errmsg == "") YosysJS.dot_into_svg(text, 'svg');
+ document.getElementById('popup').style.visibility = 'hidden';
+ }));
+ }
+ function synth4() {
+ document.getElementById('popup').style.visibility = 'visible';
+ ys.write_file("input.v", document.getElementById('code').value);
+ ys.run('design -reset; read_verilog input.v; synth -run coarse; synth -run fine; show -stretch', handle_run_errors);
+ ys.read_file('show.dot', (function(text){
+ if (ys.errmsg == "") YosysJS.dot_into_svg(text, 'svg');
+ document.getElementById('popup').style.visibility = 'hidden';
+ }));
+ }
+ var ys = YosysJS.create_worker(on_ys_ready);
+ ys.verbose(true);
+ </script>
+</body></html>
diff --git a/misc/yosysjs/demo03.html b/misc/yosysjs/demo03.html
new file mode 100644
index 00000000..3dc465cb
--- /dev/null
+++ b/misc/yosysjs/demo03.html
@@ -0,0 +1,103 @@
+<html><head>
+<title>YosysJS Example Application #02</title>
+<script type="text/javascript" src="yosysjs.js"></script>
+<script src="http://wavedrom.com/skins/default.js" type="text/javascript"></script>
+<script src="http://wavedrom.com/WaveDrom.js" type="text/javascript"></script>
+<style type="text/css">
+.noedit { color: #666; }
+</style>
+<script id="golden_verilog" type="text/plain">
+module ref(input clk, reset, input [7:0] A, output reg [7:0] Y);
+ always @(posedge clk) begin
+ if (reset)
+ Y <= 0;
+ else
+ Y <= ((Y << 5) + Y) ^ A;
+ end
+endmodule
+</script>
+</head><body>
+ <div id="popup" style="position: fixed; left: 0; top: 0; width:100%; height:100%; text-align:center; z-index: 1000;
+ background-color: rgba(100, 100, 100, 0.5);"><div style="width:300px; margin: 200px auto; background-color: #88f;
+ border:3px dashed #000; padding:15px; text-align:center;"><span id="popupmsg">Loading...</span></div>
+ </div>
+ <h1>YosysJS Example Application #03</h1>
+ <b>Your mission:</b> Create a behavioral Verilog model for the following circuit:
+ <p/>
+ <div id="main" style="visibility: hidden">
+ <svg id="schem" width="800"></svg>
+ <p/>
+ <pre id="code" style="width: 800px; border:2px solid #000; padding: 0.5em;"><span class="noedit">module top(input clk, reset, input [7:0] A, output reg [7:0] Y);
+ always @(posedge clock) begin</span><span class="edit" contenteditable="true">
+ Y &lt;= A | {4{reset}};
+ </span><span class="noedit">end
+endmodule</span></pre><p/>
+ <input type="button" value="Check Model" onclick="check_model()"> <span id="checkmessage"></span>
+ <p/>
+ <p id="wave">&nbsp;</p>
+ </div>
+ <script type="text/javascript">
+ function on_ys_ready() {
+ ys.write_file('golden.v', document.getElementById('golden_verilog').textContent);
+ ys.run('echo on; read_verilog golden.v; proc;;');
+ ys.run('show -notitle -width -stretch');
+ YosysJS.dot_into_svg(ys.read_file('show.dot'), 'schem');
+ document.getElementById('popup').style.visibility = 'hidden';
+ document.getElementById('popupmsg').textContent = 'Please wait..';
+ document.getElementById('main').style.visibility = 'visible';
+ }
+ function check_model() {
+ function work() {
+ ys.remove_file('wave.json');
+ ys.write_file('code.v', document.getElementById('code').textContent);
+ ys.errmsg = '';
+ ys.run('design -reset; read_verilog code.v; hierarchy -top top; proc; opt; flatten; hierarchy; ' +
+ 'read_verilog golden.v; proc; miter -equiv -ignore_gold_x -make_outputs -flatten ref top miter; ' +
+ 'hierarchy -top miter; clean -purge; sat -set-init-undef -seq 8 -dump_json wave.json -show-ports ' +
+ '-max_undef -prove trigger 0 miter');
+ w = document.getElementById('wave')
+ if (ys.errmsg) {
+ w.innerHTML = '<b><pre>ERROR: ' + ys.errmsg.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;') + '</pre></b>';
+ } else {
+ wdata = ys.read_file('wave.json');
+ if (wdata) {
+ wdata = JSON.parse(wdata);
+ function wsignal(signame, newname) {
+ for (i = 0; i < wdata["signal"].length; i++)
+ if (wdata["signal"][i].name == signame) {
+ if (newname)
+ wdata["signal"][i].name = newname;
+ return wdata["signal"][i];
+ }
+ return {};
+ }
+ wdata2 = {
+ "signal" : [
+ { name: 'clk', wave: 'P........' },
+ wsignal("trigger"),
+ {},
+ [ "Inputs", wsignal("in_reset", "reset"), wsignal("in_A", "A") ],
+ {},
+ [ "Y Output", wsignal("gold_Y", "Ref"), wsignal("gate_Y", "UUT") ],
+ ],
+ "config" : wdata["config"]
+ };
+ wdata2 = JSON.stringify(wdata2)
+ w.innerHTML = '<b>The model did not pass verification:</b><p/>' +
+ '<script type="WaveDrom">' + wdata2 + '<\/script>';
+ WaveDrom.ProcessAll();
+ } else {
+ w.innerHTML = '<b>Congratulations! The model did pass verification.</b><p/>';
+ }
+ }
+ document.getElementById('popup').style.visibility = 'hidden';
+ }
+ document.getElementById('popup').style.visibility = 'visible';
+ window.setTimeout(work, 100);
+ }
+
+ YosysJS.load_viz();
+ var ys = YosysJS.create('', on_ys_ready);
+ ys.logprint = true;
+ </script>
+</body></html>
diff --git a/misc/yosysjs/yosysjs.js b/misc/yosysjs/yosysjs.js
new file mode 100644
index 00000000..ea98c9b5
--- /dev/null
+++ b/misc/yosysjs/yosysjs.js
@@ -0,0 +1,312 @@
+var YosysJS = new function() {
+ this.script_element = document.currentScript;
+ this.viz_element = undefined;
+ this.viz_ready = true;
+
+ this.url_prefix = this.script_element.src.replace(/[^/]+$/, '')
+
+ this.load_viz = function() {
+ if (this.viz_element)
+ return;
+
+ this.viz_element = document.createElement('iframe')
+ this.viz_element.style.display = 'none'
+ document.body.appendChild(this.viz_element);
+
+ this.viz_element.contentWindow.document.open();
+ this.viz_element.contentWindow.document.write('<script type="text/javascript" onload="viz_ready = true;" src="' + this.url_prefix + 'viz.js"></' + 'script>');
+ this.viz_element.contentWindow.document.close();
+
+ var that = this;
+ function check_viz_ready() {
+ if (that.viz_element.contentWindow.viz_ready) {
+ console.log("YosysJS: Successfully loaded Viz.");
+ that.viz_ready = true;
+ } else
+ window.setTimeout(check_viz_ready, 100);
+ }
+
+ this.viz_ready = false;
+ window.setTimeout(check_viz_ready, 100);
+ }
+
+ this.dot_to_svg = function(dot_text) {
+ return this.viz_element.contentWindow.Viz(dot_text, "svg");
+ }
+
+ this.dot_into_svg = function(dot_text, svg_element) {
+ if (typeof(svg_element) == 'string' && svg_element != "")
+ svg_element = document.getElementById(svg_element);
+ svg_element.innerHTML = this.dot_to_svg(dot_text);
+ c = svg_element.firstChild;
+ while (c) {
+ if (c.tagName == 'svg') {
+ while (c.firstChild)
+ svg_element.appendChild(c.firstChild);
+ svg_element.setAttribute('viewBox', c.getAttribute('viewBox'));
+ // svg_element.removeChild(c);
+ break;
+ }
+ c = c.nextSibling;
+ }
+ }
+
+ this.create = function(reference_element, on_ready) {
+ var ys = new Object();
+ ys.YosysJS = this;
+ ys.init_script = "";
+ ys.ready = false;
+ ys.verbose = false;
+ ys.logprint = false;
+ ys.echo = false;
+ ys.errmsg = "";
+
+ if (typeof(reference_element) == 'string' && reference_element != "")
+ reference_element = document.getElementById(reference_element);
+
+ if (reference_element) {
+ if (reference_element.tagName == 'textarea')
+ ys.init_script = reference_element.value;
+
+ if (reference_element.tagName == 'iframe') {
+ ys.iframe_element = reference_element;
+ } else {
+ ys.iframe_element = document.createElement('iframe');
+ ys.iframe_element.id = reference_element.id;
+ for (i in reference_element.style)
+ ys.iframe_element.style[i] = reference_element.style[i];
+ reference_element.parentNode.insertBefore(ys.iframe_element, reference_element);
+ reference_element.parentNode.removeChild(reference_element);
+ }
+ } else {
+ ys.iframe_element = document.createElement('iframe');
+ ys.iframe_element.style.display = 'none';
+ document.body.appendChild(ys.iframe_element);
+ }
+
+ ys.print_buffer = "";
+ ys.last_line_empty = false;
+ ys.got_normal_log_message = false;
+ ys.window = ys.iframe_element.contentWindow;
+
+ var doc = ys.window.document;
+ var mod = ys.window.Module = {
+ print: function(text) {
+ if (typeof(text) == 'number')
+ return;
+ ys.print_buffer += text + "\n";
+ ys.got_normal_log_message = true;
+ if (ys.logprint)
+ console.log(text);
+ if (ys.verbose) {
+ ys.last_line_empty = text == "";
+ if (text == "") {
+ span = doc.createElement('br');
+ } else {
+ span = doc.createElement('span');
+ span.textContent = text + "\n";
+ span.style.fontFamily = 'monospace';
+ span.style.whiteSpace = 'pre';
+ }
+ doc.firstChild.appendChild(span);
+ if (doc.body)
+ ys.window.scrollTo(0, doc.body.scrollHeight);
+ else
+ ys.window.scrollBy(0, 100);
+ }
+ ys.ready = true;
+ },
+ printErr: function(text) {
+ if (typeof(text) == 'number')
+ return;
+ if (ys.logprint)
+ console.log(text);
+ if (ys.got_normal_log_message) {
+ ys.print_buffer += text + "\n";
+ ys.last_line_empty = text == "";
+ if (text == "") {
+ span = doc.createElement('br');
+ } else {
+ span = doc.createElement('span');
+ span.textContent = text + "\n";
+ span.style.fontFamily = 'monospace';
+ span.style.whiteSpace = 'pre';
+ span.style.color = 'red';
+ }
+ doc.firstChild.appendChild(span);
+ if (doc.body)
+ ys.window.scrollTo(0, doc.body.scrollHeight);
+ else
+ ys.window.scrollBy(0, 100);
+ } else
+ if (!ys.logprint)
+ console.log(text);
+ },
+ };
+
+ ys.write = function(text) {
+ ys.print_buffer += text + "\n";
+ ys.last_line_empty = text == "";
+ span = doc.createElement('span');
+ span.textContent = text + "\n";
+ span.style.fontFamily = 'monospace';
+ span.style.whiteSpace = 'pre';
+ doc.firstChild.appendChild(span);
+ if (doc.body)
+ ys.window.scrollTo(0, doc.body.scrollHeight);
+ else
+ ys.window.scrollBy(0, 100);
+ }
+
+ ys.prompt = function() {
+ return mod.ccall('prompt', 'string', [], [])
+ }
+
+ ys.run = function(cmd) {
+ ys.print_buffer = "";
+ if (ys.echo) {
+ if (!ys.last_line_empty)
+ ys.write("");
+ ys.write(ys.prompt() + cmd);
+ }
+ try {
+ mod.ccall('run', '', ['string'], [cmd]);
+ } catch (e) {
+ ys.errmsg = mod.ccall('errmsg', 'string', [], []);
+ }
+ return ys.print_buffer;
+ }
+
+ ys.read_file = function(filename) {
+ try {
+ return ys.window.FS.readFile(filename, {encoding: 'utf8'});
+ } catch (e) {
+ return "";
+ }
+ }
+
+ ys.write_file = function(filename, text) {
+ return ys.window.FS.writeFile(filename, text, {encoding: 'utf8'});
+ }
+
+ ys.read_dir = function(dirname) {
+ return ys.window.FS.readdir(dirname);
+ }
+
+ ys.remove_file = function(filename) {
+ try {
+ ys.window.FS.unlink(filename);
+ } catch (e) { }
+ }
+
+ doc.open();
+ doc.write('<script type="text/javascript" src="' + this.url_prefix + 'yosys.js"></' + 'script>');
+ doc.close();
+
+ if (on_ready || ys.init_script) {
+ function check_ready() {
+ if (ys.ready && ys.YosysJS.viz_ready) {
+ if (ys.init_script) {
+ ys.write_file("/script.ys", ys.init_script);
+ ys.run("script /script.ys");
+ }
+ if (on_ready)
+ on_ready(ys);
+ } else
+ window.setTimeout(check_ready, 100);
+ }
+ window.setTimeout(check_ready, 100);
+ }
+
+ return ys;
+ }
+
+ this.create_worker = function(on_ready) {
+ var ys = new Object();
+ ys.YosysJS = this;
+ ys.worker = new Worker(this.url_prefix + 'yosyswrk.js');
+ ys.callback_idx = 1;
+ ys.callback_cache = {};
+ ys.errmsg = "";
+
+ ys.callback_cache[0] = on_ready;
+ on_ready = null;
+
+ ys.worker.onmessage = function(e) {
+ var response = e.data[0];
+ var callback = ys.callback_cache[response.idx];
+ delete ys.callback_cache[response.idx];
+ if ("errmsg" in response) ys.errmsg = response.errmsg;
+ if (callback) callback.apply(null, response.args);
+ }
+
+ ys.run = function(cmd, callback) {
+ var request = {
+ "idx": ys.callback_idx,
+ "mode": "run",
+ "cmd": cmd
+ };
+
+ ys.callback_cache[ys.callback_idx++] = callback;
+ ys.worker.postMessage([request]);
+ }
+
+ ys.read_file = function(filename, callback) {
+ var request = {
+ "idx": ys.callback_idx,
+ "mode": "read_file",
+ "filename": filename
+ };
+
+ ys.callback_cache[ys.callback_idx++] = callback;
+ ys.worker.postMessage([request]);
+ }
+
+ ys.write_file = function(filename, text, callback) {
+ var request = {
+ "idx": ys.callback_idx,
+ "mode": "write_file",
+ "filename": filename,
+ "text": text
+ };
+
+ ys.callback_cache[ys.callback_idx++] = callback;
+ ys.worker.postMessage([request]);
+ }
+
+ ys.read_dir = function(dirname, callback) {
+ var request = {
+ "idx": ys.callback_idx,
+ "mode": "read_dir",
+ "dirname": dirname
+ };
+
+ ys.callback_cache[ys.callback_idx++] = callback;
+ ys.worker.postMessage([request]);
+ }
+
+ ys.remove_file = function(filename, callback) {
+ var request = {
+ "idx": ys.callback_idx,
+ "mode": "remove_file",
+ "filename": filename
+ };
+
+ ys.callback_cache[ys.callback_idx++] = callback;
+ ys.worker.postMessage([request]);
+ }
+
+ ys.verbose = function(value, callback) {
+ var request = {
+ "idx": ys.callback_idx,
+ "mode": "verbose",
+ "value": value
+ };
+
+ ys.callback_cache[ys.callback_idx++] = callback;
+ ys.worker.postMessage([request]);
+ }
+
+ return ys;
+ }
+}
diff --git a/misc/yosysjs/yosyswrk.js b/misc/yosysjs/yosyswrk.js
new file mode 100644
index 00000000..b6173439
--- /dev/null
+++ b/misc/yosysjs/yosyswrk.js
@@ -0,0 +1,63 @@
+var Module = {};
+var verbose_mode = false;
+var text_buffer = "";
+
+Module["printErr"] = Module["print"] = function(text) {
+ if (verbose_mode)
+ console.log(text);
+ text_buffer += text + "\n";
+}
+
+importScripts('yosys.js');
+
+onmessage = function(e) {
+ var request = e.data[0];
+ var response = { "idx": request.idx, "args": [] };
+
+ if (request.mode == "run") {
+ response["errmsg"] = "";
+ try {
+ text_buffer = "";
+ Module.ccall('run', '', ['string'], [request.cmd]);
+ } catch (e) {
+ response.errmsg = Module.ccall('errmsg', 'string', [], []);
+ }
+ response.args.push(text_buffer);
+ response.args.push(response.errmsg);
+ text_buffer = "";
+ }
+
+ if (request.mode == "read_file") {
+ try {
+ response.args.push(FS.readFile(request.filename, {encoding: 'utf8'}));
+ } catch (e) { }
+ }
+
+ if (request.mode == "write_file") {
+ try {
+ FS.writeFile(request.filename, request.text, {encoding: 'utf8'});
+ } catch (e) { }
+ }
+
+ if (request.mode == "read_dir") {
+ try {
+ response.args.push(FS.readdir(request.dirname));
+ } catch (e) { }
+ }
+
+ if (request.mode == "remove_file") {
+ try {
+ FS.unlink(request.filename);
+ } catch (e) { }
+ }
+
+ if (request.mode == "verbose") {
+ if (request.value)
+ console.log(text_buffer);
+ verbose_mode = request.value;
+ }
+
+ postMessage([response]);
+}
+
+postMessage([{ "idx": 0, "args": [] }]);
diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc
new file mode 100644
index 00000000..01ada773
--- /dev/null
+++ b/passes/cmds/Makefile.inc
@@ -0,0 +1,28 @@
+
+OBJS += passes/cmds/add.o
+OBJS += passes/cmds/delete.o
+OBJS += passes/cmds/design.o
+OBJS += passes/cmds/select.o
+OBJS += passes/cmds/show.o
+OBJS += passes/cmds/rename.o
+OBJS += passes/cmds/connect.o
+OBJS += passes/cmds/scatter.o
+OBJS += passes/cmds/setundef.o
+OBJS += passes/cmds/splitnets.o
+OBJS += passes/cmds/stat.o
+OBJS += passes/cmds/setattr.o
+OBJS += passes/cmds/copy.o
+OBJS += passes/cmds/splice.o
+OBJS += passes/cmds/scc.o
+OBJS += passes/cmds/torder.o
+OBJS += passes/cmds/logcmd.o
+OBJS += passes/cmds/tee.o
+OBJS += passes/cmds/write_file.o
+OBJS += passes/cmds/connwrappers.o
+OBJS += passes/cmds/cover.o
+OBJS += passes/cmds/trace.o
+OBJS += passes/cmds/plugin.o
+OBJS += passes/cmds/check.o
+OBJS += passes/cmds/qwp.o
+OBJS += passes/cmds/edgetypes.o
+
diff --git a/passes/cmds/add.cc b/passes/cmds/add.cc
new file mode 100644
index 00000000..e698926f
--- /dev/null
+++ b/passes/cmds/add.cc
@@ -0,0 +1,154 @@
+/*
+ * 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"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+static void add_wire(RTLIL::Design *design, RTLIL::Module *module, std::string name, int width, bool flag_input, bool flag_output, bool flag_global)
+{
+ RTLIL::Wire *wire = NULL;
+ name = RTLIL::escape_id(name);
+
+ if (module->count_id(name) != 0)
+ {
+ if (module->wires_.count(name) > 0)
+ wire = module->wires_.at(name);
+
+ if (wire != NULL && wire->width != width)
+ wire = NULL;
+
+ if (wire != NULL && wire->port_input != flag_input)
+ wire = NULL;
+
+ if (wire != NULL && wire->port_output != flag_output)
+ wire = NULL;
+
+ if (wire == NULL)
+ log_cmd_error("Found incompatible object with same name in module %s!\n", module->name.c_str());
+
+ log("Module %s already has such an object.\n", module->name.c_str());
+ }
+ else
+ {
+ wire = module->addWire(name, width);
+ wire->port_input = flag_input;
+ wire->port_output = flag_output;
+
+ if (flag_input || flag_output) {
+ wire->port_id = module->wires_.size();
+ module->fixup_ports();
+ }
+
+ log("Added wire %s to module %s.\n", name.c_str(), module->name.c_str());
+ }
+
+ if (!flag_global)
+ return;
+
+ for (auto &it : module->cells_)
+ {
+ if (design->modules_.count(it.second->type) == 0)
+ continue;
+
+ RTLIL::Module *mod = design->modules_.at(it.second->type);
+ if (!design->selected_whole_module(mod->name))
+ continue;
+ if (mod->get_bool_attribute("\\blackbox"))
+ continue;
+ if (it.second->hasPort(name))
+ continue;
+
+ it.second->setPort(name, wire);
+ log("Added connection %s to cell %s.%s (%s).\n", name.c_str(), module->name.c_str(), it.first.c_str(), it.second->type.c_str());
+ }
+}
+
+struct AddPass : public Pass {
+ AddPass() : Pass("add", "add objects to the design") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" add <command> [selection]\n");
+ log("\n");
+ log("This command adds objects to the design. It operates on all fully selected\n");
+ log("modules. So e.g. 'add -wire foo' will add a wire foo to all selected modules.\n");
+ log("\n");
+ log("\n");
+ log(" add {-wire|-input|-inout|-output} <name> <width> [selection]\n");
+ log("\n");
+ log("Add a wire (input, inout, output port) with the given name and width. The\n");
+ log("command will fail if the object exists already and has different properties\n");
+ log("than the object to be created.\n");
+ log("\n");
+ log("\n");
+ log(" add -global_input <name> <width> [selection]\n");
+ log("\n");
+ log("Like 'add -input', but also connect the signal between instances of the\n");
+ log("selected modules.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::string command;
+ std::string arg_name;
+ bool arg_flag_input = false;
+ bool arg_flag_output = false;
+ bool arg_flag_global = false;
+ int arg_width = 0;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-wire" || arg == "-input" || arg == "-inout" || arg == "-output" || arg == "-global_input") {
+ if (argidx+2 >= args.size())
+ break;
+ command = "wire";
+ if (arg == "-input" || arg == "-inout" || arg == "-global_input")
+ arg_flag_input = true;
+ if (arg == "-output" || arg == "-inout")
+ arg_flag_output = true;
+ if (arg == "-global_input")
+ arg_flag_global = true;
+ arg_name = args[++argidx];
+ arg_width = atoi(args[++argidx].c_str());
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto &mod : design->modules_)
+ {
+ RTLIL::Module *module = mod.second;
+ if (!design->selected_whole_module(module->name))
+ continue;
+ if (module->get_bool_attribute("\\blackbox"))
+ continue;
+
+ if (command == "wire")
+ add_wire(design, module, arg_name, arg_width, arg_flag_input, arg_flag_output, arg_flag_global);
+ }
+ }
+} AddPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc
new file mode 100644
index 00000000..b3622cb1
--- /dev/null
+++ b/passes/cmds/check.cc
@@ -0,0 +1,162 @@
+/*
+ * 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"
+#include "kernel/utils.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct CheckPass : public Pass {
+ CheckPass() : Pass("check", "check for obvious problems in the design") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" check [options] [selection]\n");
+ log("\n");
+ log("This pass identifies the following problems in the current design:\n");
+ log("\n");
+ log(" - combinatorial loops\n");
+ log("\n");
+ log(" - two or more conflicting drivers for one wire\n");
+ log("\n");
+ log(" - used wires that do not have a driver\n");
+ log("\n");
+ log("When called with -noinit then this command also checks for wires which have\n");
+ log("the 'init' attribute set.\n");
+ log("\n");
+ log("When called with -assert then the command will produce an error if any\n");
+ log("problems are found in the current design.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ int counter = 0;
+ bool noinit = false;
+ bool assert_mode = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-noinit") {
+ noinit = true;
+ continue;
+ }
+ if (args[argidx] == "-assert") {
+ assert_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ log_header(design, "Executing CHECK pass (checking for obvious problems).\n");
+
+ for (auto module : design->selected_whole_modules_warn())
+ {
+ if (module->has_processes_warn())
+ continue;
+
+ log("checking module %s..\n", log_id(module));
+
+ SigMap sigmap(module);
+ dict<SigBit, vector<string>> wire_drivers;
+ dict<SigBit, int> wire_drivers_count;
+ pool<SigBit> used_wires;
+ TopoSort<string> topo;
+
+ for (auto cell : module->cells())
+ for (auto &conn : cell->connections()) {
+ SigSpec sig = sigmap(conn.second);
+ bool logic_cell = yosys_celltypes.cell_evaluable(cell->type);
+ if (cell->input(conn.first))
+ for (auto bit : sig)
+ if (bit.wire) {
+ if (logic_cell)
+ topo.edge(stringf("wire %s", log_signal(bit)),
+ stringf("cell %s (%s)", log_id(cell), log_id(cell->type)));
+ used_wires.insert(bit);
+ }
+ if (cell->output(conn.first))
+ for (int i = 0; i < GetSize(sig); i++) {
+ if (logic_cell)
+ topo.edge(stringf("cell %s (%s)", log_id(cell), log_id(cell->type)),
+ stringf("wire %s", log_signal(sig[i])));
+ if (sig[i].wire)
+ wire_drivers[sig[i]].push_back(stringf("port %s[%d] of cell %s (%s)",
+ log_id(conn.first), i, log_id(cell), log_id(cell->type)));
+ }
+ if (!cell->input(conn.first) && cell->output(conn.first))
+ for (auto bit : sig)
+ if (bit.wire) wire_drivers_count[bit]++;
+ }
+
+ for (auto wire : module->wires()) {
+ if (wire->port_input) {
+ SigSpec sig = sigmap(wire);
+ for (int i = 0; i < GetSize(sig); i++)
+ wire_drivers[sig[i]].push_back(stringf("module input %s[%d]", log_id(wire), i));
+ }
+ if (wire->port_output)
+ for (auto bit : sigmap(wire))
+ if (bit.wire) used_wires.insert(bit);
+ if (wire->port_input && !wire->port_output)
+ for (auto bit : sigmap(wire))
+ if (bit.wire) wire_drivers_count[bit]++;
+ if (noinit && wire->attributes.count("\\init")) {
+ log_warning("Wire %s.%s has an unprocessed 'init' attribute.\n", log_id(module), log_id(wire));
+ counter++;
+ }
+ }
+
+ for (auto it : wire_drivers)
+ if (wire_drivers_count[it.first] > 1) {
+ string message = stringf("multiple conflicting drivers for %s.%s:\n", log_id(module), log_signal(it.first));
+ for (auto str : it.second)
+ message += stringf(" %s\n", str.c_str());
+ log_warning("%s", message.c_str());
+ counter++;
+ }
+
+ for (auto bit : used_wires)
+ if (!wire_drivers.count(bit)) {
+ log_warning("Wire %s.%s is used but has no driver.\n", log_id(module), log_signal(bit));
+ counter++;
+ }
+
+ topo.sort();
+ for (auto &loop : topo.loops) {
+ string message = stringf("found logic loop in module %s:\n", log_id(module));
+ for (auto &str : loop)
+ message += stringf(" %s\n", str.c_str());
+ log_warning("%s", message.c_str());
+ counter++;
+ }
+ }
+
+ log("found and reported %d problems.\n", counter);
+
+ if (assert_mode && counter > 0)
+ log_error("Found %d problems in 'check -assert'.\n", counter);
+ }
+} CheckPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/connect.cc b/passes/cmds/connect.cc
new file mode 100644
index 00000000..52611cf4
--- /dev/null
+++ b/passes/cmds/connect.cc
@@ -0,0 +1,189 @@
+/*
+ * 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/register.h"
+#include "kernel/rtlil.h"
+#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+static void unset_drivers(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, RTLIL::SigSpec &sig)
+{
+ CellTypes ct(design);
+
+ RTLIL::Wire *dummy_wire = module->addWire(NEW_ID, sig.size());
+
+ for (auto &it : module->cells_)
+ for (auto &port : it.second->connections_)
+ if (ct.cell_output(it.second->type, port.first))
+ sigmap(port.second).replace(sig, dummy_wire, &port.second);
+
+ for (auto &conn : module->connections_)
+ sigmap(conn.first).replace(sig, dummy_wire, &conn.first);
+}
+
+struct ConnectPass : public Pass {
+ ConnectPass() : Pass("connect", "create or remove connections") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" connect [-nomap] [-nounset] -set <lhs-expr> <rhs-expr>\n");
+ log("\n");
+ log("Create a connection. This is equivalent to adding the statement 'assign\n");
+ log("<lhs-expr> = <rhs-expr>;' to the Verilog input. Per default, all existing\n");
+ log("drivers for <lhs-expr> are unconnected. This can be overwritten by using\n");
+ log("the -nounset option.\n");
+ log("\n");
+ log("\n");
+ log(" connect [-nomap] -unset <expr>\n");
+ log("\n");
+ log("Unconnect all existing drivers for the specified expression.\n");
+ log("\n");
+ log("\n");
+ log(" connect [-nomap] -port <cell> <port> <expr>\n");
+ log("\n");
+ log("Connect the specified cell port to the specified cell port.\n");
+ log("\n");
+ log("\n");
+ log("Per default signal alias names are resolved and all signal names are mapped\n");
+ log("the the signal name of the primary driver. Using the -nomap option deactivates\n");
+ log("this behavior.\n");
+ log("\n");
+ log("The connect command operates in one module only. Either only one module must\n");
+ log("be selected or an active module must be set using the 'cd' command.\n");
+ log("\n");
+ log("This command does not operate on module with processes.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ RTLIL::Module *module = NULL;
+ for (auto &it : design->modules_) {
+ if (!design->selected(it.second))
+ continue;
+ if (module != NULL)
+ log_cmd_error("Multiple modules selected: %s, %s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(it.first));
+ module = it.second;
+ }
+ if (module == NULL)
+ log_cmd_error("No modules selected.\n");
+ if (!module->processes.empty())
+ log_cmd_error("Found processes in selected module.\n");
+
+ bool flag_nounset = false, flag_nomap = false;
+ std::string set_lhs, set_rhs, unset_expr;
+ std::string port_cell, port_port, port_expr;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-nounset") {
+ flag_nounset = true;
+ continue;
+ }
+ if (arg == "-nomap") {
+ flag_nomap = true;
+ continue;
+ }
+ if (arg == "-set" && argidx+2 < args.size()) {
+ set_lhs = args[++argidx];
+ set_rhs = args[++argidx];
+ continue;
+ }
+ if (arg == "-unset" && argidx+1 < args.size()) {
+ unset_expr = args[++argidx];
+ continue;
+ }
+ if (arg == "-port" && argidx+3 < args.size()) {
+ port_cell = args[++argidx];
+ port_port = args[++argidx];
+ port_expr = args[++argidx];
+ continue;
+ }
+ break;
+ }
+
+ SigMap sigmap;
+ if (!flag_nomap)
+ for (auto &it : module->connections()) {
+ std::vector<RTLIL::SigBit> lhs = it.first.to_sigbit_vector();
+ std::vector<RTLIL::SigBit> rhs = it.first.to_sigbit_vector();
+ for (size_t i = 0; i < lhs.size(); i++)
+ if (rhs[i].wire != NULL)
+ sigmap.add(lhs[i], rhs[i]);
+ }
+
+ if (!set_lhs.empty())
+ {
+ if (!unset_expr.empty() || !port_cell.empty())
+ log_cmd_error("Cant 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))
+ log_cmd_error("Failed to parse set lhs expression `%s'.\n", set_lhs.c_str());
+ if (!RTLIL::SigSpec::parse_rhs(sig_lhs, sig_rhs, module, set_rhs))
+ log_cmd_error("Failed to parse set rhs expression `%s'.\n", set_rhs.c_str());
+
+ sigmap.apply(sig_lhs);
+ sigmap.apply(sig_rhs);
+
+ if (!flag_nounset)
+ unset_drivers(design, module, sigmap, sig_lhs);
+
+ module->connect(RTLIL::SigSig(sig_lhs, sig_rhs));
+ }
+ else
+ if (!unset_expr.empty())
+ {
+ if (!port_cell.empty() || flag_nounset)
+ log_cmd_error("Cant use -unset together with -port and/or -nounset.\n");
+
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, unset_expr))
+ log_cmd_error("Failed to parse unset expression `%s'.\n", unset_expr.c_str());
+
+ sigmap.apply(sig);
+ unset_drivers(design, module, sigmap, sig);
+ }
+ else
+ if (!port_cell.empty())
+ {
+ if (flag_nounset)
+ log_cmd_error("Cant 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());
+
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, port_expr))
+ log_cmd_error("Failed to parse port expression `%s'.\n", port_expr.c_str());
+
+ module->cells_.at(RTLIL::escape_id(port_cell))->setPort(RTLIL::escape_id(port_port), sigmap(sig));
+ }
+ else
+ log_cmd_error("Expected -set, -unset, or -port.\n");
+ }
+} ConnectPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/connwrappers.cc b/passes/cmds/connwrappers.cc
new file mode 100644
index 00000000..c9ab226d
--- /dev/null
+++ b/passes/cmds/connwrappers.cc
@@ -0,0 +1,209 @@
+/*
+ * 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/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct ConnwrappersWorker
+{
+ struct portdecl_t {
+ // key: celltype, portname;
+ std::string widthparam, signparam;
+ bool is_signed;
+ };
+
+ std::set<RTLIL::IdString> decl_celltypes;
+ std::map<std::pair<RTLIL::IdString, RTLIL::IdString>, portdecl_t> decls;
+
+ void add_port(std::string celltype, std::string portname, std::string widthparam, std::string signparam)
+ {
+ std::pair<std::string, std::string> key(RTLIL::escape_id(celltype), RTLIL::escape_id(portname));
+ decl_celltypes.insert(key.first);
+
+ if (decls.count(key))
+ log_cmd_error("Duplicate port decl: %s %s\n", celltype.c_str(), portname.c_str());
+
+ portdecl_t decl;
+ decl.widthparam = RTLIL::escape_id(widthparam);
+ decl.signparam = RTLIL::escape_id(signparam);
+ decl.is_signed = false;
+ decls[key] = decl;
+ }
+
+ void add_port(std::string celltype, std::string portname, std::string widthparam, bool is_signed)
+ {
+ std::pair<std::string, std::string> key(RTLIL::escape_id(celltype), RTLIL::escape_id(portname));
+ decl_celltypes.insert(key.first);
+
+ if (decls.count(key))
+ log_cmd_error("Duplicate port decl: %s %s\n", celltype.c_str(), portname.c_str());
+
+ portdecl_t decl;
+ decl.widthparam = RTLIL::escape_id(widthparam);
+ decl.is_signed = is_signed;
+ decls[key] = decl;
+ }
+
+ void work(RTLIL::Design *design, RTLIL::Module *module)
+ {
+ std::map<RTLIL::SigBit, std::pair<bool, RTLIL::SigSpec>> extend_map;
+ SigMap sigmap(module);
+
+ for (auto &it : module->cells_)
+ {
+ RTLIL::Cell *cell = it.second;
+
+ if (!decl_celltypes.count(cell->type))
+ continue;
+
+ for (auto &conn : cell->connections())
+ {
+ std::pair<RTLIL::IdString, RTLIL::IdString> key(cell->type, conn.first);
+
+ if (!decls.count(key))
+ continue;
+
+ portdecl_t &decl = decls.at(key);
+
+ if (!cell->parameters.count(decl.widthparam))
+ continue;
+
+ if (!decl.signparam.empty() && !cell->parameters.count(decl.signparam))
+ continue;
+
+ int inner_width = cell->parameters.at(decl.widthparam).as_int();
+ int outer_width = conn.second.size();
+ bool is_signed = decl.signparam.empty() ? decl.is_signed : cell->parameters.at(decl.signparam).as_bool();
+
+ if (inner_width >= outer_width)
+ continue;
+
+ RTLIL::SigSpec sig = sigmap(conn.second);
+ extend_map[sig.extract(inner_width - 1, 1)] = std::pair<bool, RTLIL::SigSpec>(is_signed,
+ sig.extract(inner_width, outer_width - inner_width));
+ }
+ }
+
+ for (auto &it : module->cells_)
+ {
+ RTLIL::Cell *cell = it.second;
+
+ if (!design->selected(module, cell))
+ continue;
+
+ for (auto &conn : cell->connections_)
+ {
+ std::vector<RTLIL::SigBit> sigbits = sigmap(conn.second).to_sigbit_vector();
+ RTLIL::SigSpec old_sig;
+
+ for (size_t i = 0; i < sigbits.size(); i++)
+ {
+ if (!extend_map.count(sigbits[i]))
+ continue;
+
+ bool is_signed = extend_map.at(sigbits[i]).first;
+ RTLIL::SigSpec extend_sig = extend_map.at(sigbits[i]).second;
+
+ int extend_width = 0;
+ RTLIL::SigBit extend_bit = is_signed ? sigbits[i] : RTLIL::SigBit(RTLIL::State::S0);
+ while (extend_width < extend_sig.size() && i + extend_width + 1 < sigbits.size() &&
+ sigbits[i + extend_width + 1] == extend_bit) extend_width++;
+
+ if (extend_width == 0)
+ continue;
+
+ if (old_sig.size() == 0)
+ old_sig = conn.second;
+
+ conn.second.replace(i+1, extend_sig.extract(0, extend_width));
+ i += extend_width;
+ }
+
+ if (old_sig.size())
+ log("Connected extended bits of %s.%s:%s: %s -> %s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name),
+ RTLIL::id2cstr(conn.first), log_signal(old_sig), log_signal(conn.second));
+ }
+ }
+ }
+};
+
+struct ConnwrappersPass : public Pass {
+ ConnwrappersPass() : Pass("connwrappers", "replace undef values with defined constants") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" connwrappers [options] [selection]\n");
+ log("\n");
+ log("Wrappers are used in coarse-grain synthesis to wrap cells with smaller ports\n");
+ log("in wrapper cells with a (larger) constant port size. I.e. the upper bits\n");
+ log("of the wrapper output are signed/unsigned bit extended. This command uses this\n");
+ log("knowledge to rewire the inputs of the driven cells to match the output of\n");
+ log("the driving cell.\n");
+ log("\n");
+ log(" -signed <cell_type> <port_name> <width_param>\n");
+ log(" -unsigned <cell_type> <port_name> <width_param>\n");
+ log(" consider the specified signed/unsigned wrapper output\n");
+ log("\n");
+ log(" -port <cell_type> <port_name> <width_param> <sign_param>\n");
+ log(" use the specified parameter to decide if signed or unsigned\n");
+ log("\n");
+ log("The options -signed, -unsigned, and -port can be specified multiple times.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ ConnwrappersWorker worker;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-signed" && argidx+3 < args.size()) {
+ worker.add_port(args[argidx+1], args[argidx+2], args[argidx+3], true);
+ argidx += 3;
+ continue;
+ }
+ if (args[argidx] == "-unsigned" && argidx+3 < args.size()) {
+ worker.add_port(args[argidx+1], args[argidx+2], args[argidx+3], false);
+ argidx += 3;
+ continue;
+ }
+ if (args[argidx] == "-port" && argidx+4 < args.size()) {
+ worker.add_port(args[argidx+1], args[argidx+2], args[argidx+3], args[argidx+4]);
+ argidx += 4;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ log_header(design, "Executing CONNWRAPPERS pass (connect extended ports of wrapper cells).\n");
+
+ for (auto &mod_it : design->modules_)
+ if (design->selected(mod_it.second))
+ worker.work(design, mod_it.second);
+ }
+} ConnwrappersPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/copy.cc b/passes/cmds/copy.cc
new file mode 100644
index 00000000..fb863512
--- /dev/null
+++ b/passes/cmds/copy.cc
@@ -0,0 +1,59 @@
+/*
+ * 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/register.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct CopyPass : public Pass {
+ CopyPass() : Pass("copy", "copy modules in the design") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" copy old_name new_name\n");
+ log("\n");
+ log("Copy the specified module. Note that selection patterns are not supported\n");
+ log("by this command.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ if (args.size() != 3)
+ log_cmd_error("Invalid number of arguments!\n");
+
+ std::string src_name = RTLIL::escape_id(args[1]);
+ std::string trg_name = RTLIL::escape_id(args[2]);
+
+ if (design->modules_.count(src_name) == 0)
+ log_cmd_error("Can't find source module %s.\n", src_name.c_str());
+
+ if (design->modules_.count(trg_name) != 0)
+ log_cmd_error("Target module name %s already exists.\n", trg_name.c_str());
+
+ RTLIL::Module *new_mod = design->module(src_name)->clone();
+ new_mod->name = trg_name;
+ design->add(new_mod);
+ }
+} CopyPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/cover.cc b/passes/cmds/cover.cc
new file mode 100644
index 00000000..1475475c
--- /dev/null
+++ b/passes/cmds/cover.cc
@@ -0,0 +1,157 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2014 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 <sys/types.h>
+
+#ifndef _WIN32
+# include <unistd.h>
+#else
+# include <io.h>
+#endif
+
+#include "kernel/register.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct CoverPass : public Pass {
+ CoverPass() : Pass("cover", "print code coverage counters") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" cover [options] [pattern]\n");
+ log("\n");
+ log("Print the code coverage counters collected using the cover() macro in the Yosys\n");
+ log("C++ code. This is useful to figure out what parts of Yosys are utilized by a\n");
+ log("test bench.\n");
+ log("\n");
+ log(" -q\n");
+ log(" Do not print output to the normal destination (console and/or log file)\n");
+ log("\n");
+ log(" -o file\n");
+ log(" Write output to this file, truncate if exists.\n");
+ log("\n");
+ log(" -a file\n");
+ log(" Write output to this file, append if exists.\n");
+ log("\n");
+ log(" -d dir\n");
+ log(" Write output to a newly created file in the specified directory.\n");
+ log("\n");
+ log("When one or more pattern (shell wildcards) are specified, then only counters\n");
+ log("matching at least one pattern are printed.\n");
+ log("\n");
+ log("\n");
+ log("It is also possible to instruct Yosys to print the coverage counters on program\n");
+ log("exit to a file using environment variables:\n");
+ log("\n");
+ log(" YOSYS_COVER_DIR=\"{dir-name}\" yosys {args}\n");
+ log("\n");
+ log(" This will create a file (with an auto-generated name) in this\n");
+ log(" directory and write the coverage counters to it.\n");
+ log("\n");
+ log(" YOSYS_COVER_FILE=\"{file-name}\" yosys {args}\n");
+ log("\n");
+ log(" This will append the coverage counters to the specified file.\n");
+ log("\n");
+ log("\n");
+ log("Hint: Use the following AWK command to consolidate Yosys coverage files:\n");
+ log("\n");
+ log(" gawk '{ p[$3] = $1; c[$3] += $2; } END { for (i in p)\n");
+ log(" printf \"%%-60s %%10d %%s\\n\", p[i], c[i], i; }' {files} | sort -k3\n");
+ log("\n");
+ log("\n");
+ log("Coverage counters are only available in Yosys for Linux.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::vector<FILE*> out_files;
+ std::vector<std::string> patterns;
+ bool do_log = true;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-q") {
+ do_log = false;
+ continue;
+ }
+ if ((args[argidx] == "-o" || args[argidx] == "-a" || args[argidx] == "-d") && argidx+1 < args.size()) {
+ const char *open_mode = args[argidx] == "-a" ? "a+" : "w";
+ std::string filename = args[++argidx];
+ if (args[argidx-1] == "-d") {
+ #ifdef _WIN32
+ log_cmd_error("The 'cover -d' option is not supported on win32.\n");
+ #else
+ char filename_buffer[4096];
+ snprintf(filename_buffer, 4096, "%s/yosys_cover_%d_XXXXXX.txt", filename.c_str(), getpid());
+ filename = mkstemps(filename_buffer, 4);
+ #endif
+ }
+ FILE *f = fopen(filename.c_str(), open_mode);
+ if (f == NULL) {
+ for (auto f : out_files)
+ fclose(f);
+ log_cmd_error("Can't create file %s.\n", args[argidx].c_str());
+ }
+ out_files.push_back(f);
+ continue;
+ }
+ break;
+ }
+ while (argidx < args.size() && args[argidx].substr(0, 1) != "-")
+ patterns.push_back(args[argidx++]);
+ extra_args(args, argidx, design);
+
+ if (do_log) {
+ log_header(design, "Printing code coverage counters.\n");
+ log("\n");
+ }
+
+#if defined(YOSYS_ENABLE_COVER) && defined(__linux__)
+ for (auto &it : get_coverage_data()) {
+ if (!patterns.empty()) {
+ for (auto &p : patterns)
+ if (patmatch(p.c_str(), it.first.c_str()))
+ goto pattern_match;
+ continue;
+ }
+ pattern_match:
+ for (auto f : out_files)
+ fprintf(f, "%-60s %10d %s\n", it.second.first.c_str(), it.second.second, it.first.c_str());
+ if (do_log)
+ log("%-60s %10d %s\n", it.second.first.c_str(), it.second.second, it.first.c_str());
+ }
+#else
+ for (auto f : out_files)
+ fclose(f);
+
+ log_cmd_error("This version of Yosys was not built with support for code coverage counters.\n");
+#endif
+
+ for (auto f : out_files)
+ fclose(f);
+ }
+} CoverPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/delete.cc b/passes/cmds/delete.cc
new file mode 100644
index 00000000..6d51d30e
--- /dev/null
+++ b/passes/cmds/delete.cc
@@ -0,0 +1,144 @@
+/*
+ * 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"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct DeletePass : public Pass {
+ DeletePass() : Pass("delete", "delete objects in the design") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" delete [selection]\n");
+ log("\n");
+ log("Deletes the selected objects. This will also remove entire modules, if the\n");
+ log("whole module is selected.\n");
+ log("\n");
+ log("\n");
+ log(" delete {-input|-output|-port} [selection]\n");
+ log("\n");
+ log("Does not delete any object but removes the input and/or output flag on the\n");
+ log("selected wires, thus 'deleting' module ports.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool flag_input = false;
+ bool flag_output = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-input") {
+ flag_input = true;
+ continue;
+ }
+ if (args[argidx] == "-output") {
+ flag_output = true;
+ continue;
+ }
+ if (args[argidx] == "-port") {
+ flag_input = true;
+ flag_output = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ std::vector<RTLIL::IdString> delete_mods;
+
+ for (auto &mod_it : design->modules_)
+ {
+ if (design->selected_whole_module(mod_it.first) && !flag_input && !flag_output) {
+ delete_mods.push_back(mod_it.first);
+ continue;
+ }
+
+ if (!design->selected_module(mod_it.first))
+ continue;
+
+ RTLIL::Module *module = mod_it.second;
+
+ if (flag_input || flag_output) {
+ for (auto &it : module->wires_)
+ if (design->selected(module, it.second)) {
+ if (flag_input)
+ it.second->port_input = false;
+ if (flag_output)
+ it.second->port_output = false;
+ }
+ module->fixup_ports();
+ continue;
+ }
+
+ pool<RTLIL::Wire*> delete_wires;
+ pool<RTLIL::Cell*> delete_cells;
+ pool<RTLIL::IdString> delete_procs;
+ pool<RTLIL::IdString> delete_mems;
+
+ for (auto &it : module->wires_)
+ if (design->selected(module, it.second))
+ delete_wires.insert(it.second);
+
+ for (auto &it : module->memories)
+ if (design->selected(module, it.second))
+ delete_mems.insert(it.first);
+
+ for (auto &it : module->cells_) {
+ if (design->selected(module, it.second))
+ delete_cells.insert(it.second);
+ if ((it.second->type == "$memrd" || it.second->type == "$memwr") &&
+ delete_mems.count(it.second->parameters.at("\\MEMID").decode_string()) != 0)
+ delete_cells.insert(it.second);
+ }
+
+ for (auto &it : module->processes)
+ if (design->selected(module, it.second))
+ delete_procs.insert(it.first);
+
+ for (auto &it : delete_mems) {
+ delete module->memories.at(it);
+ module->memories.erase(it);
+ }
+
+ for (auto &it : delete_cells)
+ module->remove(it);
+
+ for (auto &it : delete_procs) {
+ delete module->processes.at(it);
+ module->processes.erase(it);
+ }
+
+ module->remove(delete_wires);
+
+ module->fixup_ports();
+ }
+
+ for (auto &it : delete_mods) {
+ delete design->modules_.at(it);
+ design->modules_.erase(it);
+ }
+ }
+} DeletePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/design.cc b/passes/cmds/design.cc
new file mode 100644
index 00000000..e900e7b9
--- /dev/null
+++ b/passes/cmds/design.cc
@@ -0,0 +1,256 @@
+/*
+ * 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/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+std::map<std::string, RTLIL::Design*> saved_designs;
+std::vector<RTLIL::Design*> pushed_designs;
+
+struct DesignPass : public Pass {
+ DesignPass() : Pass("design", "save, restore and reset current design") { }
+ virtual ~DesignPass() {
+ for (auto &it : saved_designs)
+ delete it.second;
+ saved_designs.clear();
+ for (auto &it : pushed_designs)
+ delete it;
+ pushed_designs.clear();
+ }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" design -reset\n");
+ log("\n");
+ log("Clear the current design.\n");
+ log("\n");
+ log("\n");
+ log(" design -save <name>\n");
+ log("\n");
+ log("Save the current design under the given name.\n");
+ log("\n");
+ log("\n");
+ log(" design -stash <name>\n");
+ log("\n");
+ log("Save the current design under the given name and then clear the current design.\n");
+ log("\n");
+ log("\n");
+ log(" design -push\n");
+ log("\n");
+ log("Push the current design to the stack and then clear the current design.\n");
+ log("\n");
+ log("\n");
+ log(" design -pop\n");
+ log("\n");
+ log("Reset the current design and pop the last design from the stack.\n");
+ log("\n");
+ log("\n");
+ log(" design -load <name>\n");
+ log("\n");
+ log("Reset the current design and load the design previously saved under the given\n");
+ log("name.\n");
+ log("\n");
+ log("\n");
+ log(" design -copy-from <name> [-as <new_mod_name>] <selection>\n");
+ log("\n");
+ log("Copy modules from the specified design into the current one. The selection is\n");
+ log("evaluated in the other design.\n");
+ log("\n");
+ log("\n");
+ log(" design -copy-to <name> [-as <new_mod_name>] [selection]\n");
+ log("\n");
+ log("Copy modules from the current design into the specified one.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool got_mode = false;
+ bool reset_mode = false;
+ bool push_mode = false;
+ bool pop_mode = false;
+ RTLIL::Design *copy_from_design = NULL, *copy_to_design = NULL;
+ std::string save_name, load_name, as_name;
+ std::vector<RTLIL::Module*> copy_src_modules;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (!got_mode && args[argidx] == "-reset") {
+ got_mode = true;
+ reset_mode = true;
+ continue;
+ }
+ if (!got_mode && args[argidx] == "-push") {
+ got_mode = true;
+ push_mode = true;
+ continue;
+ }
+ if (!got_mode && args[argidx] == "-pop") {
+ got_mode = true;
+ pop_mode = true;
+ continue;
+ }
+ if (!got_mode && args[argidx] == "-save" && argidx+1 < args.size()) {
+ got_mode = true;
+ save_name = args[++argidx];
+ continue;
+ }
+ if (!got_mode && args[argidx] == "-stash" && argidx+1 < args.size()) {
+ got_mode = true;
+ save_name = args[++argidx];
+ reset_mode = true;
+ continue;
+ }
+ if (!got_mode && args[argidx] == "-load" && argidx+1 < args.size()) {
+ got_mode = true;
+ load_name = args[++argidx];
+ if (saved_designs.count(load_name) == 0)
+ log_cmd_error("No saved design '%s' found!\n", load_name.c_str());
+ continue;
+ }
+ if (!got_mode && args[argidx] == "-copy-from" && argidx+1 < args.size()) {
+ got_mode = true;
+ if (saved_designs.count(args[++argidx]) == 0)
+ log_cmd_error("No saved design '%s' found!\n", args[argidx].c_str());
+ copy_from_design = saved_designs.at(args[argidx]);
+ copy_to_design = design;
+ continue;
+ }
+ if (!got_mode && args[argidx] == "-copy-to" && argidx+1 < args.size()) {
+ got_mode = true;
+ if (saved_designs.count(args[++argidx]) == 0)
+ saved_designs[args[argidx]] = new RTLIL::Design;
+ copy_to_design = saved_designs.at(args[argidx]);
+ copy_from_design = design;
+ continue;
+ }
+ if (copy_from_design != NULL && args[argidx] == "-as" && argidx+1 < args.size()) {
+ got_mode = true;
+ as_name = args[++argidx];
+ continue;
+ }
+ break;
+ }
+
+ if (copy_from_design != NULL)
+ {
+ if (copy_from_design != design && argidx == args.size())
+ cmd_error(args, argidx, "Missing selection.");
+
+ RTLIL::Selection sel = design->selection_stack.back();
+ if (argidx != args.size()) {
+ handle_extra_select_args(this, args, argidx, args.size(), copy_from_design);
+ sel = copy_from_design->selection_stack.back();
+ copy_from_design->selection_stack.pop_back();
+ argidx = args.size();
+ }
+
+ for (auto &it : copy_from_design->modules_) {
+ if (sel.selected_whole_module(it.first)) {
+ copy_src_modules.push_back(it.second);
+ continue;
+ }
+ if (sel.selected_module(it.first))
+ log_cmd_error("Module %s is only partly selected.\n", RTLIL::id2cstr(it.first));
+ }
+ }
+
+ extra_args(args, argidx, design, false);
+
+ if (!got_mode)
+ cmd_error(args, argidx, "Missing mode argument.");
+
+ if (pop_mode && pushed_designs.empty())
+ log_cmd_error("No pushed designs.\n");
+
+ if (copy_to_design != NULL)
+ {
+ if (!as_name.empty() && copy_src_modules.size() > 1)
+ log_cmd_error("Only one module can be selected in combination with -as.\n");
+
+ for (auto mod : copy_src_modules)
+ {
+ std::string trg_name = as_name.empty() ? mod->name.str() : RTLIL::escape_id(as_name);
+
+ if (copy_to_design->modules_.count(trg_name))
+ delete copy_to_design->modules_.at(trg_name);
+ copy_to_design->modules_[trg_name] = mod->clone();
+ copy_to_design->modules_[trg_name]->name = trg_name;
+ copy_to_design->modules_[trg_name]->design = copy_to_design;
+ }
+ }
+
+ if (!save_name.empty() || push_mode)
+ {
+ RTLIL::Design *design_copy = new RTLIL::Design;
+
+ for (auto &it : design->modules_)
+ design_copy->add(it.second->clone());
+
+ design_copy->selection_stack = design->selection_stack;
+ design_copy->selection_vars = design->selection_vars;
+ design_copy->selected_active_module = design->selected_active_module;
+
+ if (saved_designs.count(save_name))
+ delete saved_designs.at(save_name);
+
+ if (push_mode)
+ pushed_designs.push_back(design_copy);
+ else
+ saved_designs[save_name] = design_copy;
+ }
+
+ if (reset_mode || !load_name.empty() || push_mode || pop_mode)
+ {
+ for (auto &it : design->modules_)
+ delete it.second;
+ design->modules_.clear();
+
+ design->selection_stack.clear();
+ design->selection_vars.clear();
+ design->selected_active_module.clear();
+
+ design->selection_stack.push_back(RTLIL::Selection());
+ }
+
+ if (!load_name.empty() || pop_mode)
+ {
+ RTLIL::Design *saved_design = pop_mode ? pushed_designs.back() : saved_designs.at(load_name);
+
+ if (pop_mode)
+ pushed_designs.pop_back();
+
+ for (auto &it : saved_design->modules_)
+ design->add(it.second->clone());
+
+ design->selection_stack = saved_design->selection_stack;
+ design->selection_vars = saved_design->selection_vars;
+ design->selected_active_module = saved_design->selected_active_module;
+ }
+ }
+} DesignPass;
+
+YOSYS_NAMESPACE_END
+
diff --git a/passes/cmds/edgetypes.cc b/passes/cmds/edgetypes.cc
new file mode 100644
index 00000000..7b75a009
--- /dev/null
+++ b/passes/cmds/edgetypes.cc
@@ -0,0 +1,106 @@
+/*
+ * 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 EdgetypePass : public Pass {
+ EdgetypePass() : Pass("edgetypes", "list all types of edges in selection") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" edgetypes [options] [selection]\n");
+ log("\n");
+ log("This command lists all unique types of 'edges' found in the selection. An 'edge'\n");
+ log("is a 4-tuple of source and sink cell type and port name.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ // if (args[argidx] == "-ltr") {
+ // config.ltr = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ pool<string> edge_cache;
+
+ for (auto module : design->selected_modules())
+ {
+ SigMap sigmap(module);
+ dict<SigBit, pool<tuple<IdString, IdString, int>>> bit_sources, bit_sinks;
+ pool<std::pair<IdString, IdString>> multibit_ports;
+
+ for (auto cell : module->selected_cells())
+ for (auto conn : cell->connections())
+ {
+ IdString cell_type = cell->type;
+ IdString port_name = conn.first;
+ SigSpec sig = sigmap(conn.second);
+
+ if (GetSize(sig) > 1)
+ multibit_ports.insert(std::pair<IdString, IdString>(cell_type, port_name));
+
+ for (int i = 0; i < GetSize(sig); i++) {
+ if (cell->output(port_name))
+ bit_sources[sig[i]].insert(tuple<IdString, IdString, int>(cell_type, port_name, i));
+ if (cell->input(port_name))
+ bit_sinks[sig[i]].insert(tuple<IdString, IdString, int>(cell_type, port_name, i));
+ }
+ }
+
+ for (auto &it : bit_sources)
+ for (auto &source : it.second)
+ for (auto &sink : bit_sinks[it.first])
+ {
+ auto source_cell_type = std::get<0>(source);
+ auto source_port_name = std::get<1>(source);
+ auto source_bit_index = std::get<2>(source);
+
+ auto sink_cell_type = std::get<0>(sink);
+ auto sink_port_name = std::get<1>(sink);
+ auto sink_bit_index = std::get<2>(sink);
+
+ string source_str = multibit_ports.count(std::pair<IdString, IdString>(source_cell_type, source_port_name)) ?
+ stringf("%s.%s[%d]", log_id(source_cell_type), log_id(source_port_name), source_bit_index) :
+ stringf("%s.%s", log_id(source_cell_type), log_id(source_port_name));
+
+ string sink_str = multibit_ports.count(std::pair<IdString, IdString>(sink_cell_type, sink_port_name)) ?
+ stringf("%s.%s[%d]", log_id(sink_cell_type), log_id(sink_port_name), sink_bit_index) :
+ stringf("%s.%s", log_id(sink_cell_type), log_id(sink_port_name));
+
+ edge_cache.insert(source_str + " " + sink_str);
+ }
+ }
+
+ edge_cache.sort();
+ for (auto &str : edge_cache)
+ log("%s\n", str.c_str());
+ }
+} EdgetypePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/logcmd.cc b/passes/cmds/logcmd.cc
new file mode 100644
index 00000000..85386f3d
--- /dev/null
+++ b/passes/cmds/logcmd.cc
@@ -0,0 +1,82 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.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/register.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct LogPass : public Pass {
+ LogPass() : Pass("log", "print text and log files") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" log string\n");
+ log("\n");
+ log("Print the given string to the screen and/or the log file. This is useful for TCL\n");
+ log("scripts, because the TCL command \"puts\" only goes to stdout but not to\n");
+ log("logfiles.\n");
+ log("\n");
+ log(" -stdout\n");
+ log(" Print the output to stdout too. This is useful when all Yosys is executed\n");
+ log(" with a script and the -q (quiet operation) argument to notify the user.\n");
+ log("\n");
+ log(" -stderr\n");
+ log(" Print the output to stderr too.\n");
+ log("\n");
+ log(" -nolog\n");
+ log(" Don't use the internal log() command. Use either -stdout or -stderr,\n");
+ log(" otherwise no output will be generated at all.\n");
+ log("\n");
+ log(" -n\n");
+ log(" do not append a newline\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design*)
+ {
+ size_t argidx;
+ bool to_stdout = false;
+ bool to_stderr = false;
+ bool to_log = true;
+ bool newline = true;
+ std::string text;
+
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-stdout") to_stdout = true;
+ else if (args[argidx] == "-stderr") to_stderr = true;
+ else if (args[argidx] == "-nolog") to_log = false;
+ else if (args[argidx] == "-n") newline = false;
+ else break;
+ }
+ for (; argidx < args.size(); argidx++)
+ text += args[argidx] + ' ';
+ if (!text.empty()) text.resize(text.size()-1);
+
+ if (to_stdout) fprintf(stdout, (newline ? "%s\n" : "%s"), text.c_str());
+ if (to_stderr) fprintf(stderr, (newline ? "%s\n" : "%s"), text.c_str());
+ if (to_log) log ( (newline ? "%s\n" : "%s"), text.c_str());
+ }
+} LogPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc
new file mode 100644
index 00000000..828c671d
--- /dev/null
+++ b/passes/cmds/plugin.cc
@@ -0,0 +1,131 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2014 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"
+
+#ifdef YOSYS_ENABLE_PLUGINS
+# include <dlfcn.h>
+#endif
+
+YOSYS_NAMESPACE_BEGIN
+
+std::map<std::string, void*> loaded_plugins;
+std::map<std::string, std::string> loaded_plugin_aliases;
+
+#ifdef YOSYS_ENABLE_PLUGINS
+void load_plugin(std::string filename, std::vector<std::string> aliases)
+{
+ std::string orig_filename = filename;
+
+ if (filename.find('/') == std::string::npos)
+ filename = "./" + filename;
+
+ if (!loaded_plugins.count(filename)) {
+ void *hdl = dlopen(filename.c_str(), RTLD_LAZY|RTLD_LOCAL);
+ if (hdl == NULL && orig_filename.find('/') == std::string::npos)
+ hdl = dlopen((proc_share_dirname() + "plugins/" + orig_filename + ".so").c_str(), RTLD_LAZY|RTLD_LOCAL);
+ if (hdl == NULL)
+ log_cmd_error("Can't load module `%s': %s\n", filename.c_str(), dlerror());
+ loaded_plugins[orig_filename] = hdl;
+ Pass::init_register();
+ }
+
+ for (auto &alias : aliases)
+ loaded_plugin_aliases[alias] = orig_filename;
+}
+#else
+void load_plugin(std::string, std::vector<std::string>)
+{
+ log_error("This version of yosys is built without plugin support.\n");
+}
+#endif
+
+struct PluginPass : public Pass {
+ PluginPass() : Pass("plugin", "load and list loaded plugins") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" plugin [options]\n");
+ log("\n");
+ log("Load and list loaded plugins.\n");
+ log("\n");
+ log(" -i <plugin_filename>\n");
+ log(" Load (install) the specified plugin.\n");
+ log("\n");
+ log(" -a <alias_name>\n");
+ log(" Register the specified alias name for the loaded plugin\n");
+ log("\n");
+ log(" -l\n");
+ log(" List loaded plugins\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::string plugin_filename;
+ std::vector<std::string> plugin_aliases;
+ bool list_mode = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if ((args[argidx] == "-i") && argidx+1 < args.size() && plugin_filename.empty()) {
+ plugin_filename = args[++argidx];
+ continue;
+ }
+ if ((args[argidx] == "-a") && argidx+1 < args.size()) {
+ plugin_aliases.push_back(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-l") {
+ list_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design, false);
+
+ if (!plugin_filename.empty())
+ load_plugin(plugin_filename, plugin_aliases);
+
+ if (list_mode)
+ {
+ log("\n");
+ if (loaded_plugins.empty())
+ log("No plugins loaded.\n");
+ else
+ log("Loaded plugins:\n");
+
+ for (auto &it : loaded_plugins)
+ log(" %s\n", it.first.c_str());
+
+ if (!loaded_plugin_aliases.empty()) {
+ log("\n");
+ int max_alias_len = 1;
+ for (auto &it : loaded_plugin_aliases)
+ max_alias_len = max(max_alias_len, GetSize(it.first));
+ for (auto &it : loaded_plugin_aliases)
+ log("Alias: %-*s %s\n", max_alias_len, it.first.c_str(), it.second.c_str());
+ }
+ }
+ }
+} PluginPass;
+
+YOSYS_NAMESPACE_END
+
diff --git a/passes/cmds/qwp.cc b/passes/cmds/qwp.cc
new file mode 100644
index 00000000..1b800b6d
--- /dev/null
+++ b/passes/cmds/qwp.cc
@@ -0,0 +1,870 @@
+/*
+ * 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"
+
+#undef LOG_MATRICES
+#undef PYPLOT_EDGES
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+static uint32_t xorshift32_state;
+
+static double xorshift32()
+{
+ xorshift32_state ^= xorshift32_state << 13;
+ xorshift32_state ^= xorshift32_state >> 17;
+ xorshift32_state ^= xorshift32_state << 5;
+ return (xorshift32_state % 1000000) / 1e6;
+}
+
+struct QwpConfig
+{
+ bool ltr;
+ bool alpha;
+ bool verbose;
+ double grid;
+
+ std::ofstream dump_file;
+
+ QwpConfig() {
+ ltr = false;
+ alpha = false;
+ verbose = false;
+ grid = 1.0 / 16;
+ }
+};
+
+struct QwpWorker
+{
+ QwpConfig &config;
+ Module *module;
+ char direction;
+
+ struct Node {
+ Cell *cell;
+ bool tied, alt_tied;
+
+ // pos = position in current direction
+ // alt_pos = position in the other direction
+ double pos, alt_pos;
+
+ Node() {
+ cell = nullptr;
+ tied = false;
+ pos = xorshift32();
+ alt_tied = false;
+ alt_pos = xorshift32();
+ }
+
+ void tie(double v) {
+ tied = true;
+ pos = v;
+ }
+
+ void alt_tie(double v) {
+ alt_tied = true;
+ alt_pos = v;
+ }
+
+ void swap_alt() {
+ std::swap(tied, alt_tied);
+ std::swap(pos, alt_pos);
+ }
+
+ void proj_left(double midpos) {
+ cell = nullptr;
+ tie(pos > midpos ? midpos : pos);
+ }
+
+ void proj_right(double midpos) {
+ cell = nullptr;
+ tie(pos < midpos ? midpos : pos);
+ }
+ };
+
+ vector<Node> nodes;
+ dict<pair<int, int>, double> edges;
+ dict<Cell*, int> cell_to_node;
+
+ // worker state variables
+ double midpos;
+ double radius;
+ double alt_midpos;
+ double alt_radius;
+
+ QwpWorker(QwpConfig &config, Module *module, char direction = 'x') : config(config), module(module), direction(direction)
+ {
+ log_assert(direction == 'x' || direction == 'y');
+ }
+
+ void load_module()
+ {
+ log_assert(direction == 'x');
+
+ SigMap sigmap(module);
+ dict<SigBit, pool<int>> bits_to_nodes;
+
+ if (config.ltr || config.alpha)
+ {
+ dict<Wire*, double> alpha_inputs, alpha_outputs;
+
+ if (config.alpha)
+ {
+ dict<string, Wire*> alpha_order;
+
+ for (auto wire : module->wires()) {
+ if (wire->port_input || wire->port_output)
+ alpha_order[wire->name.str()] = wire;
+ }
+
+ alpha_order.sort();
+
+ for (auto &it : alpha_order) {
+ if (it.second->port_input) {
+ int idx = GetSize(alpha_inputs);
+ alpha_inputs[it.second] = idx + 0.5;
+ }
+ if (it.second->port_output) {
+ int idx = GetSize(alpha_outputs);
+ alpha_outputs[it.second] = idx + 0.5;
+ }
+ }
+ }
+
+ for (auto wire : module->wires())
+ {
+ if (!wire->port_input && !wire->port_output)
+ continue;
+
+ int idx = GetSize(nodes);
+ nodes.push_back(Node());
+
+ if (config.ltr) {
+ if (wire->port_input)
+ nodes[idx].tie(0.0);
+ else
+ nodes[idx].tie(1.0);
+ }
+
+ if (config.alpha) {
+ if (wire->port_input)
+ nodes[idx].alt_tie(alpha_inputs.at(wire) / GetSize(alpha_inputs));
+ else
+ nodes[idx].alt_tie(alpha_outputs.at(wire) / GetSize(alpha_outputs));
+ }
+
+ for (auto bit : sigmap(wire))
+ bits_to_nodes[bit].insert(idx);
+ }
+ }
+
+ for (auto cell : module->selected_cells())
+ {
+ log_assert(cell_to_node.count(cell) == 0);
+ int idx = GetSize(nodes);
+ nodes.push_back(Node());
+
+ cell_to_node[cell] = GetSize(nodes);
+ nodes[idx].cell = cell;
+
+ for (auto &conn : cell->connections())
+ for (auto bit : sigmap(conn.second))
+ bits_to_nodes[bit].insert(idx);
+ }
+
+ for (auto &it : bits_to_nodes)
+ {
+ if (GetSize(it.second) > 100)
+ continue;
+
+ for (int idx1 : it.second)
+ for (int idx2 : it.second)
+ if (idx1 < idx2)
+ edges[pair<int, int>(idx1, idx2)] += 1.0 / GetSize(it.second);
+ }
+ }
+
+ void solve(bool alt_mode = false)
+ {
+ // A := constraint_matrix
+ // y := constraint_rhs_vector
+ //
+ // AA = A' * A
+ // Ay = A' * y
+ //
+ // M := [AA Ay]
+
+ if (config.verbose)
+ log("> System size: %d^2\n", GetSize(nodes));
+
+ // Row major order
+ int N = GetSize(nodes), N1 = N+1;
+ vector<double> M(N * N1);
+
+ if (config.verbose)
+ log("> Edge constraints: %d\n", GetSize(edges));
+
+ // Edge constraints:
+ // A[i,:] := [ 0 0 .... 0 weight 0 ... 0 -weight 0 ... 0 0], y[i] := 0
+ //
+ // i.e. nonzero columns in A[i,:] at the two node indices.
+ for (auto &edge : edges)
+ {
+ int idx1 = edge.first.first;
+ int idx2 = edge.first.second;
+ double weight = edge.second * (1.0 + xorshift32() * 1e-3);
+
+ M[idx1 + idx1*N1] += weight * weight;
+ M[idx2 + idx2*N1] += weight * weight;
+
+ M[idx1 + idx2*N1] += -weight * weight;
+ M[idx2 + idx1*N1] += -weight * weight;
+ }
+
+ if (config.verbose)
+ log("> Node constraints: %d\n", GetSize(nodes));
+
+ // Node constraints:
+ // A[i,:] := [ 0 0 .... 0 weight 0 ... 0 0], y[i] := weight * pos
+ //
+ // i.e. nonzero column in A[i,:] at the node index
+ //
+ // "tied" nodes have a large weight, pinning them in position. Untied
+ // nodes have a small weight, giving then a tiny preference to stay at
+ // the current position, making sure that AA never is singular.
+ for (int idx = 0; idx < GetSize(nodes); idx++)
+ {
+ auto &node = nodes[idx];
+ double rhs = (alt_mode ? node.alt_pos : node.pos);
+
+ double weight = 1e-3;
+ if (alt_mode ? node.alt_tied : node.tied)
+ weight = 1e3;
+ weight *= (1.0 + xorshift32() * 1e-3);
+
+ M[idx + idx*N1] += weight * weight;
+ M[N + idx*N1] += rhs * weight * weight;
+ }
+
+#ifdef LOG_MATRICES
+ log("\n");
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N+1; j++)
+ log(" %10.2e", M[(N+1)*i + j]);
+ log("\n");
+ }
+#endif
+
+ if (config.verbose)
+ log("> Solving\n");
+
+ // Solve "AA*x = Ay"
+ // (least squares fit for "A*x = y")
+ //
+ // Using gaussian elimination to get M := [Id x]
+
+ vector<int> pivot_cache;
+ vector<int> queue;
+
+ for (int i = 0; i < N; i++)
+ queue.push_back(i);
+
+ // gaussian elimination
+ for (int i = 0; i < N; i++)
+ {
+ if (config.verbose && ((i+1) % (N/15)) == 0)
+ log("> Solved %d%%: %d/%d\n", (100*(i+1))/N, i+1, N);
+
+ // find best row
+ int best_row = queue.front();
+ int best_row_queue_idx = 0;
+ double best_row_absval = 0;
+
+ for (int k = 0; k < GetSize(queue); k++) {
+ int row = queue[k];
+ double absval = fabs(M[i + row*N1]);
+ if (absval > best_row_absval) {
+ best_row = row;
+ best_row_queue_idx = k;
+ best_row_absval = absval;
+ }
+ }
+
+ int row = best_row;
+ pivot_cache.push_back(row);
+
+ queue[best_row_queue_idx] = queue.back();
+ queue.pop_back();
+
+ // normalize row
+ for (int k = i+1; k < N1; k++)
+ M[k + row*N1] /= M[i + row*N1];
+ M[i + row*N1] = 1.0;
+
+ // elimination
+ for (int other_row : queue) {
+ double d = M[i + other_row*N1];
+ for (int k = i+1; k < N1; k++)
+ M[k + other_row*N1] -= d*M[k + row*N1];
+ M[i + other_row*N1] = 0.0;
+ }
+ }
+
+ if (config.verbose)
+ log("> Solved\n");
+
+ log_assert(queue.empty());
+ log_assert(GetSize(pivot_cache) == N);
+
+ // back substitution
+ for (int i = N-1; i >= 0; i--)
+ for (int j = i+1; j < N; j++)
+ {
+ int row = pivot_cache[i];
+ int other_row = pivot_cache[j];
+ M[N + row*N1] -= M[j + row*N1] * M[N + other_row*N1];
+ M[j + row*N1] = 0.0;
+ }
+
+#ifdef LOG_MATRICES
+ log("\n");
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N+1; j++)
+ log(" %10.2e", M[(N+1)*i + j]);
+ log("\n");
+ }
+#endif
+
+ if (config.verbose)
+ log("> Update nodes\n");
+
+ // update node positions
+ for (int i = 0; i < N; i++)
+ {
+ double v = M[(N+1)*i + N];
+ double c = alt_mode ? alt_midpos : midpos;
+ double r = alt_mode ? alt_radius : radius;
+
+ if (std::isfinite(v)) {
+ v = min(v, c+r);
+ v = max(v, c-r);
+ } else {
+ v = c;
+ }
+
+ if (alt_mode) {
+ if (!nodes[i].alt_tied)
+ nodes[i].alt_pos = v;
+ } else {
+ if (!nodes[i].tied)
+ nodes[i].pos = v;
+ }
+ }
+ }
+
+ void log_cell_coordinates(int indent, bool log_all_nodes = false)
+ {
+ for (auto &node : nodes)
+ {
+ if (node.cell == nullptr && !log_all_nodes)
+ continue;
+
+ for (int i = 0; i < indent; i++)
+ log(" ");
+
+ if (direction == 'x')
+ log("X=%.2f, Y=%.2f", node.pos, node.alt_pos);
+ else
+ log("X=%.2f, Y=%.2f", node.alt_pos, node.pos);
+
+ if (node.tied)
+ log(" [%c-tied]", direction);
+
+ if (node.alt_tied)
+ log(" [%c-tied]", direction == 'x' ? 'y' : 'x');
+
+ if (node.cell != nullptr)
+ log(" %s (%s)", log_id(node.cell), log_id(node.cell->type));
+ else
+ log(" (none)");
+
+ log("\n");
+ }
+ }
+
+ void dump_svg(const pool<int> *green_nodes = nullptr, double median = -1)
+ {
+ double x_center = direction == 'x' ? midpos : alt_midpos;
+ double y_center = direction == 'y' ? midpos : alt_midpos;
+
+ double x_radius = direction == 'x' ? radius : alt_radius;
+ double y_radius = direction == 'y' ? radius : alt_radius;
+
+ config.dump_file << stringf("<svg height=\"240\" width=\"470\">\n");
+ config.dump_file << stringf("<rect x=\"0\" y=\"0\" width=\"470\" height=\"240\" style=\"fill:rgb(250,250,200);\" />\n");
+ config.dump_file << stringf("<rect x=\"20\" y=\"20\" width=\"200\" height=\"200\" style=\"fill:rgb(200,200,200);\" />\n");
+ config.dump_file << stringf("<rect x=\"250\" y=\"20\" width=\"200\" height=\"200\" style=\"fill:rgb(200,200,200);\" />\n");
+
+ double win_x = 250 + 200 * (direction == 'x' ? midpos - radius : alt_midpos - alt_radius);
+ double win_y = 20 + 200 * (direction == 'y' ? midpos - radius : alt_midpos - alt_radius);
+
+ double win_w = 200 * (direction == 'x' ? 2*radius : 2*alt_radius);
+ double win_h = 200 * (direction == 'y' ? 2*radius : 2*alt_radius);
+
+ config.dump_file << stringf("<rect x=\"%.2f\" y=\"%.2f\" width=\"%.2f\" height=\"%.2f\" "
+ "style=\"stroke:rgb(0,0,0);stroke-width:1;fill:none\" />\n", win_x, win_y, win_w, win_h);
+
+ if (median >= 0)
+ {
+ double x1 = 20.0, x2 = 220.0, y1 = 20.0, y2 = 220.0;
+
+ if (direction == 'x')
+ x1 = x2 = 120 + 100 * (median - x_center) / x_radius;
+ else
+ y1 = y2 = 120 + 100 * (median - y_center) / y_radius;
+
+ config.dump_file << stringf("<line x1=\"%.2f\" y1=\"%.2f\" x2=\"%.2f\" y2=\"%.2f\" "
+ "style=\"stroke:rgb(150,0,150);stroke-width:1\" />\n", x1, y1, x2, y2);
+ }
+
+ for (auto &edge : edges)
+ {
+ auto &node1 = nodes[edge.first.first];
+ auto &node2 = nodes[edge.first.second];
+
+ double x1 = direction == 'x' ? node1.pos : node1.alt_pos;
+ double y1 = direction == 'y' ? node1.pos : node1.alt_pos;
+
+ double x2 = direction == 'x' ? node2.pos : node2.alt_pos;
+ double y2 = direction == 'y' ? node2.pos : node2.alt_pos;
+
+ x1 = 120 + 100 * (x1 - x_center) / x_radius;
+ y1 = 120 + 100 * (y1 - y_center) / y_radius;
+
+ x2 = 120 + 100 * (x2 - x_center) / x_radius;
+ y2 = 120 + 100 * (y2 - y_center) / y_radius;
+
+ config.dump_file << stringf("<line x1=\"%.2f\" y1=\"%.2f\" x2=\"%.2f\" y2=\"%.2f\" "
+ "style=\"stroke:rgb(0,0,0);stroke-width:1\" />\n", x1, y1, x2, y2);
+ }
+
+ for (int i = 0; i < GetSize(nodes); i++)
+ {
+ auto &node = nodes[i];
+
+ double x = direction == 'x' ? node.pos : node.alt_pos;
+ double y = direction == 'y' ? node.pos : node.alt_pos;
+
+ x = 120 + 100 * (x - x_center) / x_radius;
+ y = 120 + 100 * (y - y_center) / y_radius;
+
+ const char *color = node.cell == nullptr ? "blue" : "red";
+
+ if (green_nodes != nullptr && green_nodes->count(i))
+ color = "green";
+
+ config.dump_file << stringf("<circle cx=\"%.2f\" cy=\"%.2f\" r=\"3\" fill=\"%s\"/>\n", x, y, color);
+ }
+
+ config.dump_file << stringf("</svg>\n");
+ }
+
+ void run_worker(int indent)
+ {
+ int count_cells = 0;
+
+ for (auto &node : nodes)
+ if (node.cell != nullptr)
+ count_cells++;
+
+ for (int i = 0; i < indent; i++)
+ log(" ");
+
+ string range_str;
+
+ if (direction == 'x')
+ range_str = stringf("X=%.2f:%.2f, Y=%.2f:%.2f",
+ midpos - radius, midpos + radius,
+ alt_midpos - alt_radius, alt_midpos + alt_radius);
+ else
+ range_str = stringf("X=%.2f:%.2f, Y=%.2f:%.2f",
+ alt_midpos - alt_radius, alt_midpos + alt_radius,
+ midpos - radius, midpos + radius);
+
+ log("%c-qwp on %s with %d cells, %d nodes, and %d edges.\n", direction,
+ range_str.c_str(), count_cells, GetSize(nodes), GetSize(edges));
+
+ solve();
+ solve(true);
+
+ // detect median position and check for break condition
+
+ vector<pair<double, int>> sorted_pos;
+ for (int i = 0; i < GetSize(nodes); i++)
+ if (nodes[i].cell != nullptr)
+ sorted_pos.push_back(pair<double, int>(nodes[i].pos, i));
+
+ std::sort(sorted_pos.begin(), sorted_pos.end());
+ int median_sidx = GetSize(sorted_pos)/2;
+ double median = sorted_pos[median_sidx].first;
+
+ double left_scale = radius / (median - (midpos - radius));
+ double right_scale = radius / ((midpos + radius) - median);
+
+ if (config.dump_file.is_open())
+ {
+ config.dump_file << stringf("<h4>LSQ %c-Solution for %s:</h4>\n", direction, range_str.c_str());
+
+ pool<int> green_nodes;
+ for (int i = 0; i < median_sidx; i++)
+ green_nodes.insert(sorted_pos[i].second);
+
+ dump_svg(&green_nodes, median);
+ }
+
+ for (auto &node : nodes)
+ {
+ double rel_pos = node.pos - median;
+ if (rel_pos < 0) {
+ node.pos = midpos + left_scale*rel_pos;
+ if (std::isfinite(node.pos)) {
+ node.pos = min(node.pos, midpos);
+ node.pos = max(node.pos, midpos - radius);
+ } else
+ node.pos = midpos - radius/2;
+ } else {
+ node.pos = midpos + right_scale*rel_pos;
+ if (std::isfinite(node.pos)) {
+ node.pos = max(node.pos, midpos);
+ node.pos = min(node.pos, midpos + radius);
+ } else
+ node.pos = midpos + radius/2;
+ }
+ }
+
+ if (GetSize(sorted_pos) < 2 || (2*radius <= config.grid && 2*alt_radius <= config.grid)) {
+ log_cell_coordinates(indent + 1);
+ return;
+ }
+
+ // create child workers
+
+ char child_direction = direction == 'x' ? 'y' : 'x';
+
+ QwpWorker left_worker(config, module, child_direction);
+ QwpWorker right_worker(config, module, child_direction);
+
+ // duplicate nodes into child workers
+
+ dict<int, int> left_nodes, right_nodes;
+
+ for (int k = 0; k < GetSize(sorted_pos); k++)
+ {
+ int i = sorted_pos[k].second;
+
+ if (k < median_sidx) {
+ left_nodes[i] = GetSize(left_worker.nodes);
+ left_worker.nodes.push_back(nodes[i]);
+ if (left_worker.nodes.back().pos > midpos)
+ left_worker.nodes.back().pos = midpos;
+ left_worker.nodes.back().swap_alt();
+ } else {
+ right_nodes[i] = GetSize(right_worker.nodes);
+ right_worker.nodes.push_back(nodes[i]);
+ if (right_worker.nodes.back().pos < midpos)
+ right_worker.nodes.back().pos = midpos;
+ right_worker.nodes.back().swap_alt();
+ }
+ }
+
+ // duplicate edges into child workers, project nodes as needed
+
+ for (auto &edge : edges)
+ {
+ int idx1 = edge.first.first;
+ int idx2 = edge.first.second;
+ double weight = edge.second;
+
+ if (nodes[idx1].cell == nullptr && nodes[idx2].cell == nullptr)
+ continue;
+
+ int left_idx1 = left_nodes.count(idx1) ? left_nodes.at(idx1) : -1;
+ int left_idx2 = left_nodes.count(idx2) ? left_nodes.at(idx2) : -1;
+
+ int right_idx1 = right_nodes.count(idx1) ? right_nodes.at(idx1) : -1;
+ int right_idx2 = right_nodes.count(idx2) ? right_nodes.at(idx2) : -1;
+
+ if (left_idx1 >= 0 && left_worker.nodes[left_idx1].cell && left_idx2 < 0) {
+ left_idx2 = left_nodes[idx2] = GetSize(left_worker.nodes);
+ left_worker.nodes.push_back(nodes[idx2]);
+ left_worker.nodes.back().proj_left(midpos);
+ left_worker.nodes.back().swap_alt();
+ } else
+ if (left_idx2 >= 0 && left_worker.nodes[left_idx2].cell && left_idx1 < 0) {
+ left_idx1 = left_nodes[idx1] = GetSize(left_worker.nodes);
+ left_worker.nodes.push_back(nodes[idx1]);
+ left_worker.nodes.back().proj_left(midpos);
+ left_worker.nodes.back().swap_alt();
+ }
+
+ if (right_idx1 >= 0 && right_worker.nodes[right_idx1].cell && right_idx2 < 0) {
+ right_idx2 = right_nodes[idx2] = GetSize(right_worker.nodes);
+ right_worker.nodes.push_back(nodes[idx2]);
+ right_worker.nodes.back().proj_right(midpos);
+ right_worker.nodes.back().swap_alt();
+ } else
+ if (right_idx2 >= 0 && right_worker.nodes[right_idx2].cell && right_idx1 < 0) {
+ right_idx1 = right_nodes[idx1] = GetSize(right_worker.nodes);
+ right_worker.nodes.push_back(nodes[idx1]);
+ right_worker.nodes.back().proj_right(midpos);
+ right_worker.nodes.back().swap_alt();
+ }
+
+ if (left_idx1 >= 0 && left_idx2 >= 0)
+ left_worker.edges[pair<int, int>(left_idx1, left_idx2)] += weight;
+
+ if (right_idx1 >= 0 && right_idx2 >= 0)
+ right_worker.edges[pair<int, int>(right_idx1, right_idx2)] += weight;
+ }
+
+ // run child workers
+
+ left_worker.midpos = right_worker.midpos = alt_midpos;
+ left_worker.radius = right_worker.radius = alt_radius;
+
+ left_worker.alt_midpos = midpos - radius/2;
+ right_worker.alt_midpos = midpos + radius/2;
+ left_worker.alt_radius = right_worker.alt_radius = radius/2;
+
+ left_worker.run_worker(indent+1);
+ right_worker.run_worker(indent+1);
+
+ // re-integrate results
+
+ for (auto &it : left_nodes)
+ if (left_worker.nodes[it.second].cell != nullptr) {
+ nodes[it.first].pos = left_worker.nodes[it.second].alt_pos;
+ nodes[it.first].alt_pos = left_worker.nodes[it.second].pos;
+ }
+
+ for (auto &it : right_nodes)
+ if (right_worker.nodes[it.second].cell != nullptr) {
+ nodes[it.first].pos = right_worker.nodes[it.second].alt_pos;
+ nodes[it.first].alt_pos = right_worker.nodes[it.second].pos;
+ }
+
+ if (config.dump_file.is_open()) {
+ config.dump_file << stringf("<h4>Final %c-Solution for %s:</h4>\n", direction, range_str.c_str());
+ dump_svg();
+ }
+ }
+
+ void histogram(const vector<double> &values)
+ {
+ if (values.empty()) {
+ log("no data\n");
+ return;
+ }
+
+ double min_value = values.front();
+ double max_value = values.front();
+
+ for (auto &v : values) {
+ min_value = min(min_value, v);
+ max_value = max(max_value, v);
+ }
+
+ if (fabs(max_value - min_value) < 0.001) {
+ log("all values in range %f .. %f\n", min_value, max_value);
+ return;
+ }
+
+ vector<int> buckets(60);
+ int max_bucket_val = 0;
+
+ for (auto &v : values) {
+ int idx = min(int(GetSize(buckets) * (v - min_value) / (max_value - min_value)), GetSize(buckets)-1);
+ max_bucket_val = max(max_bucket_val, ++buckets.at(idx));
+ }
+
+ for (int i = 4; i >= 0; i--) {
+ for (int k = 0; k < GetSize(buckets); k++) {
+ int v = 10 * buckets[k] / max_bucket_val;
+ if (v >= 2*i+1)
+ log(v == 2*i+1 ? "." : ":");
+ else
+ log(i == 0 ? (buckets[k] > 0 ? "," : "_") : " ");
+ }
+ log("\n");
+ }
+ log("%-30f%30f\n", min_value, max_value);
+ }
+
+ void run()
+ {
+ log("\n");
+ log("Running qwp on module %s..\n", log_id(module));
+
+ if (config.dump_file.is_open())
+ config.dump_file << stringf("<h3>QWP protocol for module %s:</h3>\n", log_id(module));
+
+ load_module();
+
+ midpos = 0.5;
+ radius = 0.5;
+ alt_midpos = 0.5;
+ alt_radius = 0.5;
+ run_worker(1);
+
+ for (auto &node : nodes)
+ if (node.cell != nullptr)
+ node.cell->attributes["\\qwp_position"] = stringf("%f %f", node.pos, node.alt_pos);
+
+ vector<double> edge_lengths;
+ vector<double> weighted_edge_lengths;
+
+ double total_edge_length = 0;
+ double total_weighted_edge_length = 0;
+
+ for (auto &edge : edges)
+ {
+ auto &node1 = nodes[edge.first.first];
+ auto &node2 = nodes[edge.first.second];
+
+ double distance = sqrt(pow(node1.pos - node2.pos, 2) + pow(node1.alt_pos - node2.alt_pos, 2));
+ double weighted_distance = distance * edge.second;
+
+ edge_lengths.push_back(distance);
+ weighted_edge_lengths.push_back(weighted_distance);
+
+ total_edge_length += distance;
+ total_weighted_edge_length += weighted_distance;
+ }
+
+ log("\n");
+ log("Summary for module %s:\n", log_id(module));
+ log("Number of edges: %d\n", GetSize(edges));
+ log("Total edge length: %f\n", total_edge_length);
+ log("Total weighted edge length: %f\n", total_weighted_edge_length);
+
+ log("\n");
+ log("Histogram over edge lengths:\n");
+ histogram(edge_lengths);
+
+ log("\n");
+ log("Histogram over weighted edge lengths:\n");
+ histogram(weighted_edge_lengths);
+ }
+};
+
+struct QwpPass : public Pass {
+ QwpPass() : Pass("qwp", "quadratic wirelength placer") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" qwp [options] [selection]\n");
+ log("\n");
+ log("This command runs quadratic wirelength placement on the selected modules and\n");
+ log("annotates the cells in the design with 'qwp_position' attributes.\n");
+ log("\n");
+ log(" -ltr\n");
+ log(" Add left-to-right constraints: constrain all inputs on the left border\n");
+ log(" outputs to the right border.\n");
+ log("\n");
+ log(" -alpha\n");
+ log(" Add constraints for inputs/outputs to be placed in alphanumerical\n");
+ log(" order along the y-axis (top-to-bottom).\n");
+ log("\n");
+ log(" -grid N\n");
+ log(" Number of grid divisions in x- and y-direction. (default=16)\n");
+ log("\n");
+ log(" -dump <html_file_name>\n");
+ log(" Dump a protocol of the placement algorithm to the html file.\n");
+ log("\n");
+ log(" -v\n");
+ log(" Verbose solver output for profiling or debugging\n");
+ log("\n");
+ log("Note: This implementation of a quadratic wirelength placer uses exact\n");
+ log("dense matrix operations. It is only a toy-placer for small circuits.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ QwpConfig config;
+ xorshift32_state = 123456789;
+
+ log_header(design, "Executing QWP pass (quadratic wirelength placer).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-ltr") {
+ config.ltr = true;
+ continue;
+ }
+ if (args[argidx] == "-alpha") {
+ config.alpha = true;
+ continue;
+ }
+ if (args[argidx] == "-v") {
+ config.verbose = true;
+ continue;
+ }
+ if (args[argidx] == "-grid" && argidx+1 < args.size()) {
+ config.grid = 1.0 / atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-dump" && argidx+1 < args.size()) {
+ config.dump_file.open(args[++argidx], std::ofstream::trunc);
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ QwpWorker worker(config, module);
+ worker.run();
+
+#ifdef PYPLOT_EDGES
+ log("\n");
+ log("plt.figure(figsize=(10, 10));\n");
+
+ for (auto &edge : worker.edges) {
+ log("plt.plot([%.2f, %.2f], [%.2f, %.2f], \"r-\");\n",
+ worker.nodes[edge.first.first].pos,
+ worker.nodes[edge.first.second].pos,
+ worker.nodes[edge.first.first].alt_pos,
+ worker.nodes[edge.first.second].alt_pos);
+ }
+
+ for (auto &node : worker.nodes) {
+ const char *style = node.cell != nullptr ? "ko" : "ks";
+ log("plt.plot([%.2f], [%.2f], \"%s\");\n", node.pos, node.alt_pos, style);
+ }
+#endif
+ }
+ }
+} QwpPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc
new file mode 100644
index 00000000..6a002869
--- /dev/null
+++ b/passes/cmds/rename.cc
@@ -0,0 +1,232 @@
+/*
+ * 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/register.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+static void rename_in_module(RTLIL::Module *module, std::string from_name, std::string to_name)
+{
+ from_name = RTLIL::escape_id(from_name);
+ to_name = RTLIL::escape_id(to_name);
+
+ if (module->count_id(to_name))
+ log_cmd_error("There is already an object `%s' in module `%s'.\n", to_name.c_str(), module->name.c_str());
+
+ for (auto &it : module->wires_)
+ if (it.first == from_name) {
+ 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)
+ module->fixup_ports();
+ return;
+ }
+
+ for (auto &it : module->cells_)
+ if (it.first == from_name) {
+ 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;
+ }
+
+ log_cmd_error("Object `%s' not found!\n", from_name.c_str());
+}
+
+struct RenamePass : public Pass {
+ RenamePass() : Pass("rename", "rename object in the design") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" rename old_name new_name\n");
+ log("\n");
+ log("Rename the specified object. Note that selection patterns are not supported\n");
+ log("by this command.\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");
+ log("names. The -pattern option can be used to set the pattern for the new names.\n");
+ log("The character %% in the pattern is replaced with a integer number. The default\n");
+ log("pattern is '_%%_'.\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(" rename -top new_name\n");
+ log("\n");
+ log("Rename top module.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::string pattern_prefix = "_", pattern_suffix = "_";
+ bool flag_enumerate = false;
+ bool flag_hide = false;
+ bool flag_top = false;
+ bool got_mode = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-enumerate" && !got_mode) {
+ flag_enumerate = true;
+ got_mode = true;
+ continue;
+ }
+ if (arg == "-hide" && !got_mode) {
+ flag_hide = true;
+ got_mode = true;
+ continue;
+ }
+ if (arg == "-top" && !got_mode) {
+ flag_top = true;
+ got_mode = true;
+ continue;
+ }
+ if (arg == "-pattern" && argidx+1 < args.size() && args[argidx+1].find('%') != std::string::npos) {
+ int pos = args[++argidx].find('%');
+ pattern_prefix = args[argidx].substr(0, pos);
+ pattern_suffix = args[argidx].substr(pos+1);
+ continue;
+ }
+ break;
+ }
+
+ if (flag_enumerate)
+ {
+ 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))
+ do it.second->name = stringf("\\%s%d%s", pattern_prefix.c_str(), counter++, pattern_suffix.c_str());
+ while (module->count_id(it.second->name) > 0);
+ 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))
+ do it.second->name = stringf("\\%s%d%s", pattern_prefix.c_str(), counter++, pattern_suffix.c_str());
+ while (module->count_id(it.second->name) > 0);
+ new_cells[it.second->name] = it.second;
+ }
+ module->cells_.swap(new_cells);
+ }
+ }
+ else
+ if (flag_hide)
+ {
+ extra_args(args, argidx, design);
+
+ for (auto &mod : design->modules_)
+ {
+ RTLIL::Module *module = mod.second;
+ if (!design->selected(module))
+ continue;
+
+ dict<RTLIL::IdString, RTLIL::Wire*> new_wires;
+ for (auto &it : module->wires_) {
+ if (design->selected(module, it.second))
+ if (it.first[0] == '\\' && it.second->port_id == 0)
+ it.second->name = NEW_ID;
+ 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 (design->selected(module, it.second))
+ if (it.first[0] == '\\')
+ it.second->name = NEW_ID;
+ new_cells[it.second->name] = it.second;
+ }
+ module->cells_.swap(new_cells);
+ }
+ }
+ else
+ if (flag_top)
+ {
+ if (argidx+1 != args.size())
+ log_cmd_error("Invalid number of arguments!\n");
+
+ IdString new_name = RTLIL::escape_id(args[argidx]);
+ RTLIL::Module *module = design->top_module();
+
+ if (module == NULL)
+ log_cmd_error("No top module found!\n");
+
+ log("Renaming module %s to %s.\n", log_id(module), log_id(new_name));
+ design->rename(module, new_name);
+ }
+ else
+ {
+ if (argidx+2 != args.size())
+ log_cmd_error("Invalid number of arguments!\n");
+
+ std::string from_name = args[argidx++];
+ std::string to_name = args[argidx++];
+
+ 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);
+ }
+ else
+ {
+ for (auto &mod : design->modules_) {
+ if (mod.first == from_name || RTLIL::unescape_id(mod.first) == from_name) {
+ to_name = RTLIL::escape_id(to_name);
+ log("Renaming module %s to %s.\n", mod.first.c_str(), to_name.c_str());
+ RTLIL::Module *module = mod.second;
+ design->modules_.erase(module->name);
+ module->name = to_name;
+ design->modules_[module->name] = module;
+ goto rename_ok;
+ }
+ }
+
+ log_cmd_error("Object `%s' not found!\n", from_name.c_str());
+ rename_ok:;
+ }
+ }
+ }
+} RenamePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/scatter.cc b/passes/cmds/scatter.cc
new file mode 100644
index 00000000..f083e1f6
--- /dev/null
+++ b/passes/cmds/scatter.cc
@@ -0,0 +1,73 @@
+/*
+ * 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/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct ScatterPass : public Pass {
+ ScatterPass() : Pass("scatter", "add additional intermediate nets") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" scatter [selection]\n");
+ log("\n");
+ log("This command adds additional intermediate nets on all cell ports. This is used\n");
+ log("for testing the correct use of the SigMap helper in passes. If you don't know\n");
+ log("what this means: don't worry -- you only need this pass when testing your own\n");
+ log("extensions to Yosys.\n");
+ log("\n");
+ log("Use the opt_clean command to get rid of the additional nets.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ CellTypes ct(design);
+ extra_args(args, 1, design);
+
+ for (auto &mod_it : design->modules_)
+ {
+ if (!design->selected(mod_it.second))
+ continue;
+
+ for (auto &c : mod_it.second->cells_)
+ for (auto &p : c.second->connections_)
+ {
+ RTLIL::Wire *wire = mod_it.second->addWire(NEW_ID, p.second.size());
+
+ if (ct.cell_output(c.second->type, p.first)) {
+ RTLIL::SigSig sigsig(p.second, wire);
+ mod_it.second->connect(sigsig);
+ } else {
+ RTLIL::SigSig sigsig(wire, p.second);
+ mod_it.second->connect(sigsig);
+ }
+
+ p.second = wire;
+ }
+ }
+ }
+} ScatterPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc
new file mode 100644
index 00000000..bb6d7447
--- /dev/null
+++ b/passes/cmds/scc.cc
@@ -0,0 +1,344 @@
+/*
+ * 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.
+ *
+ */
+
+// [[CITE]] Tarjan's strongly connected components algorithm
+// Tarjan, R. E. (1972), "Depth-first search and linear graph algorithms", SIAM Journal on Computing 1 (2): 146-160, doi:10.1137/0201010
+// http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm
+
+#include "kernel/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <set>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct SccWorker
+{
+ RTLIL::Design *design;
+ RTLIL::Module *module;
+ SigMap sigmap;
+ CellTypes ct;
+
+ std::set<RTLIL::Cell*> workQueue;
+ std::map<RTLIL::Cell*, std::set<RTLIL::Cell*>> cellToNextCell;
+ std::map<RTLIL::Cell*, RTLIL::SigSpec> cellToPrevSig, cellToNextSig;
+
+ std::map<RTLIL::Cell*, std::pair<int, int>> cellLabels;
+ std::map<RTLIL::Cell*, int> cellDepth;
+ std::set<RTLIL::Cell*> cellsOnStack;
+ std::vector<RTLIL::Cell*> cellStack;
+ int labelCounter;
+
+ std::map<RTLIL::Cell*, int> cell2scc;
+ std::vector<std::set<RTLIL::Cell*>> sccList;
+
+ void run(RTLIL::Cell *cell, int depth, int maxDepth)
+ {
+ log_assert(workQueue.count(cell) > 0);
+
+ workQueue.erase(cell);
+ cellLabels[cell] = std::pair<int, int>(labelCounter, labelCounter);
+ labelCounter++;
+
+ cellsOnStack.insert(cell);
+ cellStack.push_back(cell);
+
+ if (maxDepth >= 0)
+ cellDepth[cell] = depth;
+
+ for (auto nextCell : cellToNextCell[cell])
+ if (cellLabels.count(nextCell) == 0) {
+ run(nextCell, depth+1, maxDepth);
+ cellLabels[cell].second = min(cellLabels[cell].second, cellLabels[nextCell].second);
+ } else
+ if (cellsOnStack.count(nextCell) > 0 && (maxDepth < 0 || cellDepth[nextCell] + maxDepth > depth)) {
+ cellLabels[cell].second = min(cellLabels[cell].second, cellLabels[nextCell].second);
+ }
+
+ if (cellLabels[cell].first == cellLabels[cell].second)
+ {
+ if (cellStack.back() == cell)
+ {
+ cellStack.pop_back();
+ cellsOnStack.erase(cell);
+ }
+ else
+ {
+ log("Found an SCC:");
+ std::set<RTLIL::Cell*> scc;
+ while (cellsOnStack.count(cell) > 0) {
+ RTLIL::Cell *c = cellStack.back();
+ cellStack.pop_back();
+ cellsOnStack.erase(c);
+ log(" %s", RTLIL::id2cstr(c->name));
+ cell2scc[c] = sccList.size();
+ scc.insert(c);
+ }
+ sccList.push_back(scc);
+ log("\n");
+ }
+ }
+ }
+
+ SccWorker(RTLIL::Design *design, RTLIL::Module *module, bool nofeedbackMode, bool allCellTypes, int maxDepth) :
+ design(design), module(module), sigmap(module)
+ {
+ if (module->processes.size() > 0) {
+ log("Skipping module %s as it contains processes (run 'proc' pass first).\n", module->name.c_str());
+ return;
+ }
+
+ if (allCellTypes) {
+ ct.setup(design);
+ } else {
+ ct.setup_internals();
+ ct.setup_stdcells();
+ }
+
+ SigPool selectedSignals;
+ SigSet<RTLIL::Cell*> sigToNextCells;
+
+ for (auto &it : module->wires_)
+ if (design->selected(module, it.second))
+ selectedSignals.add(sigmap(RTLIL::SigSpec(it.second)));
+
+ for (auto &it : module->cells_)
+ {
+ RTLIL::Cell *cell = it.second;
+
+ if (!design->selected(module, cell))
+ continue;
+
+ if (!allCellTypes && !ct.cell_known(cell->type))
+ continue;
+
+ workQueue.insert(cell);
+
+ RTLIL::SigSpec inputSignals, outputSignals;
+
+ for (auto &conn : cell->connections())
+ {
+ bool isInput = true, isOutput = true;
+
+ if (ct.cell_known(cell->type)) {
+ isInput = ct.cell_input(cell->type, conn.first);
+ isOutput = ct.cell_output(cell->type, conn.first);
+ }
+
+ RTLIL::SigSpec sig = selectedSignals.extract(sigmap(conn.second));
+ sig.sort_and_unify();
+
+ if (isInput)
+ inputSignals.append(sig);
+ if (isOutput)
+ outputSignals.append(sig);
+ }
+
+ inputSignals.sort_and_unify();
+ outputSignals.sort_and_unify();
+
+ cellToPrevSig[cell] = inputSignals;
+ cellToNextSig[cell] = outputSignals;
+ sigToNextCells.insert(inputSignals, cell);
+ }
+
+ for (auto cell : workQueue)
+ cellToNextCell[cell] = sigToNextCells.find(cellToNextSig[cell]);
+
+ labelCounter = 0;
+ cellLabels.clear();
+
+ while (workQueue.size() > 0)
+ {
+ RTLIL::Cell *cell = *workQueue.begin();
+ log_assert(cellStack.size() == 0);
+ cellDepth.clear();
+
+ if (!nofeedbackMode && cellToNextCell[cell].count(cell)) {
+ log("Found an SCC:");
+ std::set<RTLIL::Cell*> scc;
+ log(" %s", RTLIL::id2cstr(cell->name));
+ cell2scc[cell] = sccList.size();
+ scc.insert(cell);
+ sccList.push_back(scc);
+ log("\n");
+ }
+
+ run(cell, 0, maxDepth);
+ }
+
+ log("Found %d SCCs in module %s.\n", int(sccList.size()), RTLIL::id2cstr(module->name));
+ }
+
+ void select(RTLIL::Selection &sel)
+ {
+ for (int i = 0; i < int(sccList.size()); i++)
+ {
+ std::set<RTLIL::Cell*> &cells = sccList[i];
+ RTLIL::SigSpec prevsig, nextsig, sig;
+
+ for (auto cell : cells) {
+ sel.selected_members[module->name].insert(cell->name);
+ prevsig.append(cellToPrevSig[cell]);
+ nextsig.append(cellToNextSig[cell]);
+ }
+
+ prevsig.sort_and_unify();
+ nextsig.sort_and_unify();
+ sig = prevsig.extract(nextsig);
+
+ for (auto &chunk : sig.chunks())
+ if (chunk.wire != NULL)
+ sel.selected_members[module->name].insert(chunk.wire->name);
+ }
+ }
+};
+
+struct SccPass : public Pass {
+ SccPass() : Pass("scc", "detect strongly connected components (logic loops)") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" scc [options] [selection]\n");
+ log("\n");
+ log("This command identifies strongly connected components (aka logic loops) in the\n");
+ log("design.\n");
+ log("\n");
+ log(" -expect <num>\n");
+ log(" expect to find exactly <num> SSCs. A different number of SSCs will\n");
+ log(" produce an error.\n");
+ log("\n");
+ log(" -max_depth <num>\n");
+ log(" limit to loops not longer than the specified number of cells. This\n");
+ log(" can e.g. be useful in identifying small local loops in a module that\n");
+ log(" implements one large SCC.\n");
+ log("\n");
+ log(" -nofeedback\n");
+ log(" do not count cells that have their output fed back into one of their\n");
+ log(" inputs as single-cell scc.\n");
+ log("\n");
+ log(" -all_cell_types\n");
+ log(" Usually this command only considers internal non-memory cells. With\n");
+ log(" this option set, all cells are considered. For unknown cells all ports\n");
+ log(" are assumed to be bidirectional 'inout' ports.\n");
+ log("\n");
+ log(" -set_attr <name> <value>\n");
+ log(" -set_cell_attr <name> <value>\n");
+ log(" -set_wire_attr <name> <value>\n");
+ log(" set the specified attribute on all cells and/or wires that are part of\n");
+ log(" a logic loop. the special token {} in the value is replaced with a\n");
+ log(" unique identifier for the logic loop.\n");
+ log("\n");
+ log(" -select\n");
+ log(" replace the current selection with a selection of all cells and wires\n");
+ log(" that are part of a found logic loop\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::map<std::string, std::string> setCellAttr, setWireAttr;
+ bool allCellTypes = false;
+ bool selectMode = false;
+ bool nofeedbackMode = false;
+ int maxDepth = -1;
+ int expect = -1;
+
+ log_header(design, "Executing SCC pass (detecting logic loops).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-max_depth" && argidx+1 < args.size()) {
+ maxDepth = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-expect" && argidx+1 < args.size()) {
+ expect = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-nofeedback") {
+ nofeedbackMode = true;
+ continue;
+ }
+ if (args[argidx] == "-all_cell_types") {
+ allCellTypes = true;
+ continue;
+ }
+ if (args[argidx] == "-set_attr" && argidx+2 < args.size()) {
+ setCellAttr[args[argidx+1]] = args[argidx+2];
+ setWireAttr[args[argidx+1]] = args[argidx+2];
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx] == "-set_cell_attr" && argidx+2 < args.size()) {
+ setCellAttr[args[argidx+1]] = args[argidx+2];
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx] == "-set_wire_attr" && argidx+2 < args.size()) {
+ setWireAttr[args[argidx+1]] = args[argidx+2];
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx] == "-select") {
+ selectMode = true;
+ continue;
+ }
+ break;
+ }
+ int origSelectPos = design->selection_stack.size() - 1;
+ extra_args(args, argidx, design);
+
+ if (setCellAttr.size() > 0 || setWireAttr.size() > 0)
+ log_cmd_error("The -set*_attr options are not implemented at the moment!\n");
+
+ RTLIL::Selection newSelection(false);
+ int scc_counter = 0;
+
+ for (auto &mod_it : design->modules_)
+ if (design->selected(mod_it.second))
+ {
+ SccWorker worker(design, mod_it.second, nofeedbackMode, allCellTypes, maxDepth);
+ scc_counter += GetSize(worker.sccList);
+
+ if (selectMode)
+ worker.select(newSelection);
+ }
+
+ if (expect >= 0) {
+ if (scc_counter == expect)
+ log("Found and expected %d SCCs.\n", scc_counter);
+ else
+ log_error("Found %d SCCs but expected %d.\n", scc_counter, expect);
+ } else
+ log("Found %d SCCs.\n", scc_counter);
+
+ if (selectMode) {
+ log_assert(origSelectPos >= 0);
+ design->selection_stack[origSelectPos] = newSelection;
+ design->selection_stack[origSelectPos].optimize(design);
+ }
+ }
+} SccPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc
new file mode 100644
index 00000000..d2e1a2e2
--- /dev/null
+++ b/passes/cmds/select.cc
@@ -0,0 +1,1582 @@
+/*
+ * 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/celltypes.h"
+#include "kernel/sigtools.h"
+#include <string.h>
+#include <errno.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+using RTLIL::id2cstr;
+
+static std::vector<RTLIL::Selection> work_stack;
+
+static bool match_ids(RTLIL::IdString id, std::string pattern)
+{
+ if (id == pattern)
+ return true;
+ if (id.size() > 0 && id[0] == '\\' && id.substr(1) == pattern)
+ return true;
+ if (patmatch(pattern.c_str(), id.c_str()))
+ return true;
+ if (id.size() > 0 && id[0] == '\\' && patmatch(pattern.c_str(), id.substr(1).c_str()))
+ return true;
+ if (id.size() > 0 && id[0] == '$' && pattern.size() > 0 && pattern[0] == '$') {
+ const char *p = id.c_str();
+ const char *q = strrchr(p, '$');
+ if (pattern == q)
+ return true;
+ }
+ return false;
+}
+
+static bool match_attr_val(const RTLIL::Const &value, std::string pattern, char match_op)
+{
+ if (match_op == 0)
+ return true;
+
+ if ((value.flags & RTLIL::CONST_FLAG_STRING) == 0)
+ {
+ RTLIL::SigSpec sig_value;
+
+ if (!RTLIL::SigSpec::parse(sig_value, NULL, pattern))
+ return false;
+
+ RTLIL::Const pattern_value = sig_value.as_const();
+
+ if (match_op == '=')
+ return value == pattern_value;
+ if (match_op == '!')
+ return value != pattern_value;
+ if (match_op == '<')
+ return value.as_int() < pattern_value.as_int();
+ if (match_op == '>')
+ return value.as_int() > pattern_value.as_int();
+ if (match_op == '[')
+ return value.as_int() <= pattern_value.as_int();
+ if (match_op == ']')
+ return value.as_int() >= pattern_value.as_int();
+ }
+ else
+ {
+ std::string value_str = value.decode_string();
+
+ if (match_op == '=')
+ if (patmatch(pattern.c_str(), value.decode_string().c_str()))
+ return true;
+
+ if (match_op == '=')
+ return value_str == pattern;
+ if (match_op == '!')
+ return value_str != pattern;
+ if (match_op == '<')
+ return value_str < pattern;
+ if (match_op == '>')
+ return value_str > pattern;
+ if (match_op == '[')
+ return value_str <= pattern;
+ if (match_op == ']')
+ return value_str >= pattern;
+ }
+
+ log_abort();
+}
+
+static bool match_attr(const dict<RTLIL::IdString, RTLIL::Const> &attributes, std::string name_pat, std::string value_pat, char match_op)
+{
+ if (name_pat.find('*') != std::string::npos || name_pat.find('?') != std::string::npos || name_pat.find('[') != std::string::npos) {
+ for (auto &it : attributes) {
+ if (patmatch(name_pat.c_str(), it.first.c_str()) && match_attr_val(it.second, value_pat, match_op))
+ return true;
+ if (it.first.size() > 0 && it.first[0] == '\\' && patmatch(name_pat.c_str(), it.first.substr(1).c_str()) && match_attr_val(it.second, value_pat, match_op))
+ return true;
+ }
+ } else {
+ if (name_pat.size() > 0 && (name_pat[0] == '\\' || name_pat[0] == '$') && attributes.count(name_pat) && match_attr_val(attributes.at(name_pat), value_pat, match_op))
+ return true;
+ if (attributes.count("\\" + name_pat) && match_attr_val(attributes.at("\\" + name_pat), value_pat, match_op))
+ return true;
+ }
+ return false;
+}
+
+static bool match_attr(const dict<RTLIL::IdString, RTLIL::Const> &attributes, std::string match_expr)
+{
+ size_t pos = match_expr.find_first_of("<!=>");
+
+ if (pos != std::string::npos) {
+ if (match_expr.substr(pos, 2) == "!=")
+ return match_attr(attributes, match_expr.substr(0, pos), match_expr.substr(pos+2), '!');
+ if (match_expr.substr(pos, 2) == "<=")
+ return match_attr(attributes, match_expr.substr(0, pos), match_expr.substr(pos+2), '[');
+ if (match_expr.substr(pos, 2) == ">=")
+ return match_attr(attributes, match_expr.substr(0, pos), match_expr.substr(pos+2), ']');
+ return match_attr(attributes, match_expr.substr(0, pos), match_expr.substr(pos+1), match_expr[pos]);
+ }
+
+ return match_attr(attributes, match_expr, std::string(), 0);
+}
+
+static void select_op_neg(RTLIL::Design *design, RTLIL::Selection &lhs)
+{
+ if (lhs.full_selection) {
+ lhs.full_selection = false;
+ lhs.selected_modules.clear();
+ lhs.selected_members.clear();
+ return;
+ }
+
+ if (lhs.selected_modules.size() == 0 && lhs.selected_members.size() == 0) {
+ lhs.full_selection = true;
+ return;
+ }
+
+ RTLIL::Selection new_sel(false);
+
+ for (auto &mod_it : design->modules_)
+ {
+ if (lhs.selected_whole_module(mod_it.first))
+ continue;
+ if (!lhs.selected_module(mod_it.first)) {
+ new_sel.selected_modules.insert(mod_it.first);
+ continue;
+ }
+
+ RTLIL::Module *mod = mod_it.second;
+ for (auto &it : mod->wires_)
+ if (!lhs.selected_member(mod_it.first, it.first))
+ new_sel.selected_members[mod->name].insert(it.first);
+ for (auto &it : mod->memories)
+ if (!lhs.selected_member(mod_it.first, it.first))
+ new_sel.selected_members[mod->name].insert(it.first);
+ for (auto &it : mod->cells_)
+ if (!lhs.selected_member(mod_it.first, it.first))
+ new_sel.selected_members[mod->name].insert(it.first);
+ for (auto &it : mod->processes)
+ if (!lhs.selected_member(mod_it.first, it.first))
+ new_sel.selected_members[mod->name].insert(it.first);
+ }
+
+ lhs.selected_modules.swap(new_sel.selected_modules);
+ lhs.selected_members.swap(new_sel.selected_members);
+}
+
+static int my_xorshift32_rng() {
+ static uint32_t x32 = 314159265;
+ x32 ^= x32 << 13;
+ x32 ^= x32 >> 17;
+ x32 ^= x32 << 5;
+ return x32 & 0x0fffffff;
+}
+
+static void select_op_random(RTLIL::Design *design, RTLIL::Selection &lhs, int count)
+{
+ vector<pair<IdString, IdString>> objects;
+
+ for (auto mod : design->modules())
+ {
+ if (!lhs.selected_module(mod->name))
+ continue;
+
+ for (auto cell : mod->cells()) {
+ if (lhs.selected_member(mod->name, cell->name))
+ objects.push_back(make_pair(mod->name, cell->name));
+ }
+
+ for (auto wire : mod->wires()) {
+ if (lhs.selected_member(mod->name, wire->name))
+ objects.push_back(make_pair(mod->name, wire->name));
+ }
+ }
+
+ lhs = RTLIL::Selection(false);
+
+ while (!objects.empty() && count-- > 0)
+ {
+ int idx = my_xorshift32_rng() % GetSize(objects);
+ lhs.selected_members[objects[idx].first].insert(objects[idx].second);
+ objects[idx] = objects.back();
+ objects.pop_back();
+ }
+
+ lhs.optimize(design);
+}
+
+static void select_op_submod(RTLIL::Design *design, RTLIL::Selection &lhs)
+{
+ for (auto &mod_it : design->modules_)
+ {
+ if (lhs.selected_whole_module(mod_it.first))
+ {
+ for (auto &cell_it : mod_it.second->cells_)
+ {
+ if (design->modules_.count(cell_it.second->type) == 0)
+ continue;
+ lhs.selected_modules.insert(cell_it.second->type);
+ }
+ }
+ }
+}
+
+static void select_op_cells_to_modules(RTLIL::Design *design, RTLIL::Selection &lhs)
+{
+ RTLIL::Selection new_sel(false);
+ for (auto &mod_it : design->modules_)
+ if (lhs.selected_module(mod_it.first))
+ for (auto &cell_it : mod_it.second->cells_)
+ if (lhs.selected_member(mod_it.first, cell_it.first) && design->modules_.count(cell_it.second->type))
+ new_sel.selected_modules.insert(cell_it.second->type);
+ lhs = new_sel;
+}
+
+static void select_op_module_to_cells(RTLIL::Design *design, RTLIL::Selection &lhs)
+{
+ RTLIL::Selection new_sel(false);
+ for (auto &mod_it : design->modules_)
+ for (auto &cell_it : mod_it.second->cells_)
+ if (design->modules_.count(cell_it.second->type) && lhs.selected_whole_module(cell_it.second->type))
+ new_sel.selected_members[mod_it.first].insert(cell_it.first);
+ lhs = new_sel;
+}
+
+static void select_op_fullmod(RTLIL::Design *design, RTLIL::Selection &lhs)
+{
+ lhs.optimize(design);
+ for (auto &it : lhs.selected_members)
+ lhs.selected_modules.insert(it.first);
+ lhs.selected_members.clear();
+}
+
+static void select_op_alias(RTLIL::Design *design, RTLIL::Selection &lhs)
+{
+ for (auto &mod_it : design->modules_)
+ {
+ if (lhs.selected_whole_module(mod_it.first))
+ continue;
+ if (!lhs.selected_module(mod_it.first))
+ continue;
+
+ SigMap sigmap(mod_it.second);
+ SigPool selected_bits;
+
+ for (auto &it : mod_it.second->wires_)
+ if (lhs.selected_member(mod_it.first, it.first))
+ selected_bits.add(sigmap(it.second));
+
+ for (auto &it : mod_it.second->wires_)
+ if (!lhs.selected_member(mod_it.first, it.first) && selected_bits.check_any(sigmap(it.second)))
+ lhs.selected_members[mod_it.first].insert(it.first);
+ }
+}
+
+static void select_op_union(RTLIL::Design*, RTLIL::Selection &lhs, const RTLIL::Selection &rhs)
+{
+ if (rhs.full_selection) {
+ lhs.full_selection = true;
+ lhs.selected_modules.clear();
+ lhs.selected_members.clear();
+ return;
+ }
+
+ if (lhs.full_selection)
+ return;
+
+ for (auto &it : rhs.selected_members)
+ for (auto &it2 : it.second)
+ lhs.selected_members[it.first].insert(it2);
+
+ for (auto &it : rhs.selected_modules) {
+ lhs.selected_modules.insert(it);
+ lhs.selected_members.erase(it);
+ }
+}
+
+static void select_op_diff(RTLIL::Design *design, RTLIL::Selection &lhs, const RTLIL::Selection &rhs)
+{
+ if (rhs.full_selection) {
+ lhs.full_selection = false;
+ lhs.selected_modules.clear();
+ lhs.selected_members.clear();
+ return;
+ }
+
+ if (lhs.full_selection) {
+ if (!rhs.full_selection && rhs.selected_modules.size() == 0 && rhs.selected_members.size() == 0)
+ return;
+ lhs.full_selection = false;
+ for (auto &it : design->modules_)
+ lhs.selected_modules.insert(it.first);
+ }
+
+ for (auto &it : rhs.selected_modules) {
+ lhs.selected_modules.erase(it);
+ lhs.selected_members.erase(it);
+ }
+
+ for (auto &it : rhs.selected_members)
+ {
+ if (design->modules_.count(it.first) == 0)
+ continue;
+
+ RTLIL::Module *mod = design->modules_[it.first];
+
+ if (lhs.selected_modules.count(mod->name) > 0)
+ {
+ for (auto &it : mod->wires_)
+ lhs.selected_members[mod->name].insert(it.first);
+ for (auto &it : mod->memories)
+ lhs.selected_members[mod->name].insert(it.first);
+ for (auto &it : mod->cells_)
+ lhs.selected_members[mod->name].insert(it.first);
+ for (auto &it : mod->processes)
+ lhs.selected_members[mod->name].insert(it.first);
+ lhs.selected_modules.erase(mod->name);
+ }
+
+ if (lhs.selected_members.count(mod->name) == 0)
+ continue;
+
+ for (auto &it2 : it.second)
+ lhs.selected_members[mod->name].erase(it2);
+ }
+}
+
+static void select_op_intersect(RTLIL::Design *design, RTLIL::Selection &lhs, const RTLIL::Selection &rhs)
+{
+ if (rhs.full_selection)
+ return;
+
+ if (lhs.full_selection) {
+ lhs.full_selection = false;
+ for (auto &it : design->modules_)
+ lhs.selected_modules.insert(it.first);
+ }
+
+ std::vector<RTLIL::IdString> del_list;
+
+ for (auto &it : lhs.selected_modules)
+ if (rhs.selected_modules.count(it) == 0) {
+ if (rhs.selected_members.count(it) > 0)
+ for (auto &it2 : rhs.selected_members.at(it))
+ lhs.selected_members[it].insert(it2);
+ del_list.push_back(it);
+ }
+ for (auto &it : del_list)
+ lhs.selected_modules.erase(it);
+
+ del_list.clear();
+ for (auto &it : lhs.selected_members) {
+ if (rhs.selected_modules.count(it.first) > 0)
+ continue;
+ if (rhs.selected_members.count(it.first) == 0) {
+ del_list.push_back(it.first);
+ continue;
+ }
+ std::vector<RTLIL::IdString> del_list2;
+ for (auto &it2 : it.second)
+ if (rhs.selected_members.at(it.first).count(it2) == 0)
+ del_list2.push_back(it2);
+ for (auto &it2 : del_list2)
+ it.second.erase(it2);
+ if (it.second.size() == 0)
+ del_list.push_back(it.first);
+ }
+ for (auto &it : del_list)
+ lhs.selected_members.erase(it);
+}
+
+namespace {
+ struct expand_rule_t {
+ char mode;
+ std::set<RTLIL::IdString> cell_types, port_names;
+ };
+}
+
+static int parse_comma_list(std::set<RTLIL::IdString> &tokens, std::string str, size_t pos, std::string stopchar)
+{
+ stopchar += ',';
+ while (1) {
+ size_t endpos = str.find_first_of(stopchar, pos);
+ if (endpos == std::string::npos)
+ endpos = str.size();
+ if (endpos != pos)
+ tokens.insert(RTLIL::escape_id(str.substr(pos, endpos-pos)));
+ pos = endpos;
+ if (pos == str.size() || str[pos] != ',')
+ return pos;
+ pos++;
+ }
+}
+
+static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::vector<expand_rule_t> &rules, std::set<RTLIL::IdString> &limits, int max_objects, char mode, CellTypes &ct, bool eval_only)
+{
+ int sel_objects = 0;
+ bool is_input, is_output;
+ for (auto &mod_it : design->modules_)
+ {
+ if (lhs.selected_whole_module(mod_it.first) || !lhs.selected_module(mod_it.first))
+ continue;
+
+ RTLIL::Module *mod = mod_it.second;
+ std::set<RTLIL::Wire*> selected_wires;
+ auto selected_members = lhs.selected_members[mod->name];
+
+ for (auto &it : mod->wires_)
+ if (lhs.selected_member(mod_it.first, it.first) && limits.count(it.first) == 0)
+ selected_wires.insert(it.second);
+
+ for (auto &conn : mod->connections())
+ {
+ std::vector<RTLIL::SigBit> conn_lhs = conn.first.to_sigbit_vector();
+ std::vector<RTLIL::SigBit> conn_rhs = conn.second.to_sigbit_vector();
+
+ for (size_t i = 0; i < conn_lhs.size(); i++) {
+ if (conn_lhs[i].wire == NULL || conn_rhs[i].wire == NULL)
+ continue;
+ if (mode != 'i' && selected_wires.count(conn_rhs[i].wire) && selected_members.count(conn_lhs[i].wire->name) == 0)
+ lhs.selected_members[mod->name].insert(conn_lhs[i].wire->name), sel_objects++, max_objects--;
+ if (mode != 'o' && selected_wires.count(conn_lhs[i].wire) && selected_members.count(conn_rhs[i].wire->name) == 0)
+ lhs.selected_members[mod->name].insert(conn_rhs[i].wire->name), sel_objects++, max_objects--;
+ }
+ }
+
+ for (auto &cell : mod->cells_)
+ for (auto &conn : cell.second->connections())
+ {
+ char last_mode = '-';
+ if (eval_only && !yosys_celltypes.cell_evaluable(cell.second->type))
+ goto exclude_match;
+ for (auto &rule : rules) {
+ last_mode = rule.mode;
+ if (rule.cell_types.size() > 0 && rule.cell_types.count(cell.second->type) == 0)
+ continue;
+ if (rule.port_names.size() > 0 && rule.port_names.count(conn.first) == 0)
+ continue;
+ if (rule.mode == '+')
+ goto include_match;
+ else
+ goto exclude_match;
+ }
+ if (last_mode == '+')
+ goto exclude_match;
+ include_match:
+ is_input = mode == 'x' || ct.cell_input(cell.second->type, conn.first);
+ is_output = mode == 'x' || ct.cell_output(cell.second->type, conn.first);
+ for (auto &chunk : conn.second.chunks())
+ if (chunk.wire != NULL) {
+ if (max_objects != 0 && selected_wires.count(chunk.wire) > 0 && selected_members.count(cell.first) == 0)
+ if (mode == 'x' || (mode == 'i' && is_output) || (mode == 'o' && is_input))
+ lhs.selected_members[mod->name].insert(cell.first), sel_objects++, max_objects--;
+ if (max_objects != 0 && selected_members.count(cell.first) > 0 && limits.count(cell.first) == 0 && selected_members.count(chunk.wire->name) == 0)
+ if (mode == 'x' || (mode == 'i' && is_input) || (mode == 'o' && is_output))
+ lhs.selected_members[mod->name].insert(chunk.wire->name), sel_objects++, max_objects--;
+ }
+ exclude_match:;
+ }
+ }
+
+ return sel_objects;
+}
+
+static void select_op_expand(RTLIL::Design *design, std::string arg, char mode, bool eval_only)
+{
+ int pos = (mode == 'x' ? 2 : 3) + (eval_only ? 1 : 0);
+ int levels = 1, rem_objects = -1;
+ std::vector<expand_rule_t> rules;
+ std::set<RTLIL::IdString> limits;
+
+ CellTypes ct;
+
+ if (mode != 'x')
+ ct.setup(design);
+
+ if (pos < int(arg.size()) && arg[pos] == '*') {
+ levels = 1000000;
+ pos++;
+ } else
+ if (pos < int(arg.size()) && '0' <= arg[pos] && arg[pos] <= '9') {
+ size_t endpos = arg.find_first_not_of("0123456789", pos);
+ if (endpos == std::string::npos)
+ endpos = arg.size();
+ levels = atoi(arg.substr(pos, endpos-pos).c_str());
+ pos = endpos;
+ }
+
+ if (pos < int(arg.size()) && arg[pos] == '.') {
+ size_t endpos = arg.find_first_not_of("0123456789", ++pos);
+ if (endpos == std::string::npos)
+ endpos = arg.size();
+ if (int(endpos) > pos)
+ rem_objects = atoi(arg.substr(pos, endpos-pos).c_str());
+ pos = endpos;
+ }
+
+ while (pos < int(arg.size())) {
+ if (arg[pos] != ':' || pos+1 == int(arg.size()))
+ log_cmd_error("Syntax error in expand operator '%s'.\n", arg.c_str());
+ pos++;
+ if (arg[pos] == '+' || arg[pos] == '-') {
+ expand_rule_t rule;
+ rule.mode = arg[pos++];
+ pos = parse_comma_list(rule.cell_types, arg, pos, "[:");
+ if (pos < int(arg.size()) && arg[pos] == '[') {
+ pos = parse_comma_list(rule.port_names, arg, pos+1, "]:");
+ if (pos < int(arg.size()) && arg[pos] == ']')
+ pos++;
+ }
+ rules.push_back(rule);
+ } else {
+ size_t endpos = arg.find(':', pos);
+ if (endpos == std::string::npos)
+ endpos = arg.size();
+ if (int(endpos) > pos) {
+ std::string str = arg.substr(pos, endpos-pos);
+ if (str[0] == '@') {
+ str = RTLIL::escape_id(str.substr(1));
+ if (design->selection_vars.count(str) > 0) {
+ for (auto i1 : design->selection_vars.at(str).selected_members)
+ for (auto i2 : i1.second)
+ limits.insert(i2);
+ } else
+ log_cmd_error("Selection %s is not defined!\n", RTLIL::unescape_id(str).c_str());
+ } else
+ limits.insert(RTLIL::escape_id(str));
+ }
+ pos = endpos;
+ }
+ }
+
+#if 0
+ log("expand by %d levels (max. %d objects):\n", levels, rem_objects);
+ for (auto &rule : rules) {
+ log(" rule (%c):\n", rule.mode);
+ if (rule.cell_types.size() > 0) {
+ log(" cell types:");
+ for (auto &it : rule.cell_types)
+ log(" %s", it.c_str());
+ log("\n");
+ }
+ if (rule.port_names.size() > 0) {
+ log(" port names:");
+ for (auto &it : rule.port_names)
+ log(" %s", it.c_str());
+ log("\n");
+ }
+ }
+ if (limits.size() > 0) {
+ log(" limits:");
+ for (auto &it : limits)
+ log(" %s", it.c_str());
+ log("\n");
+ }
+#endif
+
+ while (levels-- > 0 && rem_objects != 0) {
+ int num_objects = select_op_expand(design, work_stack.back(), rules, limits, rem_objects, mode, ct, eval_only);
+ if (num_objects == 0)
+ break;
+ rem_objects -= num_objects;
+ }
+
+ if (rem_objects == 0)
+ log_warning("reached configured limit at `%s'.\n", arg.c_str());
+}
+
+static void select_filter_active_mod(RTLIL::Design *design, RTLIL::Selection &sel)
+{
+ if (design->selected_active_module.empty())
+ return;
+
+ if (sel.full_selection) {
+ sel.full_selection = false;
+ sel.selected_modules.clear();
+ sel.selected_members.clear();
+ sel.selected_modules.insert(design->selected_active_module);
+ return;
+ }
+
+ std::vector<RTLIL::IdString> del_list;
+ for (auto mod_name : sel.selected_modules)
+ if (mod_name != design->selected_active_module)
+ del_list.push_back(mod_name);
+ for (auto &it : sel.selected_members)
+ if (it.first != design->selected_active_module)
+ del_list.push_back(it.first);
+ for (auto mod_name : del_list) {
+ sel.selected_modules.erase(mod_name);
+ sel.selected_members.erase(mod_name);
+ }
+}
+
+static void select_stmt(RTLIL::Design *design, std::string arg)
+{
+ std::string arg_mod, arg_memb;
+
+ if (arg.size() == 0)
+ return;
+
+ if (arg[0] == '%') {
+ if (arg == "%") {
+ if (design->selection_stack.size() > 0)
+ work_stack.push_back(design->selection_stack.back());
+ } else
+ if (arg == "%%") {
+ while (work_stack.size() > 1) {
+ select_op_union(design, work_stack.front(), work_stack.back());
+ work_stack.pop_back();
+ }
+ } else
+ if (arg == "%n") {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%n.\n");
+ select_op_neg(design, work_stack[work_stack.size()-1]);
+ } else
+ if (arg == "%u") {
+ if (work_stack.size() < 2)
+ log_cmd_error("Must have at least two elements on the stack for operator %%u.\n");
+ select_op_union(design, work_stack[work_stack.size()-2], work_stack[work_stack.size()-1]);
+ work_stack.pop_back();
+ } else
+ if (arg == "%d") {
+ if (work_stack.size() < 2)
+ log_cmd_error("Must have at least two elements on the stack for operator %%d.\n");
+ select_op_diff(design, work_stack[work_stack.size()-2], work_stack[work_stack.size()-1]);
+ work_stack.pop_back();
+ } else
+ if (arg == "%D") {
+ if (work_stack.size() < 2)
+ log_cmd_error("Must have at least two elements on the stack for operator %%d.\n");
+ select_op_diff(design, work_stack[work_stack.size()-1], work_stack[work_stack.size()-2]);
+ work_stack[work_stack.size()-2] = work_stack[work_stack.size()-1];
+ work_stack.pop_back();
+ } else
+ if (arg == "%i") {
+ if (work_stack.size() < 2)
+ log_cmd_error("Must have at least two elements on the stack for operator %%i.\n");
+ select_op_intersect(design, work_stack[work_stack.size()-2], work_stack[work_stack.size()-1]);
+ work_stack.pop_back();
+ } else
+ if (arg.size() >= 2 && arg[0] == '%' && arg[1] == 'R') {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%R.\n");
+ int count = arg.size() > 2 ? atoi(arg.c_str() + 2) : 1;
+ select_op_random(design, work_stack[work_stack.size()-1], count);
+ } else
+ if (arg == "%s") {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%s.\n");
+ select_op_submod(design, work_stack[work_stack.size()-1]);
+ } else
+ if (arg == "%M") {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%M.\n");
+ select_op_cells_to_modules(design, work_stack[work_stack.size()-1]);
+ } else
+ if (arg == "%C") {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%M.\n");
+ select_op_module_to_cells(design, work_stack[work_stack.size()-1]);
+ } else
+ if (arg == "%c") {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%c.\n");
+ work_stack.push_back(work_stack.back());
+ } else
+ if (arg == "%m") {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%m.\n");
+ select_op_fullmod(design, work_stack[work_stack.size()-1]);
+ } else
+ if (arg == "%a") {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%a.\n");
+ select_op_alias(design, work_stack[work_stack.size()-1]);
+ } else
+ if (arg == "%x" || (arg.size() > 2 && arg.substr(0, 2) == "%x" && (arg[2] == ':' || arg[2] == '*' || arg[2] == '.' || ('0' <= arg[2] && arg[2] <= '9')))) {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%x.\n");
+ select_op_expand(design, arg, 'x', false);
+ } else
+ if (arg == "%ci" || (arg.size() > 3 && arg.substr(0, 3) == "%ci" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%ci.\n");
+ select_op_expand(design, arg, 'i', false);
+ } else
+ if (arg == "%co" || (arg.size() > 3 && arg.substr(0, 3) == "%co" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%co.\n");
+ select_op_expand(design, arg, 'o', false);
+ } else
+ if (arg == "%xe" || (arg.size() > 3 && arg.substr(0, 3) == "%xe" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%xe.\n");
+ select_op_expand(design, arg, 'x', true);
+ } else
+ if (arg == "%cie" || (arg.size() > 4 && arg.substr(0, 4) == "%cie" && (arg[4] == ':' || arg[4] == '*' || arg[4] == '.' || ('0' <= arg[4] && arg[4] <= '9')))) {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%cie.\n");
+ select_op_expand(design, arg, 'i', true);
+ } else
+ if (arg == "%coe" || (arg.size() > 4 && arg.substr(0, 4) == "%coe" && (arg[4] == ':' || arg[4] == '*' || arg[4] == '.' || ('0' <= arg[4] && arg[4] <= '9')))) {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%coe.\n");
+ select_op_expand(design, arg, 'o', true);
+ } else
+ log_cmd_error("Unknown selection operator '%s'.\n", arg.c_str());
+ if (work_stack.size() >= 1)
+ select_filter_active_mod(design, work_stack.back());
+ return;
+ }
+
+ if (arg[0] == '@') {
+ std::string set_name = RTLIL::escape_id(arg.substr(1));
+ if (design->selection_vars.count(set_name) > 0)
+ work_stack.push_back(design->selection_vars[set_name]);
+ else
+ log_cmd_error("Selection @%s is not defined!\n", RTLIL::unescape_id(set_name).c_str());
+ select_filter_active_mod(design, work_stack.back());
+ return;
+ }
+
+ if (!design->selected_active_module.empty()) {
+ arg_mod = design->selected_active_module;
+ arg_memb = arg;
+ } else {
+ size_t pos = arg.find('/');
+ if (pos == std::string::npos) {
+ if (arg.find(':') == std::string::npos || arg.substr(0, 1) == "A")
+ arg_mod = arg;
+ else
+ arg_mod = "*", arg_memb = arg;
+ } else {
+ arg_mod = arg.substr(0, pos);
+ arg_memb = arg.substr(pos+1);
+ }
+ }
+
+ work_stack.push_back(RTLIL::Selection());
+ RTLIL::Selection &sel = work_stack.back();
+
+ if (arg == "*" && arg_mod == "*") {
+ select_filter_active_mod(design, work_stack.back());
+ return;
+ }
+
+ sel.full_selection = false;
+ for (auto &mod_it : design->modules_)
+ {
+ if (arg_mod.substr(0, 2) == "A:") {
+ if (!match_attr(mod_it.second->attributes, arg_mod.substr(2)))
+ continue;
+ } else
+ if (!match_ids(mod_it.first, arg_mod))
+ continue;
+
+ if (arg_memb == "") {
+ sel.selected_modules.insert(mod_it.first);
+ continue;
+ }
+
+ RTLIL::Module *mod = mod_it.second;
+ if (arg_memb.substr(0, 2) == "w:") {
+ for (auto &it : mod->wires_)
+ if (match_ids(it.first, arg_memb.substr(2)))
+ sel.selected_members[mod->name].insert(it.first);
+ } else
+ if (arg_memb.substr(0, 2) == "i:") {
+ for (auto &it : mod->wires_)
+ if (it.second->port_input && match_ids(it.first, arg_memb.substr(2)))
+ sel.selected_members[mod->name].insert(it.first);
+ } else
+ if (arg_memb.substr(0, 2) == "o:") {
+ for (auto &it : mod->wires_)
+ if (it.second->port_output && match_ids(it.first, arg_memb.substr(2)))
+ sel.selected_members[mod->name].insert(it.first);
+ } else
+ if (arg_memb.substr(0, 2) == "x:") {
+ for (auto &it : mod->wires_)
+ if ((it.second->port_input || it.second->port_output) && match_ids(it.first, arg_memb.substr(2)))
+ sel.selected_members[mod->name].insert(it.first);
+ } else
+ if (arg_memb.substr(0, 2) == "s:") {
+ size_t delim = arg_memb.substr(2).find(':');
+ if (delim == std::string::npos) {
+ int width = atoi(arg_memb.substr(2).c_str());
+ for (auto &it : mod->wires_)
+ if (it.second->width == width)
+ sel.selected_members[mod->name].insert(it.first);
+ } else {
+ std::string min_str = arg_memb.substr(2, delim);
+ std::string max_str = arg_memb.substr(2+delim+1);
+ int min_width = min_str.empty() ? 0 : atoi(min_str.c_str());
+ int max_width = max_str.empty() ? -1 : atoi(max_str.c_str());
+ for (auto &it : mod->wires_)
+ if (min_width <= it.second->width && (it.second->width <= max_width || max_width == -1))
+ sel.selected_members[mod->name].insert(it.first);
+ }
+ } else
+ if (arg_memb.substr(0, 2) == "m:") {
+ for (auto &it : mod->memories)
+ if (match_ids(it.first, arg_memb.substr(2)))
+ sel.selected_members[mod->name].insert(it.first);
+ } else
+ if (arg_memb.substr(0, 2) == "c:") {
+ for (auto &it : mod->cells_)
+ if (match_ids(it.first, arg_memb.substr(2)))
+ sel.selected_members[mod->name].insert(it.first);
+ } else
+ if (arg_memb.substr(0, 2) == "t:") {
+ for (auto &it : mod->cells_)
+ if (match_ids(it.second->type, arg_memb.substr(2)))
+ sel.selected_members[mod->name].insert(it.first);
+ } else
+ if (arg_memb.substr(0, 2) == "p:") {
+ for (auto &it : mod->processes)
+ if (match_ids(it.first, arg_memb.substr(2)))
+ sel.selected_members[mod->name].insert(it.first);
+ } else
+ if (arg_memb.substr(0, 2) == "a:") {
+ for (auto &it : mod->wires_)
+ if (match_attr(it.second->attributes, arg_memb.substr(2)))
+ sel.selected_members[mod->name].insert(it.first);
+ for (auto &it : mod->memories)
+ if (match_attr(it.second->attributes, arg_memb.substr(2)))
+ sel.selected_members[mod->name].insert(it.first);
+ for (auto &it : mod->cells_)
+ if (match_attr(it.second->attributes, arg_memb.substr(2)))
+ sel.selected_members[mod->name].insert(it.first);
+ for (auto &it : mod->processes)
+ if (match_attr(it.second->attributes, arg_memb.substr(2)))
+ sel.selected_members[mod->name].insert(it.first);
+ } else
+ if (arg_memb.substr(0, 2) == "r:") {
+ for (auto &it : mod->cells_)
+ if (match_attr(it.second->parameters, arg_memb.substr(2)))
+ sel.selected_members[mod->name].insert(it.first);
+ } else {
+ if (arg_memb.substr(0, 2) == "n:")
+ arg_memb = arg_memb.substr(2);
+ for (auto &it : mod->wires_)
+ if (match_ids(it.first, arg_memb))
+ sel.selected_members[mod->name].insert(it.first);
+ for (auto &it : mod->memories)
+ if (match_ids(it.first, arg_memb))
+ sel.selected_members[mod->name].insert(it.first);
+ for (auto &it : mod->cells_)
+ if (match_ids(it.first, arg_memb))
+ sel.selected_members[mod->name].insert(it.first);
+ for (auto &it : mod->processes)
+ if (match_ids(it.first, arg_memb))
+ sel.selected_members[mod->name].insert(it.first);
+ }
+ }
+
+ select_filter_active_mod(design, work_stack.back());
+}
+
+PRIVATE_NAMESPACE_END
+YOSYS_NAMESPACE_BEGIN
+
+// used in kernel/register.cc and maybe other locations, extern decl. in register.h
+void handle_extra_select_args(Pass *pass, vector<string> args, size_t argidx, size_t args_size, RTLIL::Design *design)
+{
+ work_stack.clear();
+ for (; argidx < args_size; argidx++) {
+ if (args[argidx].substr(0, 1) == "-") {
+ if (pass != NULL)
+ pass->cmd_error(args, argidx, "Unexpected option in selection arguments.");
+ else
+ log_cmd_error("Unexpected option in selection arguments.");
+ }
+ select_stmt(design, args[argidx]);
+ }
+ while (work_stack.size() > 1) {
+ select_op_union(design, work_stack.front(), work_stack.back());
+ work_stack.pop_back();
+ }
+ if (work_stack.empty())
+ design->selection_stack.push_back(RTLIL::Selection(false));
+ else
+ design->selection_stack.push_back(work_stack.back());
+}
+
+// extern decl. in register.h
+RTLIL::Selection eval_select_args(const vector<string> &args, RTLIL::Design *design)
+{
+ work_stack.clear();
+ for (auto &arg : args)
+ select_stmt(design, arg);
+ while (work_stack.size() > 1) {
+ select_op_union(design, work_stack.front(), work_stack.back());
+ work_stack.pop_back();
+ }
+ if (work_stack.empty())
+ return RTLIL::Selection(false);
+ return work_stack.back();
+}
+
+// extern decl. in register.h
+void eval_select_op(vector<RTLIL::Selection> &work, const string &op, RTLIL::Design *design)
+{
+ work_stack.swap(work);
+ select_stmt(design, op);
+ work_stack.swap(work);
+}
+
+YOSYS_NAMESPACE_END
+PRIVATE_NAMESPACE_BEGIN
+
+struct SelectPass : public Pass {
+ SelectPass() : Pass("select", "modify and view the list of selected objects") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" select [ -add | -del | -set <name> ] {-read <filename> | <selection>}\n");
+ log(" select [ <assert_option> ] {-read <filename> | <selection>}\n");
+ log(" select [ -list | -write <filename> | -count | -clear ]\n");
+ log(" select -module <modname>\n");
+ log("\n");
+ log("Most commands use the list of currently selected objects to determine which part\n");
+ log("of the design to operate on. This command can be used to modify and view this\n");
+ 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 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");
+ log(" -add, -del\n");
+ log(" add or remove the given objects to the current selection.\n");
+ log(" without this options the current selection is replaced.\n");
+ log("\n");
+ log(" -set <name>\n");
+ log(" do not modify the current selection. instead save the new selection\n");
+ log(" under the given name (see @<name> below). to save the current selection,\n");
+ log(" use \"select -set <name> %%\"\n");
+ log("\n");
+ log(" -assert-none\n");
+ log(" do not modify the current selection. instead assert that the given\n");
+ log(" selection is empty. i.e. produce an error if any object matching the\n");
+ log(" selection is found.\n");
+ log("\n");
+ log(" -assert-any\n");
+ log(" do not modify the current selection. instead assert that the given\n");
+ log(" selection is non-empty. i.e. produce an error if no object matching\n");
+ log(" the selection is found.\n");
+ log("\n");
+ log(" -assert-count N\n");
+ log(" do not modify the current selection. instead assert that the given\n");
+ log(" selection contains exactly N objects.\n");
+ log("\n");
+ log(" -assert-max N\n");
+ log(" do not modify the current selection. instead assert that the given\n");
+ log(" selection contains less than or exactly N objects.\n");
+ log("\n");
+ log(" -assert-min N\n");
+ log(" do not modify the current selection. instead assert that the given\n");
+ log(" selection contains at least N objects.\n");
+ log("\n");
+ log(" -list\n");
+ log(" list all objects in the current selection\n");
+ log("\n");
+ log(" -write <filename>\n");
+ log(" like -list but write the output to the specified file\n");
+ log("\n");
+ log(" -read <filename>\n");
+ log(" read the specified file (written by -write)\n");
+ log("\n");
+ log(" -count\n");
+ log(" count all objects in the current selection\n");
+ log("\n");
+ log(" -clear\n");
+ log(" clear the current selection. this effectively selects the whole\n");
+ log(" design. it also resets the selected module (see -module). use the\n");
+ log(" command 'select *' to select everything but stay in the current module.\n");
+ log("\n");
+ log(" -none\n");
+ log(" create an empty selection. the current module is unchanged.\n");
+ log("\n");
+ log(" -module <modname>\n");
+ log(" limit the current scope to the specified module.\n");
+ log(" the difference between this and simply selecting the module\n");
+ log(" is that all object names are interpreted relative to this\n");
+ log(" module after this command until the selection is cleared again.\n");
+ log("\n");
+ log("When this command is called without an argument, the current selection\n");
+ log("is displayed in a compact form (i.e. only the module name when a whole module\n");
+ log("is selected).\n");
+ log("\n");
+ log("The <selection> argument itself is a series of commands for a simple stack\n");
+ log("machine. Each element on the stack represents a set of selected objects.\n");
+ log("After this commands have been executed, the union of all remaining sets\n");
+ log("on the stack is computed and used as selection for the command.\n");
+ log("\n");
+ log("Pushing (selecting) object when not in -module mode:\n");
+ log("\n");
+ log(" <mod_pattern>\n");
+ log(" select the specified module(s)\n");
+ log("\n");
+ log(" <mod_pattern>/<obj_pattern>\n");
+ log(" select the specified object(s) from the module(s)\n");
+ log("\n");
+ log("Pushing (selecting) object when in -module mode:\n");
+ log("\n");
+ log(" <obj_pattern>\n");
+ log(" select the specified object(s) from the current module\n");
+ log("\n");
+ log("A <mod_pattern> can be a module name, wildcard expression (*, ?, [..])\n");
+ log("matching module names, or one of the following:\n");
+ log("\n");
+ log(" A:<pattern>, A:<pattern>=<pattern>\n");
+ log(" all modules with an attribute matching the given pattern\n");
+ log(" in addition to = also <, <=, >=, and > are supported\n");
+ log("\n");
+ log("An <obj_pattern> can be an object name, wildcard expression, or one of\n");
+ log("the following:\n");
+ log("\n");
+ log(" w:<pattern>\n");
+ log(" all wires with a name matching the given wildcard pattern\n");
+ log("\n");
+ log(" i:<pattern>, o:<pattern>, x:<pattern>\n");
+ log(" all inputs (i:), outputs (o:) or any ports (x:) with matching names\n");
+ log("\n");
+ log(" s:<size>, s:<min>:<max>\n");
+ log(" all wires with a matching width\n");
+ log("\n");
+ log(" m:<pattern>\n");
+ log(" all memories with a name matching the given pattern\n");
+ log("\n");
+ log(" c:<pattern>\n");
+ log(" all cells with a name matching the given pattern\n");
+ log("\n");
+ log(" t:<pattern>\n");
+ log(" all cells with a type matching the given pattern\n");
+ log("\n");
+ log(" p:<pattern>\n");
+ log(" all processes with a name matching the given pattern\n");
+ log("\n");
+ log(" a:<pattern>\n");
+ log(" all objects with an attribute name matching the given pattern\n");
+ log("\n");
+ log(" a:<pattern>=<pattern>\n");
+ log(" all objects with a matching attribute name-value-pair.\n");
+ log(" in addition to = also <, <=, >=, and > are supported\n");
+ log("\n");
+ log(" r:<pattern>, r:<pattern>=<pattern>\n");
+ log(" cells with matching parameters. also with <, <=, >= and >.\n");
+ log("\n");
+ log(" n:<pattern>\n");
+ log(" all objects with a name matching the given pattern\n");
+ log(" (i.e. 'n:' is optional as it is the default matching rule)\n");
+ log("\n");
+ log(" @<name>\n");
+ log(" push the selection saved prior with 'select -set <name> ...'\n");
+ log("\n");
+ log("The following actions can be performed on the top sets on the stack:\n");
+ log("\n");
+ log(" %%\n");
+ log(" push a copy of the current selection to the stack\n");
+ log("\n");
+ log(" %%%%\n");
+ log(" replace the stack with a union of all elements on it\n");
+ log("\n");
+ log(" %%n\n");
+ log(" replace top set with its invert\n");
+ log("\n");
+ log(" %%u\n");
+ log(" replace the two top sets on the stack with their union\n");
+ log("\n");
+ log(" %%i\n");
+ log(" replace the two top sets on the stack with their intersection\n");
+ log("\n");
+ log(" %%d\n");
+ log(" pop the top set from the stack and subtract it from the new top\n");
+ log("\n");
+ log(" %%D\n");
+ log(" like %%d but swap the roles of two top sets on the stack\n");
+ log("\n");
+ log(" %%c\n");
+ log(" create a copy of the top set from the stack and push it\n");
+ log("\n");
+ log(" %%x[<num1>|*][.<num2>][:<rule>[:<rule>..]]\n");
+ log(" expand top set <num1> num times according to the specified rules.\n");
+ log(" (i.e. select all cells connected to selected wires and select all\n");
+ log(" wires connected to selected cells) The rules specify which cell\n");
+ log(" ports to use for this. the syntax for a rule is a '-' for exclusion\n");
+ log(" and a '+' for inclusion, followed by an optional comma separated\n");
+ log(" list of cell types followed by an optional comma separated list of\n");
+ log(" cell ports in square brackets. a rule can also be just a cell or wire\n");
+ log(" name that limits the expansion (is included but does not go beyond).\n");
+ log(" select at most <num2> objects. a warning message is printed when this\n");
+ log(" limit is reached. When '*' is used instead of <num1> then the process\n");
+ log(" is repeated until no further object are selected.\n");
+ log("\n");
+ log(" %%ci[<num1>|*][.<num2>][:<rule>[:<rule>..]]\n");
+ log(" %%co[<num1>|*][.<num2>][:<rule>[:<rule>..]]\n");
+ log(" similar to %%x, but only select input (%%ci) or output cones (%%co)\n");
+ log("\n");
+ log(" %%xe[...] %%cie[...] %%coe\n");
+ log(" like %%x, %%ci, and %%co but only consider combinatorial cells\n");
+ log("\n");
+ log(" %%a\n");
+ log(" expand top set by selecting all wires that are (at least in part)\n");
+ log(" aliases for selected wires.\n");
+ log("\n");
+ log(" %%s\n");
+ log(" expand top set by adding all modules that implement cells in selected\n");
+ log(" modules\n");
+ log("\n");
+ log(" %%m\n");
+ log(" expand top set by selecting all modules that contain selected objects\n");
+ log("\n");
+ log(" %%M\n");
+ log(" select modules that implement selected cells\n");
+ log("\n");
+ log(" %%C\n");
+ log(" select cells that implement selected modules\n");
+ log("\n");
+ log(" %%R[<num>]\n");
+ log(" select <num> random objects from top selection (default 1)\n");
+ log("\n");
+ log("Example: the following command selects all wires that are connected to a\n");
+ log("'GATE' input of a 'SWITCH' cell:\n");
+ log("\n");
+ log(" select */t:SWITCH %%x:+[GATE] */t:SWITCH %%d\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool add_mode = false;
+ bool del_mode = false;
+ bool clear_mode = false;
+ bool none_mode = false;
+ bool list_mode = false;
+ bool count_mode = false;
+ bool got_module = false;
+ bool assert_none = false;
+ bool assert_any = false;
+ int assert_count = -1;
+ int assert_max = -1;
+ int assert_min = -1;
+ std::string write_file, read_file;
+ std::string set_name, sel_str;
+
+ work_stack.clear();
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-add") {
+ add_mode = true;
+ continue;
+ }
+ if (arg == "-del") {
+ del_mode = true;
+ continue;
+ }
+ if (arg == "-assert-none") {
+ assert_none = true;
+ continue;
+ }
+ if (arg == "-assert-any") {
+ assert_any = true;
+ continue;
+ }
+ if (arg == "-assert-count" && argidx+1 < args.size()) {
+ assert_count = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (arg == "-assert-max" && argidx+1 < args.size()) {
+ assert_max = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (arg == "-assert-min" && argidx+1 < args.size()) {
+ assert_min = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (arg == "-clear") {
+ clear_mode = true;
+ continue;
+ }
+ if (arg == "-none") {
+ none_mode = true;
+ continue;
+ }
+ if (arg == "-list") {
+ list_mode = true;
+ continue;
+ }
+ if (arg == "-write" && argidx+1 < args.size()) {
+ write_file = args[++argidx];
+ continue;
+ }
+ if (arg == "-read" && argidx+1 < args.size()) {
+ read_file = args[++argidx];
+ continue;
+ }
+ if (arg == "-count") {
+ count_mode = true;
+ continue;
+ }
+ if (arg == "-module" && argidx+1 < args.size()) {
+ RTLIL::IdString mod_name = RTLIL::escape_id(args[++argidx]);
+ if (design->modules_.count(mod_name) == 0)
+ log_cmd_error("No such module: %s\n", id2cstr(mod_name));
+ design->selected_active_module = mod_name.str();
+ got_module = true;
+ continue;
+ }
+ if (arg == "-set" && argidx+1 < args.size()) {
+ set_name = RTLIL::escape_id(args[++argidx]);
+ continue;
+ }
+ if (arg.size() > 0 && arg[0] == '-')
+ log_cmd_error("Unknown option %s.\n", arg.c_str());
+ select_stmt(design, arg);
+ sel_str += " " + arg;
+ }
+
+ if (!read_file.empty())
+ {
+ if (!sel_str.empty())
+ log_cmd_error("Option -read can not be combined with a selection expression.\n");
+
+ std::ifstream f(read_file);
+ if (f.fail())
+ log_error("Can't open '%s' for reading: %s\n", read_file.c_str(), strerror(errno));
+
+ RTLIL::Selection sel(false);
+ string line;
+
+ while (std::getline(f, line)) {
+ size_t slash_pos = line.find('/');
+ if (slash_pos == string::npos) {
+ log_warning("Ignoring line without slash in 'select -read': %s\n", line.c_str());
+ continue;
+ }
+ IdString mod_name = RTLIL::escape_id(line.substr(0, slash_pos));
+ IdString obj_name = RTLIL::escape_id(line.substr(slash_pos+1));
+ sel.selected_members[mod_name].insert(obj_name);
+ }
+
+ select_filter_active_mod(design, sel);
+ sel.optimize(design);
+ work_stack.push_back(sel);
+ }
+
+ if (clear_mode && args.size() != 2)
+ log_cmd_error("Option -clear can not be combined with any other options.\n");
+
+ if (none_mode && args.size() != 2)
+ log_cmd_error("Option -none can not be combined with any other options.\n");
+
+ if (add_mode + del_mode + assert_none + assert_any + (assert_count >= 0) + (assert_max >= 0) + (assert_min >= 0) > 1)
+ log_cmd_error("Options -add, -del, -assert-none, -assert-any, assert-count, -assert-max or -assert-min can not be combined.\n");
+
+ if ((list_mode || !write_file.empty() || count_mode) && (add_mode || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0))
+ log_cmd_error("Options -list, -write and -count can not be combined with -add, -del, -assert-none, -assert-any, assert-count, -assert-max, or -assert-min.\n");
+
+ if (!set_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0))
+ log_cmd_error("Option -set can not be combined with -list, -write, -count, -add, -del, -assert-none, -assert-any, -assert-count, -assert-max, or -assert-min.\n");
+
+ if (work_stack.size() == 0 && got_module) {
+ RTLIL::Selection sel;
+ select_filter_active_mod(design, sel);
+ work_stack.push_back(sel);
+ }
+
+ while (work_stack.size() > 1) {
+ select_op_union(design, work_stack.front(), work_stack.back());
+ work_stack.pop_back();
+ }
+
+ log_assert(design->selection_stack.size() > 0);
+
+ if (clear_mode) {
+ design->selection_stack.back() = RTLIL::Selection(true);
+ design->selected_active_module = std::string();
+ return;
+ }
+
+ if (none_mode) {
+ design->selection_stack.back() = RTLIL::Selection(false);
+ return;
+ }
+
+ if (list_mode || count_mode || !write_file.empty())
+ {
+ #define LOG_OBJECT(...) { if (list_mode) log(__VA_ARGS__); if (f != NULL) fprintf(f, __VA_ARGS__); total_count++; }
+ int total_count = 0;
+ FILE *f = NULL;
+ if (!write_file.empty()) {
+ f = fopen(write_file.c_str(), "w");
+ if (f == NULL)
+ log_error("Can't open '%s' for writing: %s\n", write_file.c_str(), strerror(errno));
+ }
+ RTLIL::Selection *sel = &design->selection_stack.back();
+ if (work_stack.size() > 0)
+ sel = &work_stack.back();
+ sel->optimize(design);
+ for (auto mod_it : design->modules_)
+ {
+ if (sel->selected_whole_module(mod_it.first) && list_mode)
+ log("%s\n", id2cstr(mod_it.first));
+ if (sel->selected_module(mod_it.first)) {
+ for (auto &it : mod_it.second->wires_)
+ if (sel->selected_member(mod_it.first, it.first))
+ LOG_OBJECT("%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))
+ LOG_OBJECT("%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))
+ LOG_OBJECT("%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))
+ LOG_OBJECT("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first))
+ }
+ }
+ if (count_mode)
+ log("%d objects.\n", total_count);
+ if (f != NULL)
+ fclose(f);
+ #undef LOG_OBJECT
+ return;
+ }
+
+ if (add_mode)
+ {
+ if (work_stack.size() == 0)
+ log_cmd_error("Nothing to add to selection.\n");
+ select_op_union(design, design->selection_stack.back(), work_stack.back());
+ design->selection_stack.back().optimize(design);
+ return;
+ }
+
+ if (del_mode)
+ {
+ if (work_stack.size() == 0)
+ log_cmd_error("Nothing to delete from selection.\n");
+ select_op_diff(design, design->selection_stack.back(), work_stack.back());
+ design->selection_stack.back().optimize(design);
+ return;
+ }
+
+ if (assert_none)
+ {
+ if (work_stack.size() == 0)
+ 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());
+ return;
+ }
+
+ if (assert_any)
+ {
+ if (work_stack.size() == 0)
+ 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());
+ return;
+ }
+
+ if (assert_count >= 0 || assert_max >= 0 || assert_min >= 0)
+ {
+ int total_count = 0;
+ if (work_stack.size() == 0)
+ log_cmd_error("No selection to check.\n");
+ RTLIL::Selection *sel = &work_stack.back();
+ sel->optimize(design);
+ 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))
+ total_count++;
+ for (auto &it : mod_it.second->memories)
+ if (sel->selected_member(mod_it.first, it.first))
+ total_count++;
+ for (auto &it : mod_it.second->cells_)
+ if (sel->selected_member(mod_it.first, it.first))
+ total_count++;
+ for (auto &it : mod_it.second->processes)
+ if (sel->selected_member(mod_it.first, it.first))
+ 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());
+ 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());
+ 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());
+ return;
+ }
+
+ if (!set_name.empty())
+ {
+ if (work_stack.size() == 0)
+ design->selection_vars[set_name] = RTLIL::Selection(false);
+ else
+ design->selection_vars[set_name] = work_stack.back();
+ return;
+ }
+
+ if (work_stack.size() == 0) {
+ RTLIL::Selection &sel = design->selection_stack.back();
+ if (sel.full_selection)
+ log("*\n");
+ for (auto &it : sel.selected_modules)
+ log("%s\n", id2cstr(it));
+ for (auto &it : sel.selected_members)
+ for (auto &it2 : it.second)
+ log("%s/%s\n", id2cstr(it.first), id2cstr(it2));
+ return;
+ }
+
+ design->selection_stack.back() = work_stack.back();
+ design->selection_stack.back().optimize(design);
+ }
+} SelectPass;
+
+struct CdPass : public Pass {
+ CdPass() : Pass("cd", "a shortcut for 'select -module <name>'") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" cd <modname>\n");
+ log("\n");
+ log("This is just a shortcut for 'select -module <modname>'.\n");
+ log("\n");
+ log("\n");
+ log(" cd <cellname>\n");
+ log("\n");
+ log("When no module with the specified name is found, but there is a cell\n");
+ log("with the specified name in the current module, then this is equivalent\n");
+ log("to 'cd <celltype>'.\n");
+ log("\n");
+ log(" cd ..\n");
+ log("\n");
+ log("This is just a shortcut for 'select -clear'.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ if (args.size() != 2)
+ log_cmd_error("Invalid number of arguments.\n");
+
+ if (args[1] == "..") {
+ design->selection_stack.back() = RTLIL::Selection(true);
+ design->selected_active_module = std::string();
+ return;
+ }
+
+ std::string modname = RTLIL::escape_id(args[1]);
+
+ if (design->modules_.count(modname) == 0 && !design->selected_active_module.empty()) {
+ RTLIL::Module *module = NULL;
+ if (design->modules_.count(design->selected_active_module) > 0)
+ module = design->modules_.at(design->selected_active_module);
+ if (module != NULL && module->cells_.count(modname) > 0)
+ modname = module->cells_.at(modname)->type.str();
+ }
+
+ if (design->modules_.count(modname) > 0) {
+ design->selected_active_module = modname;
+ design->selection_stack.back() = RTLIL::Selection();
+ select_filter_active_mod(design, design->selection_stack.back());
+ design->selection_stack.back().optimize(design);
+ return;
+ }
+
+ log_cmd_error("No such module `%s' found!\n", RTLIL::unescape_id(modname).c_str());
+ }
+} CdPass;
+
+template<typename T>
+static void log_matches(const char *title, Module *module, T list)
+{
+ std::vector<IdString> matches;
+
+ for (auto &it : list)
+ if (module->selected(it.second))
+ matches.push_back(it.first);
+
+ if (!matches.empty()) {
+ log("\n%d %s:\n", int(matches.size()), title);
+ std::sort(matches.begin(), matches.end(), RTLIL::sort_by_id_str());
+ for (auto id : matches)
+ log(" %s\n", RTLIL::id2cstr(id));
+ }
+}
+
+struct LsPass : public Pass {
+ LsPass() : Pass("ls", "list modules or objects in modules") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" ls [selection]\n");
+ log("\n");
+ log("When no active module is selected, this prints a list of modules.\n");
+ log("\n");
+ log("When an active module is selected, this prints a list of objects in the module.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ size_t argidx = 1;
+ extra_args(args, argidx, design);
+
+ if (design->selected_active_module.empty())
+ {
+ std::vector<IdString> matches;
+
+ for (auto mod : design->selected_modules())
+ matches.push_back(mod->name);
+
+ if (!matches.empty()) {
+ log("\n%d %s:\n", int(matches.size()), "modules");
+ std::sort(matches.begin(), matches.end(), RTLIL::sort_by_id_str());
+ for (auto id : matches)
+ log(" %s%s\n", log_id(id), design->selected_whole_module(design->module(id)) ? "" : "*");
+ }
+ }
+ else
+ if (design->module(design->selected_active_module) != nullptr)
+ {
+ RTLIL::Module *module = design->module(design->selected_active_module);
+ log_matches("wires", module, module->wires_);
+ log_matches("memories", module, module->memories);
+ log_matches("cells", module, module->cells_);
+ log_matches("processes", module, module->processes);
+ }
+ }
+} LsPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/setattr.cc b/passes/cmds/setattr.cc
new file mode 100644
index 00000000..689e3148
--- /dev/null
+++ b/passes/cmds/setattr.cc
@@ -0,0 +1,271 @@
+/*
+ * 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/register.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct setunset_t
+{
+ RTLIL::IdString name;
+ RTLIL::Const value;
+ bool unset;
+
+ setunset_t(std::string unset_name) : name(RTLIL::escape_id(unset_name)), value(), unset(true) { }
+
+ setunset_t(std::string set_name, std::string set_value) : name(RTLIL::escape_id(set_name)), value(), unset(false)
+ {
+ if (set_value.substr(0, 1) == "\"" && set_value.substr(GetSize(set_value)-1) == "\"") {
+ value = RTLIL::Const(set_value.substr(1, GetSize(set_value)-2));
+ } else {
+ RTLIL::SigSpec sig_value;
+ if (!RTLIL::SigSpec::parse(sig_value, NULL, set_value))
+ log_cmd_error("Can't decode value '%s'!\n", set_value.c_str());
+ value = sig_value.as_const();
+ }
+ }
+};
+
+static void do_setunset(dict<RTLIL::IdString, RTLIL::Const> &attrs, const std::vector<setunset_t> &list)
+{
+ for (auto &item : list)
+ if (item.unset)
+ attrs.erase(item.name);
+ else
+ attrs[item.name] = item.value;
+}
+
+struct SetattrPass : public Pass {
+ SetattrPass() : Pass("setattr", "set/unset attributes on objects") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" setattr [ -mod ] [ -set name value | -unset name ]... [selection]\n");
+ log("\n");
+ log("Set/unset the given attributes on the selected objects. String values must be\n");
+ log("passed in double quotes (\").\n");
+ log("\n");
+ log("When called with -mod, this command will set and unset attributes on modules\n");
+ log("instead of objects within modules.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::vector<setunset_t> setunset_list;
+ bool flag_mod = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-set" && argidx+2 < args.size()) {
+ string set_key = args[++argidx];
+ string set_val = args[++argidx];
+ setunset_list.push_back(setunset_t(set_key, set_val));
+ continue;
+ }
+ if (arg == "-unset" && argidx+1 < args.size()) {
+ setunset_list.push_back(setunset_t(args[++argidx]));
+ continue;
+ }
+ if (arg == "-mod") {
+ flag_mod = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto &mod : design->modules_)
+ {
+ RTLIL::Module *module = mod.second;
+
+ if (flag_mod) {
+ if (design->selected_whole_module(module->name))
+ do_setunset(module->attributes, setunset_list);
+ continue;
+ }
+
+ if (!design->selected(module))
+ continue;
+
+ for (auto &it : module->wires_)
+ if (design->selected(module, it.second))
+ do_setunset(it.second->attributes, setunset_list);
+
+ for (auto &it : module->memories)
+ if (design->selected(module, it.second))
+ do_setunset(it.second->attributes, setunset_list);
+
+ for (auto &it : module->cells_)
+ if (design->selected(module, it.second))
+ do_setunset(it.second->attributes, setunset_list);
+
+ for (auto &it : module->processes)
+ if (design->selected(module, it.second))
+ do_setunset(it.second->attributes, setunset_list);
+ }
+ }
+} SetattrPass;
+
+struct SetparamPass : public Pass {
+ SetparamPass() : Pass("setparam", "set/unset parameters on objects") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" setparam [ -type cell_type ] [ -set name value | -unset name ]... [selection]\n");
+ log("\n");
+ log("Set/unset the given parameters on the selected cells. String values must be\n");
+ log("passed in double quotes (\").\n");
+ log("\n");
+ log("The -type option can be used to change the cell type of the selected cells.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ vector<setunset_t> setunset_list;
+ string new_cell_type;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-set" && argidx+2 < args.size()) {
+ string set_key = args[++argidx];
+ string set_val = args[++argidx];
+ setunset_list.push_back(setunset_t(set_key, set_val));
+ continue;
+ }
+ if (arg == "-unset" && argidx+1 < args.size()) {
+ setunset_list.push_back(setunset_t(args[++argidx]));
+ continue;
+ }
+ if (arg == "-type" && argidx+1 < args.size()) {
+ new_cell_type = RTLIL::escape_id(args[++argidx]);
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto &mod : design->modules_)
+ {
+ RTLIL::Module *module = mod.second;
+
+ if (!design->selected(module))
+ continue;
+
+ for (auto &it : module->cells_)
+ if (design->selected(module, it.second)) {
+ if (!new_cell_type.empty())
+ it.second->type = new_cell_type;
+ do_setunset(it.second->parameters, setunset_list);
+ }
+ }
+ }
+} SetparamPass;
+
+struct ChparamPass : public Pass {
+ ChparamPass() : Pass("chparam", "re-evaluate modules with new parameters") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" chparam [ -set name value ]... [selection]\n");
+ log("\n");
+ log("Re-evaluate the selected modules with new parameters. String values must be\n");
+ log("passed in double quotes (\").\n");
+ log("\n");
+ log("\n");
+ log(" chparam -list [selection]\n");
+ log("\n");
+ log("List the available parameters of the selected modules.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::vector<setunset_t> setunset_list;
+ dict<RTLIL::IdString, RTLIL::Const> new_parameters;
+ bool list_mode = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-set" && argidx+2 < args.size()) {
+ string set_key = args[++argidx];
+ string set_val = args[++argidx];
+ setunset_list.push_back(setunset_t(set_key, set_val));
+ continue;
+ }
+ if (arg == "-list") {
+ list_mode = true;
+ continue;
+ }
+ break;
+ }
+
+ for (int i = argidx; i < GetSize(args); i++)
+ if (design->module("$abstract\\" + args[i]) != nullptr &&
+ design->module(RTLIL::escape_id(args[i])) == nullptr)
+ args[i] = "$abstract\\" + args[i];
+
+ extra_args(args, argidx, design);
+
+ do_setunset(new_parameters, setunset_list);
+
+ if (list_mode) {
+ if (!new_parameters.empty())
+ log_cmd_error("The options -set and -list cannot be used together.\n");
+ for (auto module : design->selected_modules()) {
+ log("%s:\n", log_id(module));
+ for (auto param : module->avail_parameters)
+ log(" %s\n", log_id(param));
+ }
+ return;
+ }
+
+ pool<IdString> modnames, old_modnames;
+ for (auto module : design->selected_whole_modules_warn()) {
+ modnames.insert(module->name);
+ old_modnames.insert(module->name);
+ }
+ modnames.sort();
+
+ for (auto modname : modnames) {
+ Module *module = design->module(modname);
+ Module *new_module = design->module(module->derive(design, new_parameters));
+ if (module != new_module) {
+ Module *m = new_module->clone();
+ m->name = module->name;
+ design->remove(module);
+ design->add(m);
+ }
+ if (old_modnames.count(new_module->name) == 0)
+ design->remove(new_module);
+ }
+ }
+} ChparamPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc
new file mode 100644
index 00000000..26b2eb87
--- /dev/null
+++ b/passes/cmds/setundef.cc
@@ -0,0 +1,243 @@
+/*
+ * 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/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/sigtools.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct SetundefWorker
+{
+ int next_bit_mode;
+ uint32_t next_bit_state;
+
+ RTLIL::State next_bit()
+ {
+ if (next_bit_mode == 0)
+ return RTLIL::State::S0;
+
+ if (next_bit_mode == 1)
+ return RTLIL::State::S1;
+
+ // xorshift32
+ next_bit_state ^= next_bit_state << 13;
+ next_bit_state ^= next_bit_state >> 17;
+ next_bit_state ^= next_bit_state << 5;
+ log_assert(next_bit_state != 0);
+
+ return ((next_bit_state >> (next_bit_state & 15)) & 16) ? RTLIL::State::S0 : RTLIL::State::S1;
+ }
+
+ void operator()(RTLIL::SigSpec &sig)
+ {
+ for (auto &bit : sig)
+ if (bit.wire == NULL && bit.data > RTLIL::State::S1)
+ bit = next_bit();
+ }
+};
+
+struct SetundefPass : public Pass {
+ SetundefPass() : Pass("setundef", "replace undef values with defined constants") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" setundef [options] [selection]\n");
+ log("\n");
+ log("This command replaced undef (x) constants with defined (0/1) constants.\n");
+ log("\n");
+ log(" -undriven\n");
+ log(" also set undriven nets to constant values\n");
+ log("\n");
+ log(" -zero\n");
+ log(" replace with bits cleared (0)\n");
+ log("\n");
+ log(" -one\n");
+ log(" replace with bits set (1)\n");
+ log("\n");
+ log(" -random <seed>\n");
+ log(" replace with random bits using the specified integer als 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");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool got_value = false;
+ bool undriven_mode = false;
+ bool init_mode = false;
+ SetundefWorker worker;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-undriven") {
+ undriven_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-zero") {
+ got_value = true;
+ worker.next_bit_mode = 0;
+ continue;
+ }
+ if (args[argidx] == "-one") {
+ got_value = true;
+ worker.next_bit_mode = 1;
+ continue;
+ }
+ if (args[argidx] == "-init") {
+ init_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) {
+ got_value = true;
+ worker.next_bit_mode = 2;
+ worker.next_bit_state = atoi(args[++argidx].c_str()) + 1;
+ for (int i = 0; i < 10; i++)
+ worker.next_bit();
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!got_value)
+ log_cmd_error("One of the options -zero, -one, or -random <seed> must be specified.\n");
+
+ for (auto module : design->selected_modules())
+ {
+ if (undriven_mode)
+ {
+ if (!module->processes.empty())
+ log_error("The 'setundef' command can't operate in -undriven mode on modules with processes. Run 'proc' first.\n");
+
+ SigMap sigmap(module);
+ SigPool undriven_signals;
+
+ for (auto &it : module->wires_)
+ if (!it.second->port_input)
+ undriven_signals.add(sigmap(it.second));
+
+ CellTypes ct(design);
+ for (auto &it : module->cells_)
+ for (auto &conn : it.second->connections())
+ if (!ct.cell_known(it.second->type) || ct.cell_output(it.second->type, conn.first))
+ undriven_signals.del(sigmap(conn.second));
+
+ RTLIL::SigSpec sig = undriven_signals.export_all();
+ for (auto &c : sig.chunks()) {
+ RTLIL::SigSpec bits;
+ for (int i = 0; i < c.width; i++)
+ bits.append(worker.next_bit());
+ module->connect(RTLIL::SigSig(c, bits));
+ }
+ }
+
+ if (init_mode)
+ {
+ SigMap sigmap(module);
+ pool<SigBit> ffbits;
+ pool<Wire*> initwires;
+
+ pool<IdString> fftypes;
+ fftypes.insert("$dff");
+ fftypes.insert("$dffe");
+ fftypes.insert("$dffsr");
+ fftypes.insert("$adff");
+
+ std::vector<char> list_np = {'N', 'P'}, list_01 = {'0', '1'};
+
+ for (auto c1 : list_np)
+ fftypes.insert(stringf("$_DFF_%c_", c1));
+
+ for (auto c1 : list_np)
+ for (auto c2 : list_np)
+ fftypes.insert(stringf("$_DFFE_%c%c_", c1, c2));
+
+ for (auto c1 : list_np)
+ for (auto c2 : list_np)
+ for (auto c3 : list_01)
+ fftypes.insert(stringf("$_DFF_%c%c%c_", c1, c2, c3));
+
+ for (auto c1 : list_np)
+ for (auto c2 : list_np)
+ for (auto c3 : list_np)
+ fftypes.insert(stringf("$_DFFSR_%c%c%c_", c1, c2, c3));
+
+ for (auto cell : module->cells())
+ {
+ if (!fftypes.count(cell->type))
+ continue;
+
+ for (auto bit : sigmap(cell->getPort("\\Q")))
+ ffbits.insert(bit);
+ }
+
+ for (auto wire : module->wires())
+ {
+ if (!wire->attributes.count("\\init"))
+ continue;
+
+ for (auto bit : sigmap(wire))
+ ffbits.erase(bit);
+
+ initwires.insert(wire);
+ }
+
+ for (int wire_types = 0; wire_types < 2; wire_types++)
+ for (auto wire : module->wires())
+ {
+ if (wire->name[0] == (wire_types ? '\\' : '$'))
+ next_wire:
+ continue;
+
+ for (auto bit : sigmap(wire))
+ if (!ffbits.count(bit))
+ goto next_wire;
+
+ for (auto bit : sigmap(wire))
+ ffbits.erase(bit);
+
+ initwires.insert(wire);
+ }
+
+ for (auto wire : initwires)
+ {
+ Const &initval = wire->attributes["\\init"];
+
+ for (int i = 0; i < GetSize(wire); i++)
+ if (GetSize(initval) <= i)
+ initval.bits.push_back(worker.next_bit());
+ else if (initval.bits[i] == State::Sx)
+ initval.bits[i] = worker.next_bit();
+ }
+ }
+
+ module->rewrite_sigspecs(worker);
+ }
+ }
+} SetundefPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc
new file mode 100644
index 00000000..3a3939a8
--- /dev/null
+++ b/passes/cmds/show.cc
@@ -0,0 +1,851 @@
+/*
+ * 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/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/log.h"
+#include <string.h>
+
+#ifndef _WIN32
+# include <dirent.h>
+#endif
+
+#ifdef YOSYS_ENABLE_READLINE
+# include <readline/readline.h>
+#endif
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+using RTLIL::id2cstr;
+
+#undef CLUSTER_CELLS_AND_PORTBOXES
+
+struct ShowWorker
+{
+ CellTypes ct;
+
+ vector<shared_str> dot_escape_store;
+ std::map<RTLIL::IdString, int> dot_id2num_store;
+ std::map<RTLIL::IdString, int> autonames;
+ int single_idx_count;
+
+ struct net_conn { std::set<std::string> in, out; int bits; std::string color; };
+ std::map<std::string, net_conn> net_conn_map;
+
+ FILE *f;
+ RTLIL::Design *design;
+ RTLIL::Module *module;
+ uint32_t currentColor;
+ bool genWidthLabels;
+ bool genSignedLabels;
+ bool stretchIO;
+ bool enumerateIds;
+ bool abbreviateIds;
+ bool notitle;
+ int page_counter;
+
+ const std::vector<std::pair<std::string, RTLIL::Selection>> &color_selections;
+ const std::vector<std::pair<std::string, RTLIL::Selection>> &label_selections;
+
+ std::map<RTLIL::Const, int> colorattr_cache;
+ RTLIL::IdString colorattr;
+
+
+ static uint32_t xorshift32(uint32_t x) {
+ x ^= x << 13;
+ x ^= x >> 17;
+ x ^= x << 5;
+ return x;
+ }
+
+ std::string nextColor()
+ {
+ if (currentColor == 0)
+ return "color=\"black\"";
+ return stringf("colorscheme=\"dark28\", color=\"%d\", fontcolor=\"%d\"", currentColor%8+1, currentColor%8+1);
+ }
+
+ std::string nextColor(std::string presetColor)
+ {
+ if (presetColor.empty())
+ return nextColor();
+ return presetColor;
+ }
+
+ std::string nextColor(RTLIL::SigSpec sig, std::string defaultColor)
+ {
+ sig.sort_and_unify();
+ for (auto &c : sig.chunks()) {
+ if (c.wire != NULL)
+ for (auto &s : color_selections)
+ if (s.second.selected_members.count(module->name) > 0 && s.second.selected_members.at(module->name).count(c.wire->name) > 0)
+ return stringf("color=\"%s\"", s.first.c_str());
+ }
+ return defaultColor;
+ }
+
+ std::string nextColor(const RTLIL::SigSig &conn, std::string defaultColor)
+ {
+ return nextColor(conn.first, nextColor(conn.second, defaultColor));
+ }
+
+ std::string nextColor(const RTLIL::SigSpec &sig)
+ {
+ return nextColor(sig, nextColor());
+ }
+
+ std::string nextColor(const RTLIL::SigSig &conn)
+ {
+ return nextColor(conn, nextColor());
+ }
+
+ std::string widthLabel(int bits)
+ {
+ if (bits <= 1)
+ return "label=\"\"";
+ if (!genWidthLabels)
+ return "style=\"setlinewidth(3)\", label=\"\"";
+ return stringf("style=\"setlinewidth(3)\", label=\"<%d>\"", bits);
+ }
+
+ const char *findColor(std::string member_name)
+ {
+ for (auto &s : color_selections)
+ if (s.second.selected_member(module->name, member_name)) {
+ dot_escape_store.push_back(stringf(", color=\"%s\"", s.first.c_str()));
+ return dot_escape_store.back().c_str();
+ }
+
+ RTLIL::Const colorattr_value;
+ RTLIL::Cell *cell = module->cell(member_name);
+ RTLIL::Wire *wire = module->wire(member_name);
+
+ if (cell && cell->attributes.count(colorattr))
+ colorattr_value = cell->attributes.at(colorattr);
+ else if (wire && wire->attributes.count(colorattr))
+ colorattr_value = wire->attributes.at(colorattr);
+ else
+ return "";
+
+ if (colorattr_cache.count(colorattr_value) == 0) {
+ int next_id = GetSize(colorattr_cache);
+ colorattr_cache[colorattr_value] = (next_id % 8) + 1;
+ }
+
+ dot_escape_store.push_back(stringf(", colorscheme=\"dark28\", color=\"%d\", fontcolor=\"%d\"", colorattr_cache.at(colorattr_value), colorattr_cache.at(colorattr_value)));
+ return dot_escape_store.back().c_str();
+ }
+
+ const char *findLabel(std::string member_name)
+ {
+ for (auto &s : label_selections)
+ if (s.second.selected_member(module->name, member_name))
+ return escape(s.first);
+ return escape(member_name, true);
+ }
+
+ const char *escape(std::string id, bool is_name = false)
+ {
+ if (id.size() == 0)
+ return "";
+
+ if (id[0] == '$' && is_name) {
+ if (enumerateIds) {
+ if (autonames.count(id) == 0) {
+ autonames[id] = autonames.size() + 1;
+ log("Generated short name for internal identifier: _%d_ -> %s\n", autonames[id], id.c_str());
+ }
+ id = stringf("_%d_", autonames[id]);
+ } else if (abbreviateIds) {
+ const char *p = id.c_str();
+ const char *q = strrchr(p, '$');
+ id = std::string(q);
+ }
+ }
+
+ if (id[0] == '\\')
+ id = id.substr(1);
+
+ std::string str;
+ for (char ch : id) {
+ if (ch == '\\' || ch == '"')
+ str += "\\";
+ str += ch;
+ }
+
+ dot_escape_store.push_back(str);
+ return dot_escape_store.back().c_str();
+ }
+
+ int id2num(RTLIL::IdString id)
+ {
+ if (dot_id2num_store.count(id) > 0)
+ return dot_id2num_store[id];
+ return dot_id2num_store[id] = dot_id2num_store.size() + 1;
+ }
+
+ std::string gen_signode_simple(RTLIL::SigSpec sig, bool range_check = true)
+ {
+ if (GetSize(sig) == 0) {
+ fprintf(f, "v%d [ label=\"\" ];\n", single_idx_count);
+ return stringf("v%d", single_idx_count++);
+ }
+
+ if (sig.is_chunk()) {
+ const RTLIL::SigChunk &c = sig.as_chunk();
+ if (c.wire != NULL && design->selected_member(module->name, c.wire->name)) {
+ if (!range_check || c.wire->width == c.width)
+ return stringf("n%d", id2num(c.wire->name));
+ } else {
+ fprintf(f, "v%d [ label=\"%s\" ];\n", single_idx_count, findLabel(log_signal(c)));
+ return stringf("v%d", single_idx_count++);
+ }
+ }
+
+ return std::string();
+ }
+
+ std::string gen_portbox(std::string port, RTLIL::SigSpec sig, bool driver, std::string *node = NULL)
+ {
+ std::string code;
+ std::string net = gen_signode_simple(sig);
+ if (net.empty())
+ {
+ std::string label_string;
+ int pos = sig.size()-1;
+ int idx = single_idx_count++;
+ for (int rep, i = int(sig.chunks().size())-1; i >= 0; i -= rep) {
+ const RTLIL::SigChunk &c = sig.chunks().at(i);
+ net = gen_signode_simple(c, false);
+ log_assert(!net.empty());
+ for (rep = 1; i-rep >= 0 && c == sig.chunks().at(i-rep); rep++) {}
+ std::string repinfo = rep > 1 ? stringf("%dx ", rep) : "";
+ if (driver) {
+ label_string += stringf("<s%d> %d:%d - %s%d:%d |", i, pos, pos-c.width+1, repinfo.c_str(), c.offset+c.width-1, c.offset);
+ net_conn_map[net].in.insert(stringf("x%d:s%d", idx, i));
+ net_conn_map[net].bits = rep*c.width;
+ net_conn_map[net].color = nextColor(c, net_conn_map[net].color);
+ } else {
+ label_string += stringf("<s%d> %s%d:%d - %d:%d |", i, repinfo.c_str(), c.offset+c.width-1, c.offset, pos, pos-rep*c.width+1);
+ net_conn_map[net].out.insert(stringf("x%d:s%d", idx, i));
+ net_conn_map[net].bits = rep*c.width;
+ net_conn_map[net].color = nextColor(c, net_conn_map[net].color);
+ }
+ pos -= rep * c.width;
+ }
+ if (label_string[label_string.size()-1] == '|')
+ label_string = label_string.substr(0, label_string.size()-1);
+ code += stringf("x%d [ shape=record, style=rounded, label=\"%s\" ];\n", idx, label_string.c_str());
+ if (!port.empty()) {
+ currentColor = xorshift32(currentColor);
+ if (driver)
+ code += stringf("%s:e -> x%d:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", port.c_str(), idx, nextColor(sig).c_str(), widthLabel(sig.size()).c_str());
+ else
+ code += stringf("x%d:e -> %s:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", idx, port.c_str(), nextColor(sig).c_str(), widthLabel(sig.size()).c_str());
+ }
+ if (node != NULL)
+ *node = stringf("x%d", idx);
+ }
+ else
+ {
+ if (!port.empty()) {
+ if (driver)
+ net_conn_map[net].in.insert(port);
+ else
+ net_conn_map[net].out.insert(port);
+ net_conn_map[net].bits = sig.size();
+ net_conn_map[net].color = nextColor(sig, net_conn_map[net].color);
+ }
+ if (node != NULL)
+ *node = net;
+ }
+ return code;
+ }
+
+ void collect_proc_signals(std::vector<RTLIL::SigSpec> &obj, std::set<RTLIL::SigSpec> &signals)
+ {
+ for (auto &it : obj)
+ if (!it.is_fully_const())
+ signals.insert(it);
+ }
+
+ void collect_proc_signals(std::vector<RTLIL::SigSig> &obj, std::set<RTLIL::SigSpec> &input_signals, std::set<RTLIL::SigSpec> &output_signals)
+ {
+ for (auto &it : obj) {
+ output_signals.insert(it.first);
+ if (!it.second.is_fully_const())
+ input_signals.insert(it.second);
+ }
+ }
+
+ void collect_proc_signals(RTLIL::CaseRule *obj, std::set<RTLIL::SigSpec> &input_signals, std::set<RTLIL::SigSpec> &output_signals)
+ {
+ collect_proc_signals(obj->compare, input_signals);
+ collect_proc_signals(obj->actions, input_signals, output_signals);
+ for (auto it : obj->switches)
+ collect_proc_signals(it, input_signals, output_signals);
+ }
+
+ void collect_proc_signals(RTLIL::SwitchRule *obj, std::set<RTLIL::SigSpec> &input_signals, std::set<RTLIL::SigSpec> &output_signals)
+ {
+ input_signals.insert(obj->signal);
+ for (auto it : obj->cases)
+ collect_proc_signals(it, input_signals, output_signals);
+ }
+
+ void collect_proc_signals(RTLIL::SyncRule *obj, std::set<RTLIL::SigSpec> &input_signals, std::set<RTLIL::SigSpec> &output_signals)
+ {
+ input_signals.insert(obj->signal);
+ collect_proc_signals(obj->actions, input_signals, output_signals);
+ }
+
+ void collect_proc_signals(RTLIL::Process *obj, std::set<RTLIL::SigSpec> &input_signals, std::set<RTLIL::SigSpec> &output_signals)
+ {
+ collect_proc_signals(&obj->root_case, input_signals, output_signals);
+ for (auto it : obj->syncs)
+ collect_proc_signals(it, input_signals, output_signals);
+ }
+
+ void handle_module()
+ {
+ single_idx_count = 0;
+ dot_escape_store.clear();
+ dot_id2num_store.clear();
+ net_conn_map.clear();
+
+ fprintf(f, "digraph \"%s\" {\n", escape(module->name.str()));
+ if (!notitle)
+ fprintf(f, "label=\"%s\";\n", escape(module->name.str()));
+ fprintf(f, "rankdir=\"LR\";\n");
+ fprintf(f, "remincross=true;\n");
+
+ std::set<std::string> all_sources, all_sinks;
+
+ std::map<std::string, std::string> wires_on_demand;
+ for (auto &it : module->wires_) {
+ if (!design->selected_member(module->name, it.first))
+ continue;
+ const char *shape = "diamond";
+ if (it.second->port_input || it.second->port_output)
+ shape = "octagon";
+ if (it.first[0] == '\\') {
+ fprintf(f, "n%d [ shape=%s, label=\"%s\", %s, fontcolor=\"black\" ];\n",
+ id2num(it.first), shape, findLabel(it.first.str()),
+ nextColor(RTLIL::SigSpec(it.second), "color=\"black\"").c_str());
+ if (it.second->port_input)
+ all_sources.insert(stringf("n%d", id2num(it.first)));
+ else if (it.second->port_output)
+ all_sinks.insert(stringf("n%d", id2num(it.first)));
+ } else {
+ wires_on_demand[stringf("n%d", id2num(it.first))] = it.first.str();
+ }
+ }
+
+ if (stretchIO)
+ {
+ fprintf(f, "{ rank=\"source\";");
+ for (auto n : all_sources)
+ fprintf(f, " %s;", n.c_str());
+ fprintf(f, "}\n");
+
+ fprintf(f, "{ rank=\"sink\";");
+ for (auto n : all_sinks)
+ fprintf(f, " %s;", n.c_str());
+ fprintf(f, "}\n");
+ }
+
+ for (auto &it : module->cells_)
+ {
+ if (!design->selected_member(module->name, it.first))
+ continue;
+
+ std::vector<RTLIL::IdString> in_ports, out_ports;
+
+ for (auto &conn : it.second->connections()) {
+ if (!ct.cell_output(it.second->type, conn.first))
+ in_ports.push_back(conn.first);
+ else
+ out_ports.push_back(conn.first);
+ }
+
+ std::sort(in_ports.begin(), in_ports.end(), RTLIL::sort_by_id_str());
+ std::sort(out_ports.begin(), out_ports.end(), RTLIL::sort_by_id_str());
+
+ std::string label_string = "{{";
+
+ for (auto &p : in_ports)
+ label_string += stringf("<p%d> %s%s|", id2num(p), escape(p.str()),
+ genSignedLabels && it.second->hasParam(p.str() + "_SIGNED") &&
+ it.second->getParam(p.str() + "_SIGNED").as_bool() ? "*" : "");
+ if (label_string[label_string.size()-1] == '|')
+ label_string = label_string.substr(0, label_string.size()-1);
+
+ label_string += stringf("}|%s\\n%s|{", findLabel(it.first.str()), escape(it.second->type.str()));
+
+ for (auto &p : out_ports)
+ label_string += stringf("<p%d> %s|", id2num(p), escape(p.str()));
+ if (label_string[label_string.size()-1] == '|')
+ label_string = label_string.substr(0, label_string.size()-1);
+
+ label_string += "}}";
+
+ std::string code;
+ for (auto &conn : it.second->connections()) {
+ code += gen_portbox(stringf("c%d:p%d", id2num(it.first), id2num(conn.first)),
+ conn.second, ct.cell_output(it.second->type, conn.first));
+ }
+
+#ifdef CLUSTER_CELLS_AND_PORTBOXES
+ if (!code.empty())
+ fprintf(f, "subgraph cluster_c%d {\nc%d [ shape=record, label=\"%s\"%s ];\n%s}\n",
+ id2num(it.first), id2num(it.first), label_string.c_str(), findColor(it.first), code.c_str());
+ else
+#endif
+ fprintf(f, "c%d [ shape=record, label=\"%s\"%s ];\n%s",
+ id2num(it.first), label_string.c_str(), findColor(it.first.str()), code.c_str());
+ }
+
+ for (auto &it : module->processes)
+ {
+ RTLIL::Process *proc = it.second;
+
+ if (!design->selected_member(module->name, proc->name))
+ continue;
+
+ std::set<RTLIL::SigSpec> input_signals, output_signals;
+ collect_proc_signals(proc, input_signals, output_signals);
+
+ int pidx = single_idx_count++;
+ input_signals.erase(RTLIL::SigSpec());
+ output_signals.erase(RTLIL::SigSpec());
+
+ for (auto &sig : input_signals) {
+ std::string code, node;
+ code += gen_portbox("", sig, false, &node);
+ fprintf(f, "%s", code.c_str());
+ net_conn_map[node].out.insert(stringf("p%d", pidx));
+ net_conn_map[node].bits = sig.size();
+ net_conn_map[node].color = nextColor(sig, net_conn_map[node].color);
+ }
+
+ for (auto &sig : output_signals) {
+ std::string code, node;
+ code += gen_portbox("", sig, true, &node);
+ fprintf(f, "%s", code.c_str());
+ net_conn_map[node].in.insert(stringf("p%d", pidx));
+ net_conn_map[node].bits = sig.size();
+ net_conn_map[node].color = nextColor(sig, net_conn_map[node].color);
+ }
+
+ std::string proc_src = RTLIL::unescape_id(proc->name);
+ if (proc->attributes.count("\\src") > 0)
+ proc_src = proc->attributes.at("\\src").decode_string();
+ fprintf(f, "p%d [shape=box, style=rounded, label=\"PROC %s\\n%s\"];\n", pidx, findLabel(proc->name.str()), proc_src.c_str());
+ }
+
+ for (auto &conn : module->connections())
+ {
+ bool found_lhs_wire = false;
+ for (auto &c : conn.first.chunks()) {
+ if (c.wire == NULL || design->selected_member(module->name, c.wire->name))
+ found_lhs_wire = true;
+ }
+ bool found_rhs_wire = false;
+ for (auto &c : conn.second.chunks()) {
+ if (c.wire == NULL || design->selected_member(module->name, c.wire->name))
+ found_rhs_wire = true;
+ }
+ if (!found_lhs_wire || !found_rhs_wire)
+ continue;
+
+ std::string code, left_node, right_node;
+ code += gen_portbox("", conn.second, false, &left_node);
+ code += gen_portbox("", conn.first, true, &right_node);
+ fprintf(f, "%s", code.c_str());
+
+ if (left_node[0] == 'x' && right_node[0] == 'x') {
+ currentColor = xorshift32(currentColor);
+ fprintf(f, "%s:e -> %s:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", left_node.c_str(), right_node.c_str(), nextColor(conn).c_str(), widthLabel(conn.first.size()).c_str());
+ } else {
+ net_conn_map[right_node].bits = conn.first.size();
+ net_conn_map[right_node].color = nextColor(conn, net_conn_map[right_node].color);
+ net_conn_map[left_node].bits = conn.first.size();
+ net_conn_map[left_node].color = nextColor(conn, net_conn_map[left_node].color);
+ if (left_node[0] == 'x') {
+ net_conn_map[right_node].in.insert(left_node);
+ } else if (right_node[0] == 'x') {
+ net_conn_map[left_node].out.insert(right_node);
+ } else {
+ net_conn_map[right_node].in.insert(stringf("x%d:e", single_idx_count));
+ net_conn_map[left_node].out.insert(stringf("x%d:w", single_idx_count));
+ fprintf(f, "x%d [shape=box, style=rounded, label=\"BUF\"];\n", single_idx_count++);
+ }
+ }
+ }
+
+ for (auto &it : net_conn_map)
+ {
+ currentColor = xorshift32(currentColor);
+ if (wires_on_demand.count(it.first) > 0) {
+ if (it.second.in.size() == 1 && it.second.out.size() > 1 && it.second.in.begin()->substr(0, 1) == "p")
+ it.second.out.erase(*it.second.in.begin());
+ if (it.second.in.size() == 1 && it.second.out.size() == 1) {
+ std::string from = *it.second.in.begin(), to = *it.second.out.begin();
+ if (from != to || from.substr(0, 1) != "p")
+ fprintf(f, "%s:e -> %s:w [%s, %s];\n", from.c_str(), to.c_str(), nextColor(it.second.color).c_str(), widthLabel(it.second.bits).c_str());
+ continue;
+ }
+ if (it.second.in.size() == 0 || it.second.out.size() == 0)
+ fprintf(f, "%s [ shape=diamond, label=\"%s\" ];\n", it.first.c_str(), findLabel(wires_on_demand[it.first]));
+ else
+ fprintf(f, "%s [ shape=point ];\n", it.first.c_str());
+ }
+ for (auto &it2 : it.second.in)
+ fprintf(f, "%s:e -> %s:w [%s, %s];\n", it2.c_str(), it.first.c_str(), nextColor(it.second.color).c_str(), widthLabel(it.second.bits).c_str());
+ for (auto &it2 : it.second.out)
+ fprintf(f, "%s:e -> %s:w [%s, %s];\n", it.first.c_str(), it2.c_str(), nextColor(it.second.color).c_str(), widthLabel(it.second.bits).c_str());
+ }
+
+ fprintf(f, "}\n");
+ }
+
+ ShowWorker(FILE *f, RTLIL::Design *design, std::vector<RTLIL::Design*> &libs, uint32_t colorSeed, bool genWidthLabels,
+ bool genSignedLabels, bool stretchIO, bool enumerateIds, bool abbreviateIds, bool notitle,
+ const std::vector<std::pair<std::string, RTLIL::Selection>> &color_selections,
+ const std::vector<std::pair<std::string, RTLIL::Selection>> &label_selections, RTLIL::IdString colorattr) :
+ f(f), design(design), currentColor(colorSeed), genWidthLabels(genWidthLabels),
+ genSignedLabels(genSignedLabels), stretchIO(stretchIO), enumerateIds(enumerateIds), abbreviateIds(abbreviateIds),
+ notitle(notitle), color_selections(color_selections), label_selections(label_selections), colorattr(colorattr)
+ {
+ ct.setup_internals();
+ ct.setup_internals_mem();
+ ct.setup_stdcells();
+ ct.setup_stdcells_mem();
+ ct.setup_design(design);
+
+ for (auto lib : libs)
+ ct.setup_design(lib);
+
+ design->optimize();
+ page_counter = 0;
+ for (auto &mod_it : design->modules_)
+ {
+ module = mod_it.second;
+ if (!design->selected_module(module->name))
+ continue;
+ if (design->selected_whole_module(module->name)) {
+ if (module->get_bool_attribute("\\blackbox")) {
+ // log("Skipping blackbox module %s.\n", id2cstr(module->name));
+ continue;
+ } else
+ if (module->cells_.empty() && module->connections().empty() && module->processes.empty()) {
+ log("Skipping empty module %s.\n", id2cstr(module->name));
+ continue;
+ } else
+ log("Dumping module %s to page %d.\n", id2cstr(module->name), ++page_counter);
+ } else
+ log("Dumping selected parts of module %s to page %d.\n", id2cstr(module->name), ++page_counter);
+ handle_module();
+ }
+ }
+};
+
+struct ShowPass : public Pass {
+ ShowPass() : Pass("show", "generate schematics using graphviz") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" show [options] [selection]\n");
+ log("\n");
+ log("Create a graphviz DOT file for the selected part of the design and compile it\n");
+ log("to a graphics file (usually SVG or PostScript).\n");
+ log("\n");
+ log(" -viewer <viewer>\n");
+ log(" Run the specified command with the graphics file as parameter.\n");
+ log("\n");
+ log(" -format <format>\n");
+ log(" Generate a graphics file in the specified format. Use 'dot' to just\n");
+ log(" generate a .dot file, or other <format> strings such as 'svg' or 'ps'\n");
+ log(" to generate files in other formats (this calls the 'dot' command).\n");
+ log("\n");
+ log(" -lib <verilog_or_ilang_file>\n");
+ log(" Use the specified library file for determining whether cell ports are\n");
+ log(" inputs or outputs. This option can be used multiple times to specify\n");
+ log(" more than one library.\n");
+ log("\n");
+ log(" note: in most cases it is better to load the library before calling\n");
+ log(" show with 'read_verilog -lib <filename>'. it is also possible to\n");
+ log(" load liberty files with 'read_liberty -lib <filename>'.\n");
+ log("\n");
+ log(" -prefix <prefix>\n");
+ log(" generate <prefix>.* instead of ~/.yosys_show.*\n");
+ log("\n");
+ log(" -color <color> <object>\n");
+ log(" assign the specified color to the specified object. The object can be\n");
+ log(" a single selection wildcard expressions or a saved set of objects in\n");
+ log(" the @<name> syntax (see \"help select\" for details).\n");
+ log("\n");
+ log(" -label <text> <object>\n");
+ log(" assign the specified label text to the specified object. The object can\n");
+ log(" be a single selection wildcard expressions or a saved set of objects in\n");
+ log(" the @<name> syntax (see \"help select\" for details).\n");
+ log("\n");
+ log(" -colors <seed>\n");
+ log(" Randomly assign colors to the wires. The integer argument is the seed\n");
+ log(" for the random number generator. Change the seed value if the colored\n");
+ log(" graph still is ambiguous. A seed of zero deactivates the coloring.\n");
+ log("\n");
+ log(" -colorattr <attribute_name>\n");
+ log(" Use the specified attribute to assign colors. A unique color is\n");
+ 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("\n");
+ log(" -signed\n");
+ log(" mark ports (A, B) that are declared as signed (using the [AB]_SIGNED\n");
+ log(" cell parameter) with an asterisk next to the port name.\n");
+ log("\n");
+ log(" -stretch\n");
+ log(" stretch the graph so all inputs are on the left side and all outputs\n");
+ log(" (including inout ports) are on the right side.\n");
+ log("\n");
+ log(" -pause\n");
+ log(" wait for the use to press enter to before returning\n");
+ log("\n");
+ log(" -enum\n");
+ log(" enumerate objects with internal ($-prefixed) names\n");
+ log("\n");
+ log(" -long\n");
+ log(" do not abbreviate objects with internal ($-prefixed) names\n");
+ log("\n");
+ log(" -notitle\n");
+ log(" do not add the module name as graph title to the dot file\n");
+ log("\n");
+ log("When no <format> is specified, 'dot' is used. When no <format> and <viewer> is\n");
+ log("specified, 'xdot' is used to display the schematic.\n");
+ log("\n");
+ log("The generated output files are '~/.yosys_show.dot' and '~/.yosys_show.<format>',\n");
+ log("unless another prefix is specified using -prefix <prefix>.\n");
+ log("\n");
+ log("Yosys on Windows and YosysJS use different defaults: The output is written\n");
+ log("to 'show.dot' in the current directory and new viewer is launched each time\n");
+ log("the 'show' command is executed.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Generating Graphviz representation of design.\n");
+ log_push();
+
+ std::vector<std::pair<std::string, RTLIL::Selection>> color_selections;
+ std::vector<std::pair<std::string, RTLIL::Selection>> label_selections;
+
+#if defined(EMSCRIPTEN) || defined(_WIN32)
+ std::string format = "dot";
+ std::string prefix = "show";
+#else
+ std::string format;
+ std::string prefix = stringf("%s/.yosys_show", getenv("HOME") ? getenv("HOME") : ".");
+#endif
+ std::string viewer_exe;
+ std::vector<std::string> libfiles;
+ std::vector<RTLIL::Design*> libs;
+ uint32_t colorSeed = 0;
+ bool flag_width = false;
+ bool flag_signed = false;
+ bool flag_stretch = false;
+ bool flag_pause = false;
+ bool flag_enum = false;
+ bool flag_abbreviate = true;
+ bool flag_notitle = false;
+ RTLIL::IdString colorattr;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-viewer" && argidx+1 < args.size()) {
+ viewer_exe = args[++argidx];
+ continue;
+ }
+ if (arg == "-lib" && argidx+1 < args.size()) {
+ libfiles.push_back(args[++argidx]);
+ continue;
+ }
+ if (arg == "-prefix" && argidx+1 < args.size()) {
+ prefix = args[++argidx];
+ continue;
+ }
+ if (arg == "-color" && argidx+2 < args.size()) {
+ std::pair<std::string, RTLIL::Selection> data;
+ data.first = args[++argidx], argidx++;
+ handle_extra_select_args(this, args, argidx, argidx+1, design);
+ data.second = design->selection_stack.back();
+ design->selection_stack.pop_back();
+ color_selections.push_back(data);
+ continue;
+ }
+ if (arg == "-label" && argidx+2 < args.size()) {
+ std::pair<std::string, RTLIL::Selection> data;
+ data.first = args[++argidx], argidx++;
+ handle_extra_select_args(this, args, argidx, argidx+1, design);
+ data.second = design->selection_stack.back();
+ design->selection_stack.pop_back();
+ label_selections.push_back(data);
+ continue;
+ }
+ if (arg == "-colors" && argidx+1 < args.size()) {
+ colorSeed = atoi(args[++argidx].c_str());
+ for (int i = 0; i < 100; i++)
+ colorSeed = ShowWorker::xorshift32(colorSeed);
+ continue;
+ }
+ if (arg == "-colorattr" && argidx+1 < args.size()) {
+ colorattr = RTLIL::escape_id(args[++argidx]);
+ continue;
+ }
+ if (arg == "-format" && argidx+1 < args.size()) {
+ format = args[++argidx];
+ continue;
+ }
+ if (arg == "-width") {
+ flag_width= true;
+ continue;
+ }
+ if (arg == "-signed") {
+ flag_signed= true;
+ continue;
+ }
+ if (arg == "-stretch") {
+ flag_stretch= true;
+ continue;
+ }
+ if (arg == "-pause") {
+ flag_pause= true;
+ continue;
+ }
+ if (arg == "-enum") {
+ flag_enum = true;
+ flag_abbreviate = false;
+ continue;
+ }
+ if (arg == "-long") {
+ flag_enum = false;
+ flag_abbreviate = false;
+ continue;
+ }
+ if (arg == "-notitle") {
+ flag_notitle = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (format != "ps" && format != "dot") {
+ int modcount = 0;
+ for (auto &mod_it : design->modules_) {
+ if (mod_it.second->get_bool_attribute("\\blackbox"))
+ continue;
+ if (mod_it.second->cells_.empty() && mod_it.second->connections().empty())
+ continue;
+ if (design->selected_module(mod_it.first))
+ modcount++;
+ }
+ if (modcount > 1)
+ log_cmd_error("For formats different than 'ps' or 'dot' only one module must be selected.\n");
+ }
+
+ for (auto filename : libfiles) {
+ std::ifstream f;
+ f.open(filename.c_str());
+ if (f.fail())
+ log_error("Can't open lib file `%s'.\n", filename.c_str());
+ RTLIL::Design *lib = new RTLIL::Design;
+ Frontend::frontend_call(lib, &f, filename, (filename.size() > 3 && filename.substr(filename.size()-3) == ".il") ? "ilang" : "verilog");
+ libs.push_back(lib);
+ }
+
+ if (libs.size() > 0)
+ log_header(design, "Continuing show pass.\n");
+
+ std::string dot_file = stringf("%s.dot", prefix.c_str());
+ std::string out_file = stringf("%s.%s", prefix.c_str(), format.empty() ? "svg" : format.c_str());
+
+ log("Writing dot description to `%s'.\n", dot_file.c_str());
+ FILE *f = fopen(dot_file.c_str(), "w");
+ if (f == NULL) {
+ for (auto lib : libs)
+ delete lib;
+ log_cmd_error("Can't open dot file `%s' for writing.\n", dot_file.c_str());
+ }
+ ShowWorker worker(f, design, libs, colorSeed, flag_width, flag_signed, flag_stretch, flag_enum, flag_abbreviate, flag_notitle, color_selections, label_selections, colorattr);
+ fclose(f);
+
+ for (auto lib : libs)
+ delete lib;
+
+ if (worker.page_counter == 0)
+ log_cmd_error("Nothing there to show.\n");
+
+ if (format != "dot" && !format.empty()) {
+ std::string cmd = stringf("dot -T%s '%s' > '%s.new' && mv '%s.new' '%s'", format.c_str(), dot_file.c_str(), out_file.c_str(), out_file.c_str(), out_file.c_str());
+ log("Exec: %s\n", cmd.c_str());
+ if (run_command(cmd) != 0)
+ log_cmd_error("Shell command failed!\n");
+ }
+
+ if (!viewer_exe.empty()) {
+ std::string cmd = stringf("%s '%s' &", viewer_exe.c_str(), out_file.c_str());
+ log("Exec: %s\n", cmd.c_str());
+ if (run_command(cmd) != 0)
+ log_cmd_error("Shell command failed!\n");
+ } else
+ if (format.empty()) {
+ std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid'; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' &", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str());
+ log("Exec: %s\n", cmd.c_str());
+ if (run_command(cmd) != 0)
+ log_cmd_error("Shell command failed!\n");
+ }
+
+ if (flag_pause) {
+ #ifdef YOSYS_ENABLE_READLINE
+ char *input = NULL;
+ while ((input = readline("Press ENTER to continue (or type 'shell' to open a shell)> ")) != NULL) {
+ if (input[strspn(input, " \t\r\n")] == 0)
+ break;
+ char *p = input + strspn(input, " \t\r\n");
+ if (!strcmp(p, "shell")) {
+ Pass::call(design, "shell");
+ break;
+ }
+ }
+ #else
+ log_cmd_error("This version of yosys is built without readline support => 'show -pause' is not available.\n");
+ #endif
+ }
+
+ log_pop();
+ }
+} ShowPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/splice.cc b/passes/cmds/splice.cc
new file mode 100644
index 00000000..7418ec4d
--- /dev/null
+++ b/passes/cmds/splice.cc
@@ -0,0 +1,369 @@
+/*
+ * 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/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/sigtools.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+#include <tuple>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct SpliceWorker
+{
+ RTLIL::Design *design;
+ RTLIL::Module *module;
+
+ bool sel_by_cell;
+ bool sel_by_wire;
+ bool sel_any_bit;
+ bool no_outputs;
+ bool do_wires;
+
+ std::set<RTLIL::IdString> ports;
+ std::set<RTLIL::IdString> no_ports;
+
+ CellTypes ct;
+ SigMap sigmap;
+
+ std::vector<RTLIL::SigBit> driven_bits;
+ std::map<RTLIL::SigBit, int> driven_bits_map;
+
+ std::set<RTLIL::SigSpec> driven_chunks;
+ std::map<RTLIL::SigSpec, RTLIL::SigSpec> spliced_signals_cache;
+ std::map<RTLIL::SigSpec, RTLIL::SigSpec> sliced_signals_cache;
+
+ SpliceWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module), ct(design), sigmap(module)
+ {
+ }
+
+ RTLIL::SigSpec get_sliced_signal(RTLIL::SigSpec sig)
+ {
+ if (sig.size() == 0 || sig.is_fully_const())
+ return sig;
+
+ if (sliced_signals_cache.count(sig))
+ return sliced_signals_cache.at(sig);
+
+ int offset = 0;
+ int p = driven_bits_map.at(sig.extract(0, 1).as_bit()) - 1;
+ while (driven_bits.at(p) != RTLIL::State::Sm)
+ p--, offset++;
+
+ RTLIL::SigSpec sig_a;
+ for (p++; driven_bits.at(p) != RTLIL::State::Sm; p++)
+ sig_a.append(driven_bits.at(p));
+
+ RTLIL::SigSpec new_sig = sig;
+
+ if (sig_a.size() != sig.size()) {
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$slice");
+ cell->parameters["\\OFFSET"] = offset;
+ cell->parameters["\\A_WIDTH"] = sig_a.size();
+ cell->parameters["\\Y_WIDTH"] = sig.size();
+ cell->setPort("\\A", sig_a);
+ cell->setPort("\\Y", module->addWire(NEW_ID, sig.size()));
+ new_sig = cell->getPort("\\Y");
+ }
+
+ sliced_signals_cache[sig] = new_sig;
+
+ return new_sig;
+ }
+
+ RTLIL::SigSpec get_spliced_signal(RTLIL::SigSpec sig)
+ {
+ if (sig.size() == 0 || sig.is_fully_const())
+ return sig;
+
+ if (spliced_signals_cache.count(sig))
+ return spliced_signals_cache.at(sig);
+
+ int last_bit = -1;
+ std::vector<RTLIL::SigSpec> chunks;
+
+ for (auto &bit : sig.to_sigbit_vector())
+ {
+ if (bit.wire == NULL)
+ {
+ if (last_bit == 0)
+ chunks.back().append(bit);
+ else
+ chunks.push_back(bit);
+ last_bit = 0;
+ continue;
+ }
+
+ if (driven_bits_map.count(bit))
+ {
+ int this_bit = driven_bits_map.at(bit);
+ if (last_bit+1 == this_bit)
+ chunks.back().append(bit);
+ else
+ chunks.push_back(bit);
+ last_bit = this_bit;
+ continue;
+ }
+
+ log(" Failed to generate spliced signal %s.\n", log_signal(sig));
+ spliced_signals_cache[sig] = sig;
+ return sig;
+ }
+
+
+ RTLIL::SigSpec new_sig = get_sliced_signal(chunks.front());
+ for (size_t i = 1; i < chunks.size(); i++) {
+ RTLIL::SigSpec sig2 = get_sliced_signal(chunks[i]);
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$concat");
+ cell->parameters["\\A_WIDTH"] = new_sig.size();
+ cell->parameters["\\B_WIDTH"] = sig2.size();
+ cell->setPort("\\A", new_sig);
+ cell->setPort("\\B", sig2);
+ cell->setPort("\\Y", module->addWire(NEW_ID, new_sig.size() + sig2.size()));
+ new_sig = cell->getPort("\\Y");
+ }
+
+ spliced_signals_cache[sig] = new_sig;
+
+ log(" Created spliced signal: %s -> %s\n", log_signal(sig), log_signal(new_sig));
+ return new_sig;
+ }
+
+ void run()
+ {
+ log("Splicing signals in module %s:\n", RTLIL::id2cstr(module->name));
+
+ driven_bits.push_back(RTLIL::State::Sm);
+ driven_bits.push_back(RTLIL::State::Sm);
+
+ for (auto &it : module->wires_)
+ if (it.second->port_input) {
+ RTLIL::SigSpec sig = sigmap(it.second);
+ driven_chunks.insert(sig);
+ for (auto &bit : sig.to_sigbit_vector())
+ driven_bits.push_back(bit);
+ driven_bits.push_back(RTLIL::State::Sm);
+ }
+
+ for (auto &it : module->cells_)
+ for (auto &conn : it.second->connections())
+ if (!ct.cell_known(it.second->type) || ct.cell_output(it.second->type, conn.first)) {
+ RTLIL::SigSpec sig = sigmap(conn.second);
+ driven_chunks.insert(sig);
+ for (auto &bit : sig.to_sigbit_vector())
+ driven_bits.push_back(bit);
+ driven_bits.push_back(RTLIL::State::Sm);
+ }
+
+ driven_bits.push_back(RTLIL::State::Sm);
+
+ for (size_t i = 0; i < driven_bits.size(); i++)
+ driven_bits_map[driven_bits[i]] = i;
+
+ SigPool selected_bits;
+ if (!sel_by_cell)
+ for (auto &it : module->wires_)
+ if (design->selected(module, it.second))
+ selected_bits.add(sigmap(it.second));
+
+ std::vector<Cell*> mod_cells = module->cells();
+
+ for (auto cell : mod_cells) {
+ if (!sel_by_wire && !design->selected(module, cell))
+ continue;
+ for (auto &conn : cell->connections_)
+ if (ct.cell_input(cell->type, conn.first)) {
+ if (ports.size() > 0 && !ports.count(conn.first))
+ continue;
+ if (no_ports.size() > 0 && no_ports.count(conn.first))
+ continue;
+ RTLIL::SigSpec sig = sigmap(conn.second);
+ if (!sel_by_cell) {
+ if (!sel_any_bit && !selected_bits.check_all(sig))
+ continue;
+ if (sel_any_bit && !selected_bits.check_any(sig))
+ continue;
+ }
+ if (driven_chunks.count(sig) > 0)
+ continue;
+ conn.second = get_spliced_signal(sig);
+ }
+ }
+
+ std::vector<std::pair<RTLIL::Wire*, RTLIL::SigSpec>> rework_wires;
+ std::vector<Wire*> mod_wires = module->wires();
+
+ for (auto wire : mod_wires)
+ if ((!no_outputs && wire->port_output) || (do_wires && wire->name[0] == '\\')) {
+ if (!design->selected(module, wire))
+ continue;
+ RTLIL::SigSpec sig = sigmap(wire);
+ if (driven_chunks.count(sig) > 0)
+ continue;
+ RTLIL::SigSpec new_sig = get_spliced_signal(sig);
+ if (new_sig != sig)
+ rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(wire, new_sig));
+ } else
+ if (!wire->port_input) {
+ RTLIL::SigSpec sig = sigmap(wire);
+ if (spliced_signals_cache.count(sig) && spliced_signals_cache.at(sig) != sig)
+ rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(wire, spliced_signals_cache.at(sig)));
+ else if (sliced_signals_cache.count(sig) && sliced_signals_cache.at(sig) != sig)
+ rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(wire, sliced_signals_cache.at(sig)));
+ }
+
+ for (auto &it : rework_wires)
+ {
+ RTLIL::IdString orig_name = it.first->name;
+ module->rename(it.first, NEW_ID);
+
+ RTLIL::Wire *new_port = module->addWire(orig_name, it.first);
+ it.first->port_id = 0;
+ it.first->port_input = false;
+ it.first->port_output = false;
+
+ module->connect(RTLIL::SigSig(new_port, it.second));
+ }
+ }
+};
+
+struct SplicePass : public Pass {
+ SplicePass() : Pass("splice", "create explicit splicing cells") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" splice [options] [selection]\n");
+ log("\n");
+ log("This command adds $slice and $concat cells to the design to make the splicing\n");
+ log("of multi-bit signals explicit. This for example is useful for coarse grain\n");
+ log("synthesis, where dedicated hardware is needed to splice signals.\n");
+ log("\n");
+ log(" -sel_by_cell\n");
+ log(" only select the cell ports to rewire by the cell. if the selection\n");
+ log(" contains a cell, than all cell inputs are rewired, if necessary.\n");
+ log("\n");
+ log(" -sel_by_wire\n");
+ log(" only select the cell ports to rewire by the wire. if the selection\n");
+ log(" contains a wire, than all cell ports driven by this wire are wired,\n");
+ log(" if necessary.\n");
+ log("\n");
+ log(" -sel_any_bit\n");
+ log(" it is sufficient if the driver of any bit of a cell port is selected.\n");
+ log(" by default all bits must be selected.\n");
+ log("\n");
+ log(" -wires\n");
+ log(" also add $slice and $concat cells to drive otherwise unused wires.\n");
+ log("\n");
+ log(" -no_outputs\n");
+ log(" do not rewire selected module outputs.\n");
+ log("\n");
+ log(" -port <name>\n");
+ log(" only rewire cell ports with the specified name. can be used multiple\n");
+ log(" times. implies -no_output.\n");
+ log("\n");
+ log(" -no_port <name>\n");
+ log(" do not rewire cell ports with the specified name. can be used multiple\n");
+ log(" times. can not be combined with -port <name>.\n");
+ log("\n");
+ log("By default selected output wires and all cell ports of selected cells driven\n");
+ log("by selected wires are rewired.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool sel_by_cell = false;
+ bool sel_by_wire = false;
+ bool sel_any_bit = false;
+ bool no_outputs = false;
+ bool do_wires = false;
+ std::set<RTLIL::IdString> ports, no_ports;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-sel_by_cell") {
+ sel_by_cell = true;
+ continue;
+ }
+ if (args[argidx] == "-sel_by_wire") {
+ sel_by_wire = true;
+ continue;
+ }
+ if (args[argidx] == "-sel_any_bit") {
+ sel_any_bit = true;
+ continue;
+ }
+ if (args[argidx] == "-wires") {
+ do_wires = true;
+ continue;
+ }
+ if (args[argidx] == "-no_outputs") {
+ no_outputs = true;
+ continue;
+ }
+ if (args[argidx] == "-port" && argidx+1 < args.size()) {
+ ports.insert(RTLIL::escape_id(args[++argidx]));
+ no_outputs = true;
+ continue;
+ }
+ if (args[argidx] == "-no_port" && argidx+1 < args.size()) {
+ no_ports.insert(RTLIL::escape_id(args[++argidx]));
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (sel_by_cell && sel_by_wire)
+ log_cmd_error("The options -sel_by_cell and -sel_by_wire are exclusive!\n");
+
+ if (sel_by_cell && sel_any_bit)
+ log_cmd_error("The options -sel_by_cell and -sel_any_bit are exclusive!\n");
+
+ if (!ports.empty() && !no_ports.empty())
+ log_cmd_error("The options -port and -no_port are exclusive!\n");
+
+ log_header(design, "Executing SPLICE pass (creating cells for signal splicing).\n");
+
+ for (auto &mod_it : design->modules_)
+ {
+ if (!design->selected(mod_it.second))
+ continue;
+
+ if (mod_it.second->processes.size()) {
+ log("Skipping module %s as it contains processes.\n", mod_it.second->name.c_str());
+ continue;
+ }
+
+ SpliceWorker worker(design, mod_it.second);
+ worker.sel_by_cell = sel_by_cell;
+ worker.sel_by_wire = sel_by_wire;
+ worker.sel_any_bit = sel_any_bit;
+ worker.no_outputs = no_outputs;
+ worker.do_wires = do_wires;
+ worker.ports = ports;
+ worker.no_ports = no_ports;
+ worker.run();
+ }
+ }
+} SplicePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/splitnets.cc b/passes/cmds/splitnets.cc
new file mode 100644
index 00000000..14eeb066
--- /dev/null
+++ b/passes/cmds/splitnets.cc
@@ -0,0 +1,270 @@
+/*
+ * 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/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct SplitnetsWorker
+{
+ std::map<RTLIL::Wire*, std::vector<RTLIL::SigBit>> splitmap;
+
+ void append_wire(RTLIL::Module *module, RTLIL::Wire *wire, int offset, int width, std::string format)
+ {
+ std::string new_wire_name = wire->name.str();
+
+ if (format.size() > 0)
+ new_wire_name += format.substr(0, 1);
+
+ if (width > 1) {
+ new_wire_name += stringf("%d", offset+width-1);
+ if (format.size() > 2)
+ new_wire_name += format.substr(2, 1);
+ else
+ new_wire_name += ":";
+ }
+
+ new_wire_name += stringf("%d", offset);
+
+ if (format.size() > 1)
+ new_wire_name += format.substr(1, 1);
+
+ RTLIL::Wire *new_wire = module->addWire(module->uniquify(new_wire_name), width);
+ new_wire->port_id = wire->port_id ? wire->port_id + offset : 0;
+ new_wire->port_input = wire->port_input;
+ new_wire->port_output = wire->port_output;
+
+ if (wire->attributes.count("\\src"))
+ new_wire->attributes["\\src"] = wire->attributes.at("\\src");
+
+ if (wire->attributes.count("\\keep"))
+ new_wire->attributes["\\keep"] = wire->attributes.at("\\keep");
+
+ if (wire->attributes.count("\\init")) {
+ Const old_init = wire->attributes.at("\\init"), new_init;
+ for (int i = offset; i < offset+width; i++)
+ new_init.bits.push_back(i < GetSize(old_init) ? old_init.bits.at(i) : State::Sx);
+ new_wire->attributes["\\init"] = new_init;
+ }
+
+ std::vector<RTLIL::SigBit> sigvec = RTLIL::SigSpec(new_wire).to_sigbit_vector();
+ splitmap[wire].insert(splitmap[wire].end(), sigvec.begin(), sigvec.end());
+ }
+
+ void operator()(RTLIL::SigSpec &sig)
+ {
+ for (auto &bit : sig)
+ if (splitmap.count(bit.wire) > 0)
+ bit = splitmap.at(bit.wire).at(bit.offset);
+ }
+};
+
+struct SplitnetsPass : public Pass {
+ SplitnetsPass() : Pass("splitnets", "split up multi-bit nets") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" splitnets [options] [selection]\n");
+ log("\n");
+ log("This command splits multi-bit nets into single-bit nets.\n");
+ log("\n");
+ log(" -format char1[char2[char3]]\n");
+ log(" the first char is inserted between the net name and the bit index, the\n");
+ log(" second char is appended to the netname. e.g. -format () creates net\n");
+ log(" names like 'mysignal(42)'. the 3rd character is the range separation\n");
+ log(" character when creating multi-bit wires. the default is '[]:'.\n");
+ log("\n");
+ log(" -ports\n");
+ log(" also split module ports. per default only internal signals are split.\n");
+ log("\n");
+ log(" -driver\n");
+ log(" don't blindly split nets in individual bits. instead look at the driver\n");
+ log(" and split nets so that no driver drives only part of a net.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool flag_ports = false;
+ bool flag_driver = false;
+ std::string format = "[]:";
+
+ log_header(design, "Executing SPLITNETS pass (splitting up multi-bit signals).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-format" && argidx+1 < args.size()) {
+ format = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-ports") {
+ flag_ports = true;
+ continue;
+ }
+ if (args[argidx] == "-driver") {
+ flag_driver = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ // module_ports_db[module_name][old_port_name] = new_port_name_list
+ dict<IdString, dict<IdString, vector<IdString>>> module_ports_db;
+
+ for (auto module : design->selected_modules())
+ {
+ SplitnetsWorker worker;
+
+ if (flag_ports)
+ {
+ int normalized_port_factor = 0;
+
+ for (auto wire : module->wires())
+ if (wire->port_id != 0) {
+ normalized_port_factor = max(normalized_port_factor, wire->port_id+1);
+ normalized_port_factor = max(normalized_port_factor, GetSize(wire)+1);
+ }
+
+ for (auto wire : module->wires())
+ wire->port_id *= normalized_port_factor;
+ }
+
+ if (flag_driver)
+ {
+ CellTypes ct(design);
+
+ std::map<RTLIL::Wire*, std::set<int>> split_wires_at;
+
+ for (auto &c : module->cells_)
+ for (auto &p : c.second->connections())
+ {
+ if (!ct.cell_known(c.second->type))
+ continue;
+ if (!ct.cell_output(c.second->type, p.first))
+ continue;
+
+ RTLIL::SigSpec sig = p.second;
+ for (auto &chunk : sig.chunks()) {
+ if (chunk.wire == NULL)
+ continue;
+ if (chunk.wire->port_id == 0 || flag_ports) {
+ if (chunk.offset != 0)
+ split_wires_at[chunk.wire].insert(chunk.offset);
+ if (chunk.offset + chunk.width < chunk.wire->width)
+ split_wires_at[chunk.wire].insert(chunk.offset + chunk.width);
+ }
+ }
+ }
+
+ for (auto &it : split_wires_at) {
+ int cursor = 0;
+ for (int next_cursor : it.second) {
+ worker.append_wire(module, it.first, cursor, next_cursor - cursor, format);
+ cursor = next_cursor;
+ }
+ worker.append_wire(module, it.first, cursor, it.first->width - cursor, format);
+ }
+ }
+ else
+ {
+ for (auto &w : module->wires_) {
+ RTLIL::Wire *wire = w.second;
+ if (wire->width > 1 && (wire->port_id == 0 || flag_ports) && design->selected(module, w.second))
+ worker.splitmap[wire] = std::vector<RTLIL::SigBit>();
+ }
+
+ for (auto &it : worker.splitmap)
+ for (int i = 0; i < it.first->width; i++)
+ worker.append_wire(module, it.first, i, 1, format);
+ }
+
+ module->rewrite_sigspecs(worker);
+
+ if (flag_ports)
+ {
+ for (auto wire : module->wires())
+ {
+ if (wire->port_id == 0)
+ continue;
+
+ SigSpec sig(wire);
+ worker(sig);
+
+ if (sig == wire)
+ continue;
+
+ vector<IdString> &new_ports = module_ports_db[module->name][wire->name];
+
+ for (SigSpec c : sig.chunks())
+ new_ports.push_back(c.as_wire()->name);
+ }
+ }
+
+ pool<RTLIL::Wire*> delete_wires;
+ for (auto &it : worker.splitmap)
+ delete_wires.insert(it.first);
+ module->remove(delete_wires);
+
+ if (flag_ports)
+ module->fixup_ports();
+ }
+
+ if (!module_ports_db.empty())
+ {
+ for (auto module : design->modules())
+ for (auto cell : module->cells())
+ {
+ if (module_ports_db.count(cell->type) == 0)
+ continue;
+
+ for (auto &it : module_ports_db.at(cell->type))
+ {
+ IdString port_id = it.first;
+ const auto &new_port_ids = it.second;
+
+ if (!cell->hasPort(port_id))
+ continue;
+
+ int offset = 0;
+ SigSpec sig = cell->getPort(port_id);
+
+ for (auto nid : new_port_ids)
+ {
+ int nlen = GetSize(design->module(cell->type)->wire(nid));
+ if (offset + nlen > GetSize(sig))
+ nlen = GetSize(sig) - offset;
+ if (nlen > 0)
+ cell->setPort(nid, sig.extract(offset, nlen));
+ offset += nlen;
+ }
+
+ cell->unsetPort(port_id);
+ }
+ }
+ }
+ }
+} SplitnetsPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc
new file mode 100644
index 00000000..362a0edf
--- /dev/null
+++ b/passes/cmds/stat.cc
@@ -0,0 +1,297 @@
+/*
+ * 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/register.h"
+#include "kernel/celltypes.h"
+#include "passes/techmap/libparse.h"
+
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct statdata_t
+{
+ #define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \
+ X(num_memories) X(num_memory_bits) X(num_cells) X(num_processes)
+
+ #define STAT_NUMERIC_MEMBERS STAT_INT_MEMBERS X(area)
+
+ #define X(_name) int _name;
+ STAT_INT_MEMBERS
+ #undef X
+ double area;
+
+ std::map<RTLIL::IdString, int, RTLIL::sort_by_id_str> num_cells_by_type;
+ std::set<RTLIL::IdString> unknown_cell_area;
+
+ statdata_t operator+(const statdata_t &other) const
+ {
+ statdata_t sum = other;
+ #define X(_name) sum._name += _name;
+ STAT_NUMERIC_MEMBERS
+ #undef X
+ for (auto &it : num_cells_by_type)
+ sum.num_cells_by_type[it.first] += it.second;
+ return sum;
+ }
+
+ statdata_t operator*(int other) const
+ {
+ statdata_t sum = *this;
+ #define X(_name) sum._name *= other;
+ STAT_NUMERIC_MEMBERS
+ #undef X
+ for (auto &it : sum.num_cells_by_type)
+ it.second *= other;
+ return sum;
+ }
+
+ statdata_t()
+ {
+ #define X(_name) _name = 0;
+ STAT_NUMERIC_MEMBERS
+ #undef X
+ }
+
+ statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict<IdString, double> &cell_area)
+ {
+ #define X(_name) _name = 0;
+ STAT_NUMERIC_MEMBERS
+ #undef X
+
+ for (auto &it : mod->wires_)
+ {
+ if (!design->selected(mod, it.second))
+ continue;
+
+ if (it.first[0] == '\\') {
+ num_pub_wires++;
+ num_pub_wire_bits += it.second->width;
+ }
+
+ num_wires++;
+ num_wire_bits += it.second->width;
+ }
+
+ for (auto &it : mod->memories) {
+ if (!design->selected(mod, it.second))
+ continue;
+ num_memories++;
+ num_memory_bits += it.second->width * it.second->size;
+ }
+
+ for (auto &it : mod->cells_)
+ {
+ if (!design->selected(mod, it.second))
+ continue;
+
+ RTLIL::IdString cell_type = it.second->type;
+
+ if (width_mode)
+ {
+ if (cell_type.in("$not", "$pos", "$neg",
+ "$logic_not", "$logic_and", "$logic_or",
+ "$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool",
+ "$lut", "$and", "$or", "$xor", "$xnor",
+ "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
+ "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
+ "$add", "$sub", "$mul", "$div", "$mod", "$pow")) {
+ int width_a = it.second->hasPort("\\A") ? GetSize(it.second->getPort("\\A")) : 0;
+ int width_b = it.second->hasPort("\\B") ? GetSize(it.second->getPort("\\B")) : 0;
+ int width_y = it.second->hasPort("\\Y") ? GetSize(it.second->getPort("\\Y")) : 0;
+ cell_type = stringf("%s_%d", cell_type.c_str(), max<int>({width_a, width_b, width_y}));
+ }
+ else if (cell_type.in("$mux", "$pmux"))
+ cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(it.second->getPort("\\Y")));
+ else if (cell_type.in("$sr", "$dff", "$dffsr", "$adff", "$dlatch", "$dlatchsr"))
+ cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(it.second->getPort("\\Q")));
+ }
+
+ if (!cell_area.empty()) {
+ if (cell_area.count(cell_type))
+ area += cell_area.at(cell_type);
+ else
+ unknown_cell_area.insert(cell_type);
+ }
+
+ num_cells++;
+ num_cells_by_type[cell_type]++;
+ }
+
+ for (auto &it : mod->processes) {
+ if (!design->selected(mod, it.second))
+ continue;
+ num_processes++;
+ }
+ }
+
+ void log_data()
+ {
+ log(" Number of wires: %6d\n", num_wires);
+ log(" Number of wire bits: %6d\n", num_wire_bits);
+ log(" Number of public wires: %6d\n", num_pub_wires);
+ log(" Number of public wire bits: %6d\n", num_pub_wire_bits);
+ log(" Number of memories: %6d\n", num_memories);
+ log(" Number of memory bits: %6d\n", num_memory_bits);
+ log(" Number of processes: %6d\n", num_processes);
+ log(" Number of cells: %6d\n", num_cells);
+ for (auto &it : num_cells_by_type)
+ log(" %-26s %6d\n", RTLIL::id2cstr(it.first), it.second);
+
+ if (!unknown_cell_area.empty()) {
+ log("\n");
+ for (auto cell_type : unknown_cell_area)
+ log(" Area for cell type %s is unknown!\n", cell_type.c_str());
+ }
+
+ if (area != 0) {
+ log("\n");
+ log(" Chip area for this module: %f\n", area);
+ }
+ }
+};
+
+statdata_t hierarchy_worker(std::map<RTLIL::IdString, statdata_t> &mod_stat, RTLIL::IdString mod, int level)
+{
+ statdata_t mod_data = mod_stat.at(mod);
+ std::map<RTLIL::IdString, int, RTLIL::sort_by_id_str> num_cells_by_type;
+ num_cells_by_type.swap(mod_data.num_cells_by_type);
+
+ for (auto &it : num_cells_by_type)
+ if (mod_stat.count(it.first) > 0) {
+ log(" %*s%-*s %6d\n", 2*level, "", 26-2*level, RTLIL::id2cstr(it.first), it.second);
+ mod_data = mod_data + hierarchy_worker(mod_stat, it.first, level+1) * it.second;
+ mod_data.num_cells -= it.second;
+ } else {
+ mod_data.num_cells_by_type[it.first] += it.second;
+ }
+
+ return mod_data;
+}
+
+void read_liberty_cellarea(dict<IdString, double> &cell_area, string liberty_file)
+{
+ std::ifstream f;
+ f.open(liberty_file.c_str());
+ if (f.fail())
+ log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno));
+ LibertyParser libparser(f);
+ f.close();
+
+ for (auto cell : libparser.ast->children)
+ {
+ if (cell->id != "cell" || cell->args.size() != 1)
+ continue;
+
+ LibertyAst *ar = cell->find("area");
+ if (ar != NULL && !ar->value.empty())
+ cell_area["\\" + cell->args[0]] = atof(ar->value.c_str());
+ }
+}
+
+struct StatPass : public Pass {
+ StatPass() : Pass("stat", "print some statistics") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" stat [options] [selection]\n");
+ log("\n");
+ log("Print some statistics (number of objects) on the selected portion of the\n");
+ log("design.\n");
+ log("\n");
+ log(" -top <module>\n");
+ log(" print design hierarchy with this module as top. if the design is fully\n");
+ log(" selected and a module has the 'top' attribute set, this module is used\n");
+ log(" default value for this option.\n");
+ log("\n");
+ log(" -liberty <liberty_file>\n");
+ log(" use cell area information from the provided liberty file\n");
+ log("\n");
+ log(" -width\n");
+ log(" annotate internal cell types with their word width.\n");
+ log(" e.g. $add_8 for an 8 bit wide $add cell.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Printing statistics.\n");
+
+ bool width_mode = false;
+ RTLIL::Module *top_mod = NULL;
+ std::map<RTLIL::IdString, statdata_t> mod_stat;
+ dict<IdString, double> cell_area;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-width") {
+ width_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-liberty" && argidx+1 < args.size()) {
+ string liberty_file = args[++argidx];
+ rewrite_filename(liberty_file);
+ read_liberty_cellarea(cell_area, liberty_file);
+ continue;
+ }
+ if (args[argidx] == "-top" && argidx+1 < args.size()) {
+ if (design->modules_.count(RTLIL::escape_id(args[argidx+1])) == 0)
+ log_cmd_error("Can't find module %s.\n", args[argidx+1].c_str());
+ top_mod = design->modules_.at(RTLIL::escape_id(args[++argidx]));
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto mod : design->selected_modules())
+ {
+ if (!top_mod && design->full_selection())
+ if (mod->get_bool_attribute("\\top"))
+ top_mod = mod;
+
+ statdata_t data(design, mod, width_mode, cell_area);
+ mod_stat[mod->name] = data;
+
+ log("\n");
+ log("=== %s%s ===\n", RTLIL::id2cstr(mod->name), design->selected_whole_module(mod->name) ? "" : " (partially selected)");
+ log("\n");
+ data.log_data();
+ }
+
+ if (top_mod != NULL && GetSize(mod_stat) > 1)
+ {
+ log("\n");
+ log("=== design hierarchy ===\n");
+ log("\n");
+
+ log(" %-28s %6d\n", RTLIL::id2cstr(top_mod->name), 1);
+ statdata_t data = hierarchy_worker(mod_stat, top_mod->name, 0);
+
+ log("\n");
+ data.log_data();
+ }
+
+ log("\n");
+ }
+} StatPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/tee.cc b/passes/cmds/tee.cc
new file mode 100644
index 00000000..3db2dbf0
--- /dev/null
+++ b/passes/cmds/tee.cc
@@ -0,0 +1,102 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2014 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.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/register.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct TeePass : public Pass {
+ TeePass() : Pass("tee", "redirect command output to file") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" tee [-q] [-o logfile|-a logfile] cmd\n");
+ log("\n");
+ log("Execute the specified command, optionally writing the commands output to the\n");
+ 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("\n");
+ log(" -o logfile\n");
+ log(" Write output to this file, truncate if exists.\n");
+ log("\n");
+ log(" -a logfile\n");
+ 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("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::vector<FILE*> backup_log_files, files_to_close;
+ int backup_log_verbose_level = log_verbose_level;
+ backup_log_files = log_files;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-q" && files_to_close.empty()) {
+ log_files.clear();
+ continue;
+ }
+ if ((args[argidx] == "-o" || args[argidx] == "-a") && argidx+1 < args.size()) {
+ const char *open_mode = args[argidx] == "-o" ? "w" : "a+";
+ FILE *f = fopen(args[++argidx].c_str(), open_mode);
+ if (f == NULL) {
+ for (auto cf : files_to_close)
+ fclose(cf);
+ log_cmd_error("Can't create file %s.\n", args[argidx].c_str());
+ }
+ log_files.push_back(f);
+ files_to_close.push_back(f);
+ continue;
+ }
+ if (GetSize(args[argidx]) >= 2 && (args[argidx][0] == '-' || args[argidx][0] == '+') && args[argidx][1] >= '0' && args[argidx][1] <= '9') {
+ log_verbose_level += atoi(args[argidx].c_str());
+ continue;
+ }
+ break;
+ }
+
+ try {
+ std::vector<std::string> new_args(args.begin() + argidx, args.end());
+ Pass::call(design, new_args);
+ } catch (...) {
+ for (auto cf : files_to_close)
+ fclose(cf);
+ log_files = backup_log_files;
+ throw;
+ }
+
+ for (auto cf : files_to_close)
+ fclose(cf);
+
+ log_verbose_level = backup_log_verbose_level;
+ log_files = backup_log_files;
+ }
+} TeePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/torder.cc b/passes/cmds/torder.cc
new file mode 100644
index 00000000..56223610
--- /dev/null
+++ b/passes/cmds/torder.cc
@@ -0,0 +1,123 @@
+/*
+ * 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/celltypes.h"
+#include "kernel/sigtools.h"
+#include "kernel/utils.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct TorderPass : public Pass {
+ TorderPass() : Pass("torder", "print cells in topological order") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" torder [options] [selection]\n");
+ log("\n");
+ log("This command prints the selected cells in topological order.\n");
+ log("\n");
+ log(" -stop <cell_type> <cell_port>\n");
+ log(" do not use the specified cell port in topological sorting\n");
+ log("\n");
+ log(" -noautostop\n");
+ log(" by default Q outputs of internal FF cells and memory read port outputs\n");
+ log(" are not used in topological sorting. this option deactivates that.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool noautostop = false;
+ dict<IdString, pool<IdString>> stop_db;
+
+ log_header(design, "Executing TORDER pass (print cells in topological order).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-stop" && argidx+2 < args.size()) {
+ IdString cell_type = RTLIL::escape_id(args[++argidx]);
+ IdString cell_port = RTLIL::escape_id(args[++argidx]);
+ stop_db[cell_type].insert(cell_port);
+ continue;
+ }
+ if (args[argidx] == "-noautostop") {
+ noautostop = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ log("module %s\n", log_id(module));
+
+ SigMap sigmap(module);
+ dict<SigBit, pool<IdString>> bit_drivers, bit_users;
+ TopoSort<IdString, RTLIL::sort_by_id_str> toposort;
+
+ for (auto cell : module->selected_cells())
+ for (auto conn : cell->connections())
+ {
+ if (stop_db.count(cell->type) && stop_db.at(cell->type).count(conn.first))
+ continue;
+
+ if (!noautostop && yosys_celltypes.cell_known(cell->type)) {
+ if (conn.first.in("\\Q", "\\CTRL_OUT", "\\RD_DATA"))
+ continue;
+ if (cell->type == "$memrd" && conn.first == "\\DATA")
+ continue;
+ }
+
+ if (cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ bit_users[bit].insert(cell->name);
+
+ if (cell->output(conn.first))
+ for (auto bit : sigmap(conn.second))
+ bit_drivers[bit].insert(cell->name);
+
+ toposort.node(cell->name);
+ }
+
+ for (auto &it : bit_users)
+ if (bit_drivers.count(it.first))
+ for (auto driver_cell : bit_drivers.at(it.first))
+ for (auto user_cell : it.second)
+ toposort.edge(driver_cell, user_cell);
+
+ toposort.analyze_loops = true;
+ toposort.sort();
+
+ for (auto &it : toposort.loops) {
+ log(" loop");
+ for (auto cell : it)
+ log(" %s", log_id(cell));
+ log("\n");
+ }
+
+ for (auto cell : toposort.sorted)
+ log(" cell %s\n", log_id(cell));
+ }
+ }
+} TorderPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/trace.cc b/passes/cmds/trace.cc
new file mode 100644
index 00000000..1a5f873f
--- /dev/null
+++ b/passes/cmds/trace.cc
@@ -0,0 +1,98 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2014 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.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"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct TraceMonitor : public RTLIL::Monitor
+{
+ virtual void notify_module_add(RTLIL::Module *module) YS_OVERRIDE
+ {
+ log("#TRACE# Module add: %s\n", log_id(module));
+ }
+
+ virtual void notify_module_del(RTLIL::Module *module) YS_OVERRIDE
+ {
+ log("#TRACE# Module delete: %s\n", log_id(module));
+ }
+
+ virtual void notify_connect(RTLIL::Cell *cell, const RTLIL::IdString &port, const RTLIL::SigSpec &old_sig, RTLIL::SigSpec &sig) YS_OVERRIDE
+ {
+ log("#TRACE# Cell connect: %s.%s.%s = %s (was: %s)\n", log_id(cell->module), log_id(cell), log_id(port), log_signal(sig), log_signal(old_sig));
+ }
+
+ virtual void notify_connect(RTLIL::Module *module, const RTLIL::SigSig &sigsig) YS_OVERRIDE
+ {
+ log("#TRACE# Connection in module %s: %s = %s\n", log_id(module), log_signal(sigsig.first), log_signal(sigsig.second));
+ }
+
+ virtual void notify_connect(RTLIL::Module *module, const std::vector<RTLIL::SigSig> &sigsig_vec) YS_OVERRIDE
+ {
+ log("#TRACE# New connections in module %s:\n", log_id(module));
+ for (auto &sigsig : sigsig_vec)
+ log("## %s = %s\n", log_signal(sigsig.first), log_signal(sigsig.second));
+ }
+
+ virtual void notify_blackout(RTLIL::Module *module) YS_OVERRIDE
+ {
+ log("#TRACE# Blackout in module %s:\n", log_id(module));
+ }
+};
+
+struct TracePass : public Pass {
+ TracePass() : Pass("trace", "redirect command output to file") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" trace cmd\n");
+ log("\n");
+ log("Execute the specified command, logging all changes the command performs on\n");
+ log("the design in real time.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // .. parse options ..
+ break;
+ }
+
+ TraceMonitor monitor;
+ design->monitors.insert(&monitor);
+
+ try {
+ std::vector<std::string> new_args(args.begin() + argidx, args.end());
+ Pass::call(design, new_args);
+ } catch (...) {
+ design->monitors.erase(&monitor);
+ throw;
+ }
+
+ design->monitors.erase(&monitor);
+ }
+} TracePass;
+
+PRIVATE_NAMESPACE_END
+
diff --git a/passes/cmds/write_file.cc b/passes/cmds/write_file.cc
new file mode 100644
index 00000000..b7826593
--- /dev/null
+++ b/passes/cmds/write_file.cc
@@ -0,0 +1,80 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2014 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.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"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct WriteFileFrontend : public Frontend {
+ WriteFileFrontend() : Frontend("=write_file", "write a text to a file") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" write_file [options] output_file [input_file]\n");
+ log("\n");
+ log("Write the text from the input file to the output file.\n");
+ log("\n");
+ log(" -a\n");
+ log(" Append to output file (instead of overwriting)\n");
+ log("\n");
+ log("\n");
+ log("Inside a script the input file can also can a here-document:\n");
+ log("\n");
+ log(" write_file hello.txt <<EOT\n");
+ log(" Hello World!\n");
+ log(" EOT\n");
+ log("\n");
+ }
+ virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design*)
+ {
+ bool append_mode = false;
+ std::string output_filename;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-a") {
+ append_mode = true;
+ continue;
+ }
+ break;
+ }
+
+ if (argidx < args.size() && args[argidx].rfind("-", 0) != 0)
+ output_filename = args[argidx++];
+ else
+ log_cmd_error("Missing putput filename.\n");
+
+ extra_args(f, filename, args, argidx);
+
+ FILE *of = fopen(output_filename.c_str(), append_mode ? "a" : "w");
+ char buffer[64 * 1024];
+ int bytes;
+
+ while (0 < (bytes = readsome(*f, buffer, sizeof(buffer))))
+ fwrite(buffer, bytes, 1, of);
+
+ fclose(of);
+ }
+} WriteFileFrontend;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/equiv/Makefile.inc b/passes/equiv/Makefile.inc
new file mode 100644
index 00000000..dd7b3be0
--- /dev/null
+++ b/passes/equiv/Makefile.inc
@@ -0,0 +1,12 @@
+
+OBJS += passes/equiv/equiv_make.o
+OBJS += passes/equiv/equiv_miter.o
+OBJS += passes/equiv/equiv_simple.o
+OBJS += passes/equiv/equiv_status.o
+OBJS += passes/equiv/equiv_add.o
+OBJS += passes/equiv/equiv_remove.o
+OBJS += passes/equiv/equiv_induct.o
+OBJS += passes/equiv/equiv_struct.o
+OBJS += passes/equiv/equiv_purge.o
+OBJS += passes/equiv/equiv_mark.o
+
diff --git a/passes/equiv/equiv_add.cc b/passes/equiv/equiv_add.cc
new file mode 100644
index 00000000..0494a724
--- /dev/null
+++ b/passes/equiv/equiv_add.cc
@@ -0,0 +1,177 @@
+/*
+ * 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 EquivAddPass : public Pass {
+ EquivAddPass() : Pass("equiv_add", "add a $equiv cell") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" equiv_add [-try] gold_sig gate_sig\n");
+ log("\n");
+ log("This command adds an $equiv cell for the specified signals.\n");
+ log("\n");
+ log("\n");
+ log(" equiv_add [-try] -cell gold_cell gate_cell\n");
+ log("\n");
+ log("This command adds $equiv cells for the ports of the specified cells.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, Design *design)
+ {
+ bool try_mode = false;
+
+ if (design->selected_active_module.empty())
+ log_cmd_error("This command must be executed in module context!\n");
+
+ Module *module = design->module(design->selected_active_module);
+ log_assert(module != nullptr);
+
+ if (GetSize(args) > 1 && args[1] == "-try") {
+ args.erase(args.begin() + 1);
+ try_mode = true;
+ }
+
+ if (GetSize(args) == 4 && args[1] == "-cell")
+ {
+ Cell *gold_cell = module->cell(RTLIL::escape_id(args[2]));
+ Cell *gate_cell = module->cell(RTLIL::escape_id(args[3]));
+
+ if (gold_cell == nullptr) {
+ if (try_mode) {
+ log_warning("Can't find gold cell '%s'.\n", args[2].c_str());
+ return;
+ }
+ log_cmd_error("Can't find gold cell '%s'.\n", args[2].c_str());
+ }
+
+ if (gate_cell == nullptr) {
+ if (try_mode) {
+ log_warning("Can't find gate cell '%s'.\n", args[3].c_str());
+ return;
+ }
+ log_cmd_error("Can't find gate cell '%s'.\n", args[3].c_str());
+ }
+
+ for (auto conn : gold_cell->connections())
+ {
+ auto port = conn.first;
+ SigSpec gold_sig = gold_cell->getPort(port);
+ SigSpec gate_sig = gate_cell->getPort(port);
+ int width = min(GetSize(gold_sig), GetSize(gate_sig));
+
+ if (gold_cell->input(port) && gate_cell->input(port))
+ {
+ SigSpec combined_sig = module->addWire(NEW_ID, width);
+
+ for (int i = 0; i < width; i++) {
+ module->addEquiv(NEW_ID, gold_sig[i], gate_sig[i], combined_sig[i]);
+ gold_sig[i] = gate_sig[i] = combined_sig[i];
+ }
+
+ gold_cell->setPort(port, gold_sig);
+ gate_cell->setPort(port, gate_sig);
+ continue;
+ }
+
+ if (gold_cell->output(port) && gate_cell->output(port))
+ {
+ SigSpec new_gold_wire = module->addWire(NEW_ID, width);
+ SigSpec new_gate_wire = module->addWire(NEW_ID, width);
+ SigSig gg_conn;
+
+ for (int i = 0; i < width; i++) {
+ module->addEquiv(NEW_ID, new_gold_wire[i], new_gold_wire[i], gold_sig[i]);
+ gg_conn.first.append(gate_sig[i]);
+ gg_conn.second.append(gold_sig[i]);
+ gold_sig[i] = new_gold_wire[i];
+ gate_sig[i] = new_gate_wire[i];
+ }
+
+ module->connect(gg_conn);
+ gold_cell->setPort(port, gold_sig);
+ gate_cell->setPort(port, gate_sig);
+ continue;
+ }
+ }
+ }
+ else
+ {
+ if (GetSize(args) != 3)
+ cmd_error(args, GetSize(args)-1, "Invalid number of arguments.");
+
+ SigSpec gold_signal, gate_signal;
+
+ if (!SigSpec::parse(gate_signal, module, args[2])) {
+ if (try_mode) {
+ log_warning("Error in gate signal: %s\n", args[2].c_str());
+ return;
+ }
+ log_cmd_error("Error in gate signal: %s\n", args[2].c_str());
+ }
+
+ if (!SigSpec::parse_rhs(gate_signal, gold_signal, module, args[1])) {
+ if (try_mode) {
+ log_warning("Error in gold signal: %s\n", args[1].c_str());
+ return;
+ }
+ log_cmd_error("Error in gold signal: %s\n", args[1].c_str());
+ }
+
+ log_assert(GetSize(gold_signal) == GetSize(gate_signal));
+ SigSpec equiv_signal = module->addWire(NEW_ID, GetSize(gold_signal));
+
+ SigMap sigmap(module);
+ sigmap.apply(gold_signal);
+ sigmap.apply(gate_signal);
+
+ dict<SigBit, SigBit> to_equiv_bits;
+ pool<Cell*> added_equiv_cells;
+
+ for (int i = 0; i < GetSize(gold_signal); i++) {
+ Cell *equiv_cell = module->addEquiv(NEW_ID, gold_signal[i], gate_signal[i], equiv_signal[i]);
+ equiv_cell->set_bool_attribute("\\keep");
+ to_equiv_bits[gold_signal[i]] = equiv_signal[i];
+ to_equiv_bits[gate_signal[i]] = equiv_signal[i];
+ added_equiv_cells.insert(equiv_cell);
+ }
+
+ for (auto cell : module->cells())
+ for (auto conn : cell->connections())
+ if (!added_equiv_cells.count(cell) && cell->input(conn.first)) {
+ SigSpec new_sig;
+ for (auto bit : conn.second)
+ if (to_equiv_bits.count(sigmap(bit)))
+ new_sig.append(to_equiv_bits.at(sigmap(bit)));
+ else
+ new_sig.append(bit);
+ if (conn.second != new_sig)
+ cell->setPort(conn.first, new_sig);
+ }
+ }
+ }
+} EquivAddPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/equiv/equiv_induct.cc b/passes/equiv/equiv_induct.cc
new file mode 100644
index 00000000..c958c3de
--- /dev/null
+++ b/passes/equiv/equiv_induct.cc
@@ -0,0 +1,241 @@
+/*
+ * 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/satgen.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct EquivInductWorker
+{
+ Module *module;
+ SigMap sigmap;
+
+ vector<Cell*> cells;
+ pool<Cell*> workset;
+
+ ezSatPtr ez;
+ SatGen satgen;
+
+ int max_seq;
+ int success_counter;
+
+ dict<int, int> ez_step_is_consistent;
+ pool<Cell*> cell_warn_cache;
+ SigPool undriven_signals;
+
+ EquivInductWorker(Module *module, const pool<Cell*> &unproven_equiv_cells, bool model_undef, int max_seq) : module(module), sigmap(module),
+ cells(module->selected_cells()), workset(unproven_equiv_cells),
+ satgen(ez.get(), &sigmap), max_seq(max_seq), success_counter(0)
+ {
+ satgen.model_undef = model_undef;
+ }
+
+ void create_timestep(int step)
+ {
+ vector<int> ez_equal_terms;
+
+ for (auto cell : cells) {
+ if (!satgen.importCell(cell, step) && !cell_warn_cache.count(cell)) {
+ log_warning("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type));
+ cell_warn_cache.insert(cell);
+ }
+ if (cell->type == "$equiv") {
+ SigBit bit_a = sigmap(cell->getPort("\\A")).as_bit();
+ SigBit bit_b = sigmap(cell->getPort("\\B")).as_bit();
+ if (bit_a != bit_b) {
+ int ez_a = satgen.importSigBit(bit_a, step);
+ int ez_b = satgen.importSigBit(bit_b, step);
+ int cond = ez->IFF(ez_a, ez_b);
+ if (satgen.model_undef)
+ cond = ez->OR(cond, satgen.importUndefSigBit(bit_a, step));
+ ez_equal_terms.push_back(cond);
+ }
+ }
+ }
+
+ if (satgen.model_undef) {
+ for (auto bit : undriven_signals.export_all())
+ ez->assume(ez->NOT(satgen.importUndefSigBit(bit, step)));
+ }
+
+ log_assert(!ez_step_is_consistent.count(step));
+ ez_step_is_consistent[step] = ez->expression(ez->OpAnd, ez_equal_terms);
+ }
+
+ void run()
+ {
+ log("Found %d unproven $equiv cells in module %s:\n", GetSize(workset), log_id(module));
+
+ if (satgen.model_undef) {
+ for (auto cell : cells)
+ if (yosys_celltypes.cell_known(cell->type))
+ for (auto &conn : cell->connections())
+ if (yosys_celltypes.cell_input(cell->type, conn.first))
+ undriven_signals.add(sigmap(conn.second));
+ for (auto cell : cells)
+ if (yosys_celltypes.cell_known(cell->type))
+ for (auto &conn : cell->connections())
+ if (yosys_celltypes.cell_output(cell->type, conn.first))
+ undriven_signals.del(sigmap(conn.second));
+ }
+
+ create_timestep(1);
+
+ if (satgen.model_undef) {
+ for (auto bit : satgen.initial_state.export_all())
+ ez->assume(ez->NOT(satgen.importUndefSigBit(bit, 1)));
+ log(" Undef modelling: force def on %d initial reg values and %d inputs.\n",
+ GetSize(satgen.initial_state), GetSize(undriven_signals));
+ }
+
+ for (int step = 1; step <= max_seq; step++)
+ {
+ ez->assume(ez_step_is_consistent[step]);
+
+ log(" Proving existence of base case for step %d. (%d clauses over %d variables)\n", step, ez->numCnfClauses(), ez->numCnfVariables());
+ if (!ez->solve()) {
+ log(" Proof for base case failed. Circuit inherently diverges!\n");
+ return;
+ }
+
+ create_timestep(step+1);
+ int new_step_not_consistent = ez->NOT(ez_step_is_consistent[step+1]);
+ ez->bind(new_step_not_consistent);
+
+ log(" Proving induction step %d. (%d clauses over %d variables)\n", step, ez->numCnfClauses(), ez->numCnfVariables());
+ if (!ez->solve(new_step_not_consistent)) {
+ log(" Proof for induction step holds. Entire workset of %d cells proven!\n", GetSize(workset));
+ for (auto cell : workset)
+ cell->setPort("\\B", cell->getPort("\\A"));
+ success_counter += GetSize(workset);
+ return;
+ }
+
+ log(" Proof for induction step failed. %s\n", step != max_seq ? "Extending to next time step." : "Trying to prove individual $equiv from workset.");
+ }
+
+ workset.sort();
+
+ for (auto cell : workset)
+ {
+ SigBit bit_a = sigmap(cell->getPort("\\A")).as_bit();
+ SigBit bit_b = sigmap(cell->getPort("\\B")).as_bit();
+
+ log(" Trying to prove $equiv for %s:", log_signal(sigmap(cell->getPort("\\Y"))));
+
+ int ez_a = satgen.importSigBit(bit_a, max_seq+1);
+ int ez_b = satgen.importSigBit(bit_b, max_seq+1);
+ int cond = ez->XOR(ez_a, ez_b);
+
+ if (satgen.model_undef)
+ cond = ez->AND(cond, ez->NOT(satgen.importUndefSigBit(bit_a, max_seq+1)));
+
+ if (!ez->solve(cond)) {
+ log(" success!\n");
+ cell->setPort("\\B", cell->getPort("\\A"));
+ success_counter++;
+ } else {
+ log(" failed.\n");
+ }
+ }
+ }
+};
+
+struct EquivInductPass : public Pass {
+ EquivInductPass() : Pass("equiv_induct", "proving $equiv cells using temporal induction") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" equiv_induct [options] [selection]\n");
+ log("\n");
+ log("Uses a version of temporal induction to prove $equiv cells.\n");
+ log("\n");
+ log("Only selected $equiv cells are proven and only selected cells are used to\n");
+ log("perform the proof.\n");
+ log("\n");
+ log(" -undef\n");
+ log(" enable modelling of undef states\n");
+ log("\n");
+ log(" -seq <N>\n");
+ log(" the max. number of time steps to be considered (default = 4)\n");
+ log("\n");
+ log("This command is very effective in proving complex sequential circuits, when\n");
+ log("the internal state of the circuit quickly propagates to $equiv cells.\n");
+ log("\n");
+ log("However, this command uses a weak definition of 'equivalence': This command\n");
+ log("proves that the two circuits will not diverge after they produce equal\n");
+ log("outputs (observable points via $equiv) for at least <N> cycles (the <N>\n");
+ log("specified via -seq).\n");
+ log("\n");
+ log("Combined with simulation this is very powerful because simulation can give\n");
+ log("you confidence that the circuits start out synced for at least <N> cycles\n");
+ log("after reset.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, Design *design)
+ {
+ int success_counter = 0;
+ bool model_undef = false;
+ int max_seq = 4;
+
+ log_header(design, "Executing EQUIV_INDUCT pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-undef") {
+ model_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-seq" && argidx+1 < args.size()) {
+ max_seq = atoi(args[++argidx].c_str());
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ pool<Cell*> unproven_equiv_cells;
+
+ for (auto cell : module->selected_cells())
+ if (cell->type == "$equiv") {
+ if (cell->getPort("\\A") != cell->getPort("\\B"))
+ unproven_equiv_cells.insert(cell);
+ }
+
+ if (unproven_equiv_cells.empty()) {
+ log("No selected unproven $equiv cells found in %s.\n", log_id(module));
+ continue;
+ }
+
+ EquivInductWorker worker(module, unproven_equiv_cells, model_undef, max_seq);
+ worker.run();
+ success_counter += worker.success_counter;
+ }
+
+ log("Proved %d previously unproven $equiv cells.\n", success_counter);
+ }
+} EquivInductPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc
new file mode 100644
index 00000000..40ca4262
--- /dev/null
+++ b/passes/equiv/equiv_make.cc
@@ -0,0 +1,474 @@
+/*
+ * 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 EquivMakeWorker
+{
+ Module *gold_mod, *gate_mod, *equiv_mod;
+ pool<IdString> wire_names, cell_names;
+ CellTypes ct;
+
+ bool inames;
+ vector<string> blacklists;
+ vector<string> encfiles;
+
+ pool<IdString> blacklist_names;
+ dict<IdString, dict<Const, Const>> encdata;
+
+ pool<SigBit> undriven_bits;
+ SigMap assign_map;
+
+ void read_blacklists()
+ {
+ for (auto fn : blacklists)
+ {
+ std::ifstream f(fn);
+ if (f.fail())
+ log_cmd_error("Can't open blacklist file '%s'!\n", fn.c_str());
+
+ string line, token;
+ while (std::getline(f, line)) {
+ while (1) {
+ token = next_token(line);
+ if (token.empty())
+ break;
+ blacklist_names.insert(RTLIL::escape_id(token));
+ }
+ }
+ }
+ }
+
+ void read_encfiles()
+ {
+ for (auto fn : encfiles)
+ {
+ std::ifstream f(fn);
+ if (f.fail())
+ log_cmd_error("Can't open encfile '%s'!\n", fn.c_str());
+
+ dict<Const, Const> *ed = nullptr;
+ string line, token;
+ while (std::getline(f, line))
+ {
+ token = next_token(line);
+ if (token.empty() || token[0] == '#')
+ continue;
+
+ if (token == ".fsm") {
+ IdString modname = RTLIL::escape_id(next_token(line));
+ IdString signame = RTLIL::escape_id(next_token(line));
+ if (encdata.count(signame))
+ log_cmd_error("Re-definition of signal '%s' in encfile '%s'!\n", signame.c_str(), fn.c_str());
+ encdata[signame] = dict<Const, Const>();
+ ed = &encdata[signame];
+ continue;
+ }
+
+ if (token == ".map") {
+ Const gold_bits = Const::from_string(next_token(line));
+ Const gate_bits = Const::from_string(next_token(line));
+ (*ed)[gold_bits] = gate_bits;
+ continue;
+ }
+
+ log_cmd_error("Syntax error in encfile '%s'!\n", fn.c_str());
+ }
+ }
+ }
+
+ void copy_to_equiv()
+ {
+ Module *gold_clone = gold_mod->clone();
+ Module *gate_clone = gate_mod->clone();
+
+ for (auto it : gold_clone->wires().to_vector()) {
+ if ((it->name[0] == '\\' || inames) && blacklist_names.count(it->name) == 0)
+ wire_names.insert(it->name);
+ gold_clone->rename(it, it->name.str() + "_gold");
+ }
+
+ for (auto it : gold_clone->cells().to_vector()) {
+ if ((it->name[0] == '\\' || inames) && blacklist_names.count(it->name) == 0)
+ cell_names.insert(it->name);
+ gold_clone->rename(it, it->name.str() + "_gold");
+ }
+
+ for (auto it : gate_clone->wires().to_vector()) {
+ if ((it->name[0] == '\\' || inames) && blacklist_names.count(it->name) == 0)
+ wire_names.insert(it->name);
+ gate_clone->rename(it, it->name.str() + "_gate");
+ }
+
+ for (auto it : gate_clone->cells().to_vector()) {
+ if ((it->name[0] == '\\' || inames) && blacklist_names.count(it->name) == 0)
+ cell_names.insert(it->name);
+ gate_clone->rename(it, it->name.str() + "_gate");
+ }
+
+ gold_clone->cloneInto(equiv_mod);
+ gate_clone->cloneInto(equiv_mod);
+ delete gold_clone;
+ delete gate_clone;
+ }
+
+ void find_same_wires()
+ {
+ SigMap assign_map(equiv_mod);
+ SigMap rd_signal_map;
+
+ // list of cells without added $equiv cells
+ auto cells_list = equiv_mod->cells().to_vector();
+
+ for (auto id : wire_names)
+ {
+ IdString gold_id = id.str() + "_gold";
+ IdString gate_id = id.str() + "_gate";
+
+ Wire *gold_wire = equiv_mod->wire(gold_id);
+ Wire *gate_wire = equiv_mod->wire(gate_id);
+
+ if (encdata.count(id))
+ {
+ log("Creating encoder/decoder for signal %s.\n", log_id(id));
+
+ Wire *dec_wire = equiv_mod->addWire(id.str() + "_decoded", gold_wire->width);
+ Wire *enc_wire = equiv_mod->addWire(id.str() + "_encoded", gate_wire->width);
+
+ SigSpec dec_a, dec_b, dec_s;
+ SigSpec enc_a, enc_b, enc_s;
+
+ dec_a = SigSpec(State::Sx, dec_wire->width);
+ enc_a = SigSpec(State::Sx, enc_wire->width);
+
+ for (auto &it : encdata.at(id))
+ {
+ SigSpec dec_sig = gate_wire, dec_pat = it.second;
+ SigSpec enc_sig = dec_wire, enc_pat = it.first;
+
+ if (GetSize(dec_sig) != GetSize(dec_pat))
+ log_error("Invalid pattern %s for signal %s of size %d!\n",
+ log_signal(dec_pat), log_signal(dec_sig), GetSize(dec_sig));
+
+ if (GetSize(enc_sig) != GetSize(enc_pat))
+ log_error("Invalid pattern %s for signal %s of size %d!\n",
+ log_signal(enc_pat), log_signal(enc_sig), GetSize(enc_sig));
+
+ SigSpec reduced_dec_sig, reduced_dec_pat;
+ for (int i = 0; i < GetSize(dec_sig); i++)
+ if (dec_pat[i] == State::S0 || dec_pat[i] == State::S1) {
+ reduced_dec_sig.append(dec_sig[i]);
+ reduced_dec_pat.append(dec_pat[i]);
+ }
+
+ SigSpec reduced_enc_sig, reduced_enc_pat;
+ for (int i = 0; i < GetSize(enc_sig); i++)
+ if (enc_pat[i] == State::S0 || enc_pat[i] == State::S1) {
+ reduced_enc_sig.append(enc_sig[i]);
+ reduced_enc_pat.append(enc_pat[i]);
+ }
+
+ SigSpec dec_result = it.first;
+ for (auto &bit : dec_result)
+ if (bit != State::S1) bit = State::S0;
+
+ SigSpec enc_result = it.second;
+ for (auto &bit : enc_result)
+ if (bit != State::S1) bit = State::S0;
+
+ SigSpec dec_eq = equiv_mod->addWire(NEW_ID);
+ SigSpec enc_eq = equiv_mod->addWire(NEW_ID);
+
+ equiv_mod->addEq(NEW_ID, reduced_dec_sig, reduced_dec_pat, dec_eq);
+ cells_list.push_back(equiv_mod->addEq(NEW_ID, reduced_enc_sig, reduced_enc_pat, enc_eq));
+
+ dec_s.append(dec_eq);
+ enc_s.append(enc_eq);
+ dec_b.append(dec_result);
+ enc_b.append(enc_result);
+ }
+
+ equiv_mod->addPmux(NEW_ID, dec_a, dec_b, dec_s, dec_wire);
+ equiv_mod->addPmux(NEW_ID, enc_a, enc_b, enc_s, enc_wire);
+
+ rd_signal_map.add(assign_map(gate_wire), enc_wire);
+ gate_wire = dec_wire;
+ }
+
+ if (gold_wire == nullptr || gate_wire == nullptr || gold_wire->width != gate_wire->width) {
+ if (gold_wire && gold_wire->port_id)
+ log_error("Can't match gold port `%s' to a gate port.\n", log_id(gold_wire));
+ if (gate_wire && gate_wire->port_id)
+ log_error("Can't match gate port `%s' to a gold port.\n", log_id(gate_wire));
+ continue;
+ }
+
+ log("Presumably equivalent wires: %s (%s), %s (%s) -> %s\n",
+ log_id(gold_wire), log_signal(assign_map(gold_wire)),
+ log_id(gate_wire), log_signal(assign_map(gate_wire)), log_id(id));
+
+ if (gold_wire->port_output || gate_wire->port_output)
+ {
+ Wire *wire = equiv_mod->addWire(id, gold_wire->width);
+ wire->port_output = true;
+ gold_wire->port_input = false;
+ gate_wire->port_input = false;
+ gold_wire->port_output = false;
+ gate_wire->port_output = false;
+
+ for (int i = 0; i < wire->width; i++)
+ equiv_mod->addEquiv(NEW_ID, SigSpec(gold_wire, i), SigSpec(gate_wire, i), SigSpec(wire, i));
+
+ rd_signal_map.add(assign_map(gold_wire), wire);
+ rd_signal_map.add(assign_map(gate_wire), wire);
+ }
+ else
+ if (gold_wire->port_input || gate_wire->port_input)
+ {
+ Wire *wire = equiv_mod->addWire(id, gold_wire->width);
+ wire->port_input = true;
+ gold_wire->port_input = false;
+ gate_wire->port_input = false;
+ equiv_mod->connect(gold_wire, wire);
+ equiv_mod->connect(gate_wire, wire);
+ }
+ else
+ {
+ Wire *wire = equiv_mod->addWire(id, gold_wire->width);
+ SigSpec rdmap_gold, rdmap_gate, rdmap_equiv;
+
+ for (int i = 0; i < wire->width; i++) {
+ if (undriven_bits.count(assign_map(SigBit(gold_wire, i)))) {
+ log(" Skipping signal bit %d: undriven on gold side.\n", i);
+ continue;
+ }
+ if (undriven_bits.count(assign_map(SigBit(gate_wire, i)))) {
+ log(" Skipping signal bit %d: undriven on gate side.\n", i);
+ continue;
+ }
+ equiv_mod->addEquiv(NEW_ID, SigSpec(gold_wire, i), SigSpec(gate_wire, i), SigSpec(wire, i));
+ rdmap_gold.append(SigBit(gold_wire, i));
+ rdmap_gate.append(SigBit(gate_wire, i));
+ rdmap_equiv.append(SigBit(wire, i));
+ }
+
+ rd_signal_map.add(rdmap_gold, rdmap_equiv);
+ rd_signal_map.add(rdmap_gate, rdmap_equiv);
+ }
+ }
+
+ 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);
+ }
+ }
+
+ equiv_mod->fixup_ports();
+ }
+
+ void find_same_cells()
+ {
+ SigMap assign_map(equiv_mod);
+
+ for (auto id : cell_names)
+ {
+ IdString gold_id = id.str() + "_gold";
+ IdString gate_id = id.str() + "_gate";
+
+ Cell *gold_cell = equiv_mod->cell(gold_id);
+ Cell *gate_cell = equiv_mod->cell(gate_id);
+
+ if (gold_cell == nullptr || gate_cell == nullptr || gold_cell->type != gate_cell->type || !ct.cell_known(gold_cell->type) ||
+ gold_cell->parameters != gate_cell->parameters || GetSize(gold_cell->connections()) != GetSize(gate_cell->connections()))
+ try_next_cell_name:
+ continue;
+
+ for (auto gold_conn : gold_cell->connections())
+ if (!gate_cell->connections().count(gold_conn.first))
+ goto try_next_cell_name;
+
+ log("Presumably equivalent cells: %s %s (%s) -> %s\n",
+ log_id(gold_cell), log_id(gate_cell), log_id(gold_cell->type), log_id(id));
+
+ for (auto gold_conn : gold_cell->connections())
+ {
+ SigSpec gold_sig = assign_map(gold_conn.second);
+ SigSpec gate_sig = assign_map(gate_cell->getPort(gold_conn.first));
+
+ if (ct.cell_output(gold_cell->type, gold_conn.first)) {
+ equiv_mod->connect(gate_sig, gold_sig);
+ continue;
+ }
+
+ for (int i = 0; i < GetSize(gold_sig); i++)
+ if (gold_sig[i] != gate_sig[i]) {
+ Wire *w = equiv_mod->addWire(NEW_ID);
+ equiv_mod->addEquiv(NEW_ID, gold_sig[i], gate_sig[i], w);
+ gold_sig[i] = w;
+ }
+
+ gold_cell->setPort(gold_conn.first, gold_sig);
+ }
+
+ equiv_mod->remove(gate_cell);
+ equiv_mod->rename(gold_cell, id);
+ }
+ }
+
+ void find_undriven_nets(bool mark)
+ {
+ undriven_bits.clear();
+ assign_map.set(equiv_mod);
+
+ for (auto wire : equiv_mod->wires()) {
+ for (auto bit : assign_map(wire))
+ if (bit.wire)
+ undriven_bits.insert(bit);
+ }
+
+ for (auto wire : equiv_mod->wires()) {
+ if (wire->port_input)
+ for (auto bit : assign_map(wire))
+ undriven_bits.erase(bit);
+ }
+
+ for (auto cell : equiv_mod->cells()) {
+ for (auto &conn : cell->connections())
+ if (!ct.cell_known(cell->type) || ct.cell_output(cell->type, conn.first))
+ for (auto bit : assign_map(conn.second))
+ undriven_bits.erase(bit);
+ }
+
+ if (mark) {
+ SigSpec undriven_sig(undriven_bits);
+ undriven_sig.sort_and_unify();
+
+ for (auto chunk : undriven_sig.chunks()) {
+ log("Setting undriven nets to undef: %s\n", log_signal(chunk));
+ equiv_mod->connect(chunk, SigSpec(State::Sx, chunk.width));
+ }
+ }
+ }
+
+ void run()
+ {
+ copy_to_equiv();
+ find_undriven_nets(false);
+ find_same_wires();
+ find_same_cells();
+ find_undriven_nets(true);
+ }
+};
+
+struct EquivMakePass : public Pass {
+ EquivMakePass() : Pass("equiv_make", "prepare a circuit for equivalence checking") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" equiv_make [options] gold_module gate_module equiv_module\n");
+ log("\n");
+ log("This creates a module annotated with $equiv cells from two presumably\n");
+ log("equivalent modules. Use commands such as 'equiv_simple' and 'equiv_status'\n");
+ log("to work with the created equivalent checking module.\n");
+ log("\n");
+ log(" -inames\n");
+ log(" Also match cells and wires with $... names.\n");
+ log("\n");
+ log(" -blacklist <file>\n");
+ log(" Do not match cells or signals that match the names in the file.\n");
+ log("\n");
+ log(" -encfile <file>\n");
+ log(" Match FSM encodings using the description from the file.\n");
+ log(" See 'help fsm_recode' for details.\n");
+ log("\n");
+ log("Note: The circuit created by this command is not a miter (with something like\n");
+ log("a trigger output), but instead uses $equiv cells to encode the equivalence\n");
+ log("checking problem. Use 'miter -equiv' if you want to create a miter circuit.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ EquivMakeWorker worker;
+ worker.ct.setup(design);
+ worker.inames = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-inames") {
+ worker.inames = true;
+ continue;
+ }
+ if (args[argidx] == "-blacklist" && argidx+1 < args.size()) {
+ worker.blacklists.push_back(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-encfile" && argidx+1 < args.size()) {
+ worker.encfiles.push_back(args[++argidx]);
+ continue;
+ }
+ break;
+ }
+
+ if (argidx+3 != args.size())
+ log_cmd_error("Invalid number of arguments.\n");
+
+ worker.gold_mod = design->module(RTLIL::escape_id(args[argidx]));
+ worker.gate_mod = design->module(RTLIL::escape_id(args[argidx+1]));
+ worker.equiv_mod = design->module(RTLIL::escape_id(args[argidx+2]));
+
+ if (worker.gold_mod == nullptr)
+ log_cmd_error("Can't find gold module %s.\n", args[argidx].c_str());
+
+ if (worker.gate_mod == nullptr)
+ log_cmd_error("Can't find gate module %s.\n", args[argidx+1].c_str());
+
+ if (worker.equiv_mod != nullptr)
+ log_cmd_error("Equiv module %s already exists.\n", args[argidx+2].c_str());
+
+ if (worker.gold_mod->has_memories() || worker.gold_mod->has_processes())
+ log_cmd_error("Gold module contains memories or procresses. Run 'memory' or 'proc' respectively.\n");
+
+ if (worker.gate_mod->has_memories() || worker.gate_mod->has_processes())
+ log_cmd_error("Gate module contains memories or procresses. Run 'memory' or 'proc' respectively.\n");
+
+ worker.read_blacklists();
+ worker.read_encfiles();
+
+ log_header(design, "Executing EQUIV_MAKE pass (creating equiv checking module).\n");
+
+ worker.equiv_mod = design->addModule(RTLIL::escape_id(args[argidx+2]));
+ worker.run();
+ }
+} EquivMakePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/equiv/equiv_mark.cc b/passes/equiv/equiv_mark.cc
new file mode 100644
index 00000000..22c50176
--- /dev/null
+++ b/passes/equiv/equiv_mark.cc
@@ -0,0 +1,239 @@
+/*
+ * 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 EquivMarkWorker
+{
+ Module *module;
+ SigMap sigmap;
+
+ // cache for traversing signal flow graph
+ dict<SigBit, pool<IdString>> up_bit2cells;
+ dict<IdString, pool<SigBit>> up_cell2bits;
+ pool<IdString> edge_cells, equiv_cells;
+
+ // graph traversal state
+ pool<SigBit> queue, visited;
+
+ // assigned regions
+ dict<IdString, int> cell_regions;
+ dict<SigBit, int> bit_regions;
+ int next_region;
+
+ // merge-find
+ mfp<int> region_mf;
+
+ EquivMarkWorker(Module *module) : module(module), sigmap(module)
+ {
+ for (auto cell : module->cells())
+ {
+ if (cell->type == "$equiv")
+ equiv_cells.insert(cell->name);
+
+ for (auto &port : cell->connections())
+ {
+ if (cell->input(port.first))
+ for (auto bit : sigmap(port.second))
+ up_cell2bits[cell->name].insert(bit);
+
+ if (cell->output(port.first))
+ for (auto bit : sigmap(port.second))
+ up_bit2cells[bit].insert(cell->name);
+ }
+ }
+
+ next_region = 0;
+ }
+
+ void mark()
+ {
+ while (!queue.empty())
+ {
+ pool<IdString> cells;
+
+ for (auto &bit : queue)
+ {
+ // log_assert(bit_regions.count(bit) == 0);
+ bit_regions[bit] = next_region;
+ visited.insert(bit);
+
+ for (auto cell : up_bit2cells[bit])
+ if (edge_cells.count(cell) == 0)
+ cells.insert(cell);
+ }
+
+ queue.clear();
+
+ for (auto cell : cells)
+ {
+ if (next_region == 0 && equiv_cells.count(cell))
+ continue;
+
+ if (cell_regions.count(cell)) {
+ if (cell_regions.at(cell) != 0)
+ region_mf.merge(cell_regions.at(cell), next_region);
+ continue;
+ }
+
+ cell_regions[cell] = next_region;
+
+ for (auto bit : up_cell2bits[cell])
+ if (visited.count(bit) == 0)
+ queue.insert(bit);
+ }
+ }
+
+ next_region++;
+ }
+
+ void run()
+ {
+ log("Running equiv_mark on module %s:\n", log_id(module));
+
+ // marking region 0
+
+ for (auto wire : module->wires())
+ if (wire->port_id > 0)
+ for (auto bit : sigmap(wire))
+ queue.insert(bit);
+
+ for (auto cell_name : equiv_cells)
+ {
+ auto cell = module->cell(cell_name);
+
+ SigSpec sig_a = sigmap(cell->getPort("\\A"));
+ SigSpec sig_b = sigmap(cell->getPort("\\B"));
+
+ if (sig_a == sig_b) {
+ for (auto bit : sig_a)
+ queue.insert(bit);
+ edge_cells.insert(cell_name);
+ cell_regions[cell_name] = 0;
+ }
+ }
+
+ mark();
+
+ // marking unsolved regions
+
+ for (auto cell : module->cells())
+ {
+ if (cell_regions.count(cell->name) || cell->type != "$equiv")
+ continue;
+
+ SigSpec sig_a = sigmap(cell->getPort("\\A"));
+ SigSpec sig_b = sigmap(cell->getPort("\\B"));
+
+ log_assert(sig_a != sig_b);
+
+ for (auto bit : sig_a)
+ queue.insert(bit);
+
+ for (auto bit : sig_b)
+ queue.insert(bit);
+
+ cell_regions[cell->name] = next_region;
+ mark();
+ }
+
+ // setting attributes
+
+ dict<int, int> final_region_map;
+ int next_final_region = 0;
+
+ dict<int, int> region_cell_count;
+ dict<int, int> region_wire_count;
+
+ for (int i = 0; i < next_region; i++) {
+ int r = region_mf.find(i);
+ if (final_region_map.count(r) == 0)
+ final_region_map[r] = next_final_region++;
+ final_region_map[i] = final_region_map[r];
+ }
+
+ for (auto cell : module->cells())
+ {
+ if (cell_regions.count(cell->name)) {
+ int r = final_region_map.at(cell_regions.at(cell->name));
+ cell->attributes["\\equiv_region"] = Const(r);
+ region_cell_count[r]++;
+ } else
+ cell->attributes.erase("\\equiv_region");
+ }
+
+ for (auto wire : module->wires())
+ {
+ pool<int> regions;
+ for (auto bit : sigmap(wire))
+ if (bit_regions.count(bit))
+ regions.insert(region_mf.find(bit_regions.at(bit)));
+
+ if (GetSize(regions) == 1) {
+ int r = final_region_map.at(*regions.begin());
+ wire->attributes["\\equiv_region"] = Const(r);
+ region_wire_count[r]++;
+ } else
+ wire->attributes.erase("\\equiv_region");
+ }
+
+ for (int i = 0; i < next_final_region; i++)
+ log(" region %d: %d cells, %d wires\n", i, region_wire_count[i], region_cell_count[i]);
+ }
+};
+
+struct EquivMarkPass : public Pass {
+ EquivMarkPass() : Pass("equiv_mark", "mark equivalence checking regions") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" equiv_mark [options] [selection]\n");
+ log("\n");
+ log("This command marks the regions in an equivalence checking module. Region 0 is\n");
+ log("the proven part of the circuit. Regions with higher numbers are connected\n");
+ log("unproven subcricuits. The integer attribute 'equiv_region' is set on all\n");
+ log("wires and cells.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, Design *design)
+ {
+ log_header(design, "Executing EQUIV_MARK pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ // if (args[argidx] == "-foobar") {
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_whole_modules_warn()) {
+ EquivMarkWorker worker(module);
+ worker.run();
+ }
+ }
+} EquivMarkPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/equiv/equiv_miter.cc b/passes/equiv/equiv_miter.cc
new file mode 100644
index 00000000..eb2e5a17
--- /dev/null
+++ b/passes/equiv/equiv_miter.cc
@@ -0,0 +1,343 @@
+/*
+ * 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 EquivMiterWorker
+{
+ CellTypes ct;
+ SigMap sigmap;
+
+ bool mode_trigger;
+ bool mode_cmp;
+ bool mode_assert;
+ bool mode_undef;
+
+ IdString miter_name;
+ Module *miter_module;
+ Module *source_module;
+
+ dict<SigBit, Cell*> bit_to_driver;
+ pool<Cell*> seed_cells, miter_cells;
+ pool<Wire*> miter_wires;
+
+ void follow_cone(pool<Cell*> &cone, pool<Cell*> &leaves, Cell *c, bool gold_mode)
+ {
+ if (cone.count(c))
+ return;
+
+ if (c->type == "$equiv" && !seed_cells.count(c)) {
+ leaves.insert(c);
+ return;
+ }
+
+ cone.insert(c);
+
+ for (auto &conn : c->connections()) {
+ if (!ct.cell_input(c->type, conn.first))
+ continue;
+ if (c->type == "$equiv" && (conn.first == "\\A") != gold_mode)
+ continue;
+ for (auto bit : sigmap(conn.second))
+ if (bit_to_driver.count(bit))
+ follow_cone(cone, leaves, bit_to_driver.at(bit), gold_mode);
+ }
+ }
+
+ void find_miter_cells_wires()
+ {
+ sigmap.set(source_module);
+
+ // initialize bit_to_driver
+
+ for (auto c : source_module->cells())
+ for (auto &conn : c->connections())
+ if (ct.cell_output(c->type, conn.first))
+ for (auto bit : sigmap(conn.second))
+ if (bit.wire)
+ bit_to_driver[bit] = c;
+
+ // find seed cells
+
+ for (auto c : source_module->selected_cells())
+ if (c->type == "$equiv") {
+ log("Seed $equiv cell: %s\n", log_id(c));
+ seed_cells.insert(c);
+ }
+
+ // follow cone from seed cells to next $equiv
+
+ while (1)
+ {
+ pool<Cell*> gold_cone, gold_leaves;
+ pool<Cell*> gate_cone, gate_leaves;
+
+ for (auto c : seed_cells) {
+ follow_cone(gold_cone, gold_leaves, c, true);
+ follow_cone(gate_cone, gate_leaves, c, false);
+ }
+
+ log("Gold cone: %d cells (%d leaves).\n", GetSize(gold_cone), GetSize(gold_leaves));
+ log("Gate cone: %d cells (%d leaves).\n", GetSize(gate_cone), GetSize(gate_leaves));
+
+ // done if all leaves are shared leaves
+
+ if (gold_leaves == gate_leaves) {
+ miter_cells = gold_cone;
+ miter_cells.insert(gate_cone.begin(), gate_cone.end());
+ log("Selected %d miter cells.\n", GetSize(miter_cells));
+ break;
+ }
+
+ // remove shared leaves
+
+ for (auto it = gold_leaves.begin(); it != gold_leaves.end(); ) {
+ auto it2 = gate_leaves.find(*it);
+ if (it2 != gate_leaves.end()) {
+ it = gold_leaves.erase(it);
+ gate_leaves.erase(it2);
+ } else
+ ++it;
+ }
+
+ // add remaining leaves to seeds and re-run
+
+ log("Adding %d gold and %d gate seed cells.\n", GetSize(gold_leaves), GetSize(gate_leaves));
+ seed_cells.insert(gold_leaves.begin(), gold_leaves.end());
+ seed_cells.insert(gate_leaves.begin(), gate_leaves.end());
+ }
+
+ for (auto c : miter_cells)
+ for (auto &conn : c->connections())
+ for (auto bit : sigmap(conn.second))
+ if (bit.wire)
+ miter_wires.insert(bit.wire);
+ log("Selected %d miter wires.\n", GetSize(miter_wires));
+ }
+
+ void copy_to_miter()
+ {
+ // copy wires and cells
+
+ for (auto w : miter_wires)
+ miter_module->addWire(w->name, w->width);
+ for (auto c : miter_cells) {
+ miter_module->addCell(c->name, c);
+ auto mc = miter_module->cell(c->name);
+ for (auto &conn : mc->connections())
+ mc->setPort(conn.first, sigmap(conn.second));
+ }
+
+ // fixup wire references in cells
+
+ sigmap.clear();
+
+ struct RewriteSigSpecWorker {
+ RTLIL::Module * mod;
+ void operator()(SigSpec &sig) {
+ vector<SigChunk> chunks = sig.chunks();
+ for (auto &c : chunks)
+ if (c.wire != NULL)
+ c.wire = mod->wires_.at(c.wire->name);
+ sig = chunks;
+ }
+ };
+
+ RewriteSigSpecWorker rewriteSigSpecWorker;
+ rewriteSigSpecWorker.mod = miter_module;
+ miter_module->rewrite_sigspecs(rewriteSigSpecWorker);
+
+ // find undriven or unused wires
+
+ pool<SigBit> driven_bits, used_bits;
+
+ for (auto c : miter_module->cells())
+ for (auto &conn : c->connections()) {
+ if (ct.cell_input(c->type, conn.first))
+ for (auto bit : conn.second)
+ if (bit.wire)
+ used_bits.insert(bit);
+ if (ct.cell_output(c->type, conn.first))
+ for (auto bit : conn.second)
+ if (bit.wire)
+ driven_bits.insert(bit);
+ }
+
+ // create ports
+
+ for (auto w : miter_module->wires()) {
+ for (auto bit : SigSpec(w)) {
+ if (driven_bits.count(bit) && !used_bits.count(bit))
+ w->port_output = true;
+ if (!driven_bits.count(bit) && used_bits.count(bit))
+ w->port_input = true;
+ }
+ if (w->port_output && w->port_input)
+ log("Created miter inout port %s.\n", log_id(w));
+ else if (w->port_output)
+ log("Created miter output port %s.\n", log_id(w));
+ else if (w->port_input)
+ log("Created miter input port %s.\n", log_id(w));
+ }
+
+ miter_module->fixup_ports();
+ }
+
+ void make_stuff()
+ {
+ if (!mode_trigger && !mode_cmp && !mode_assert)
+ return;
+
+ SigSpec trigger_signals;
+ vector<Cell*> equiv_cells;
+
+ for (auto c : miter_module->cells())
+ if (c->type == "$equiv" && c->getPort("\\A") != c->getPort("\\B"))
+ equiv_cells.push_back(c);
+
+ for (auto c : equiv_cells)
+ {
+ SigSpec cmp = mode_undef ?
+ miter_module->LogicOr(NEW_ID, miter_module->Eqx(NEW_ID, c->getPort("\\A"), State::Sx),
+ miter_module->Eqx(NEW_ID, c->getPort("\\A"), c->getPort("\\B"))) :
+ miter_module->Eq(NEW_ID, c->getPort("\\A"), c->getPort("\\B"));
+
+ if (mode_cmp) {
+ string cmp_name = string("\\cmp") + log_signal(c->getPort("\\Y"));
+ for (int i = 1; i < GetSize(cmp_name); i++)
+ if (cmp_name[i] == '\\')
+ cmp_name[i] = '_';
+ else if (cmp_name[i] == ' ')
+ cmp_name = cmp_name.substr(0, i) + cmp_name.substr(i+1);
+ auto w = miter_module->addWire(cmp_name);
+ w->port_output = true;
+ miter_module->connect(w, cmp);
+ }
+
+ if (mode_assert)
+ miter_module->addAssert(NEW_ID, cmp, State::S1);
+
+ trigger_signals.append(miter_module->Not(NEW_ID, cmp));
+ }
+
+ if (mode_trigger) {
+ auto w = miter_module->addWire("\\trigger");
+ w->port_output = true;
+ miter_module->addReduceOr(NEW_ID, trigger_signals, w);
+ }
+
+ miter_module->fixup_ports();
+ }
+
+ void run()
+ {
+ log("Creating miter %s from module %s.\n", log_id(miter_module), log_id(source_module));
+ find_miter_cells_wires();
+ copy_to_miter();
+ make_stuff();
+ }
+};
+
+struct EquivMiterPass : public Pass {
+ EquivMiterPass() : Pass("equiv_miter", "extract miter from equiv circuit") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" equiv_miter [options] miter_module [selection]\n");
+ log("\n");
+ log("This creates a miter module for further analysis of the selected $equiv cells.\n");
+ log("\n");
+ log(" -trigger\n");
+ log(" Create a trigger output\n");
+ log("\n");
+ log(" -cmp\n");
+ log(" Create cmp_* outputs for individual unproven $equiv cells\n");
+ log("\n");
+ log(" -assert\n");
+ log(" Create a $assert cell for each unproven $equiv cell\n");
+ log("\n");
+ log(" -undef\n");
+ log(" Create compare logic that handles undefs correctly\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ EquivMiterWorker worker;
+ worker.ct.setup(design);
+ worker.mode_trigger = false;
+ worker.mode_cmp = false;
+ worker.mode_assert = false;
+ worker.mode_undef = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-trigger") {
+ worker.mode_trigger = true;
+ continue;
+ }
+ if (args[argidx] == "-cmp") {
+ worker.mode_cmp = true;
+ continue;
+ }
+ if (args[argidx] == "-assert") {
+ worker.mode_assert = true;
+ continue;
+ }
+ if (args[argidx] == "-undef") {
+ worker.mode_undef = true;
+ continue;
+ }
+ break;
+ }
+
+ if (argidx >= args.size())
+ log_cmd_error("Invalid number of arguments.\n");
+
+ worker.miter_name = RTLIL::escape_id(args[argidx++]);
+ extra_args(args, argidx, design);
+
+ if (design->module(worker.miter_name))
+ log_cmd_error("Miter module %s already exists.\n", log_id(worker.miter_name));
+
+ worker.source_module = nullptr;
+ for (auto m : design->selected_modules()) {
+ if (worker.source_module != nullptr)
+ goto found_two_modules;
+ worker.source_module = m;
+ }
+
+ if (worker.source_module == nullptr)
+ found_two_modules:
+ log_cmd_error("Exactly one module must be selected for 'equiv_miter'!\n");
+
+ log_header(design, "Executing EQUIV_MITER pass.\n");
+
+ worker.miter_module = design->addModule(worker.miter_name);
+ worker.run();
+ }
+} EquivMiterPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/equiv/equiv_purge.cc b/passes/equiv/equiv_purge.cc
new file mode 100644
index 00000000..163b1009
--- /dev/null
+++ b/passes/equiv/equiv_purge.cc
@@ -0,0 +1,210 @@
+/*
+ * 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 EquivPurgeWorker
+{
+ Module *module;
+ SigMap sigmap;
+ int name_cnt;
+
+ EquivPurgeWorker(Module *module) : module(module), sigmap(module), name_cnt(0) { }
+
+ SigSpec make_output(SigSpec sig, IdString cellname)
+ {
+ if (sig.is_wire()) {
+ Wire *wire = sig.as_wire();
+ if (wire->name[0] == '\\') {
+ if (!wire->port_output) {
+ log(" Module output: %s (%s)\n", log_signal(wire), log_id(cellname));
+ wire->port_output = true;
+ }
+ return wire;
+ }
+ }
+
+ while (1)
+ {
+ IdString name = stringf("\\equiv_%d", name_cnt++);
+ if (module->count_id(name))
+ continue;
+
+ Wire *wire = module->addWire(name, GetSize(sig));
+ wire->port_output = true;
+ module->connect(wire, sig);
+ log(" Module output: %s (%s)\n", log_signal(wire), log_id(cellname));
+ return wire;
+ }
+ }
+
+ SigSpec make_input(SigSpec sig)
+ {
+ if (sig.is_wire()) {
+ Wire *wire = sig.as_wire();
+ if (wire->name[0] == '\\') {
+ if (!wire->port_output) {
+ log(" Module input: %s\n", log_signal(wire));
+ wire->port_input = true;
+ }
+ return module->addWire(NEW_ID, GetSize(sig));
+ }
+ }
+
+ while (1)
+ {
+ IdString name = stringf("\\equiv_%d", name_cnt++);
+ if (module->count_id(name))
+ continue;
+
+ Wire *wire = module->addWire(name, GetSize(sig));
+ wire->port_input = true;
+ module->connect(sig, wire);
+ log(" Module input: %s\n", log_signal(wire));
+ return module->addWire(NEW_ID, GetSize(sig));
+ }
+ }
+
+ void run()
+ {
+ log("Running equiv_purge on module %s:\n", log_id(module));
+
+ for (auto wire : module->wires()) {
+ wire->port_input = false;
+ wire->port_output = false;
+ }
+
+ pool<SigBit> queue, visited;
+
+ // cache for traversing signal flow graph
+ dict<SigBit, pool<IdString>> up_bit2cells;
+ dict<IdString, pool<SigBit>> up_cell2bits;
+
+ for (auto cell : module->cells())
+ {
+ if (cell->type != "$equiv") {
+ for (auto &port : cell->connections()) {
+ if (cell->input(port.first))
+ for (auto bit : sigmap(port.second))
+ up_cell2bits[cell->name].insert(bit);
+ if (cell->output(port.first))
+ for (auto bit : sigmap(port.second))
+ up_bit2cells[bit].insert(cell->name);
+ }
+ continue;
+ }
+
+ SigSpec sig_a = sigmap(cell->getPort("\\A"));
+ SigSpec sig_b = sigmap(cell->getPort("\\B"));
+ SigSpec sig_y = sigmap(cell->getPort("\\Y"));
+
+ if (sig_a == sig_b)
+ continue;
+
+ for (auto bit : sig_a)
+ queue.insert(bit);
+
+ for (auto bit : sig_b)
+ queue.insert(bit);
+
+ for (auto bit : sig_y)
+ visited.insert(bit);
+
+ cell->setPort("\\Y", make_output(sig_y, cell->name));
+ }
+
+ SigSpec srcsig;
+ SigMap rewrite_sigmap(module);
+
+ while (!queue.empty())
+ {
+ pool<SigBit> next_queue;
+
+ for (auto bit : queue)
+ visited.insert(bit);
+
+ for (auto bit : queue)
+ {
+ auto &cells = up_bit2cells[bit];
+
+ if (cells.empty()) {
+ srcsig.append(bit);
+ } else {
+ for (auto cell : cells)
+ for (auto bit : up_cell2bits[cell])
+ if (visited.count(bit) == 0)
+ next_queue.insert(bit);
+ }
+ }
+
+ next_queue.swap(queue);
+ }
+
+ srcsig.sort_and_unify();
+
+ for (SigChunk chunk : srcsig.chunks())
+ if (chunk.wire != nullptr)
+ rewrite_sigmap.add(chunk, make_input(chunk));
+
+ for (auto cell : module->cells())
+ if (cell->type == "$equiv")
+ cell->setPort("\\Y", rewrite_sigmap(sigmap(cell->getPort("\\Y"))));
+
+ module->fixup_ports();
+ }
+};
+
+struct EquivPurgePass : public Pass {
+ EquivPurgePass() : Pass("equiv_purge", "purge equivalence checking module") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" equiv_purge [options] [selection]\n");
+ log("\n");
+ log("This command removes the proven part of an equivalence checking module, leaving\n");
+ log("only the unproven segments in the design. This will also remove and add module\n");
+ log("ports as needed.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, Design *design)
+ {
+ log_header(design, "Executing EQUIV_PURGE pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ // if (args[argidx] == "-foobar") {
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_whole_modules_warn()) {
+ EquivPurgeWorker worker(module);
+ worker.run();
+ }
+ }
+} EquivPurgePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/equiv/equiv_remove.cc b/passes/equiv/equiv_remove.cc
new file mode 100644
index 00000000..770497a5
--- /dev/null
+++ b/passes/equiv/equiv_remove.cc
@@ -0,0 +1,83 @@
+/*
+ * 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"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct EquivRemovePass : public Pass {
+ EquivRemovePass() : Pass("equiv_remove", "remove $equiv cells") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" equiv_remove [options] [selection]\n");
+ log("\n");
+ log("This command removes the selected $equiv cells. If neither -gold nor -gate is\n");
+ log("used then only proven cells are removed.\n");
+ log("\n");
+ log(" -gold\n");
+ log(" keep gold circuit\n");
+ log("\n");
+ log(" -gate\n");
+ log(" keep gate circuit\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, Design *design)
+ {
+ bool mode_gold = false;
+ bool mode_gate = false;
+ int remove_count = 0;
+
+ log_header(design, "Executing EQUIV_REMOVE pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-gold") {
+ mode_gold = true;
+ continue;
+ }
+ if (args[argidx] == "-gate") {
+ mode_gate = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (mode_gold && mode_gate)
+ log_cmd_error("Options -gold and -gate are exclusive.\n");
+
+ for (auto module : design->selected_modules())
+ {
+ for (auto cell : module->selected_cells())
+ if (cell->type == "$equiv" && (mode_gold || mode_gate || cell->getPort("\\A") == cell->getPort("\\B"))) {
+ log("Removing $equiv cell %s.%s (%s).\n", log_id(module), log_id(cell), log_signal(cell->getPort("\\Y")));
+ module->connect(cell->getPort("\\Y"), mode_gate ? cell->getPort("\\B") : cell->getPort("\\A"));
+ module->remove(cell);
+ remove_count++;
+ }
+ }
+
+ log("Removed a total of %d $equiv cells.\n", remove_count);
+ }
+} EquivRemovePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/equiv/equiv_simple.cc b/passes/equiv/equiv_simple.cc
new file mode 100644
index 00000000..49963ed6
--- /dev/null
+++ b/passes/equiv/equiv_simple.cc
@@ -0,0 +1,358 @@
+/*
+ * 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/satgen.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct EquivSimpleWorker
+{
+ Module *module;
+ const vector<Cell*> &equiv_cells;
+ Cell *equiv_cell;
+
+ SigMap &sigmap;
+ dict<SigBit, Cell*> &bit2driver;
+
+ ezSatPtr ez;
+ SatGen satgen;
+ int max_seq;
+ bool verbose;
+
+ pool<pair<Cell*, int>> imported_cells_cache;
+
+ EquivSimpleWorker(const vector<Cell*> &equiv_cells, SigMap &sigmap, dict<SigBit, Cell*> &bit2driver, int max_seq, bool verbose, bool model_undef) :
+ module(equiv_cells.front()->module), equiv_cells(equiv_cells), equiv_cell(nullptr),
+ sigmap(sigmap), bit2driver(bit2driver), satgen(ez.get(), &sigmap), max_seq(max_seq), verbose(verbose)
+ {
+ satgen.model_undef = model_undef;
+ }
+
+ bool find_input_cone(pool<SigBit> &next_seed, pool<Cell*> &cells_cone, pool<SigBit> &bits_cone, const pool<Cell*> &cells_stop, const pool<SigBit> &bits_stop, pool<SigBit> *input_bits, Cell *cell)
+ {
+ if (cells_cone.count(cell))
+ return false;
+
+ cells_cone.insert(cell);
+
+ if (cells_stop.count(cell))
+ return true;
+
+ for (auto &conn : cell->connections())
+ if (yosys_celltypes.cell_input(cell->type, conn.first))
+ for (auto bit : sigmap(conn.second)) {
+ if (cell->type.in("$dff", "$_DFF_P_", "$_DFF_N_")) {
+ if (!conn.first.in("\\CLK", "\\C"))
+ next_seed.insert(bit);
+ } else
+ find_input_cone(next_seed, cells_cone, bits_cone, cells_stop, bits_stop, input_bits, bit);
+ }
+ return false;
+ }
+
+ void find_input_cone(pool<SigBit> &next_seed, pool<Cell*> &cells_cone, pool<SigBit> &bits_cone, const pool<Cell*> &cells_stop, const pool<SigBit> &bits_stop, pool<SigBit> *input_bits, SigBit bit)
+ {
+ if (bits_cone.count(bit))
+ return;
+
+ bits_cone.insert(bit);
+
+ if (bits_stop.count(bit)) {
+ if (input_bits != nullptr) input_bits->insert(bit);
+ return;
+ }
+
+ if (!bit2driver.count(bit))
+ return;
+
+ if (find_input_cone(next_seed, cells_cone, bits_cone, cells_stop, bits_stop, input_bits, bit2driver.at(bit)))
+ if (input_bits != nullptr) input_bits->insert(bit);
+ }
+
+ bool run_cell()
+ {
+ SigBit bit_a = sigmap(equiv_cell->getPort("\\A")).as_bit();
+ SigBit bit_b = sigmap(equiv_cell->getPort("\\B")).as_bit();
+ int ez_context = ez->frozen_literal();
+
+ if (satgen.model_undef)
+ {
+ int ez_a = satgen.importSigBit(bit_a, max_seq+1);
+ int ez_b = satgen.importDefSigBit(bit_b, max_seq+1);
+ int ez_undef_a = satgen.importUndefSigBit(bit_a, max_seq+1);
+
+ ez->assume(ez->XOR(ez_a, ez_b), ez_context);
+ ez->assume(ez->NOT(ez_undef_a), ez_context);
+ }
+ else
+ {
+ int ez_a = satgen.importSigBit(bit_a, max_seq+1);
+ int ez_b = satgen.importSigBit(bit_b, max_seq+1);
+ ez->assume(ez->XOR(ez_a, ez_b), ez_context);
+ }
+
+ pool<SigBit> seed_a = { bit_a };
+ pool<SigBit> seed_b = { bit_b };
+
+ if (verbose) {
+ log(" Trying to prove $equiv cell %s:\n", log_id(equiv_cell));
+ log(" A = %s, B = %s, Y = %s\n", log_signal(bit_a), log_signal(bit_b), log_signal(equiv_cell->getPort("\\Y")));
+ } else {
+ log(" Trying to prove $equiv for %s:", log_signal(equiv_cell->getPort("\\Y")));
+ }
+
+ int step = max_seq;
+ while (1)
+ {
+ pool<Cell*> no_stop_cells;
+ pool<SigBit> no_stop_bits;
+
+ pool<Cell*> full_cells_cone_a, full_cells_cone_b;
+ pool<SigBit> full_bits_cone_a, full_bits_cone_b;
+
+ pool<SigBit> next_seed_a, next_seed_b;
+
+ for (auto bit_a : seed_a)
+ find_input_cone(next_seed_a, full_cells_cone_a, full_bits_cone_a, no_stop_cells, no_stop_bits, nullptr, bit_a);
+ next_seed_a.clear();
+
+ for (auto bit_b : seed_b)
+ find_input_cone(next_seed_b, full_cells_cone_b, full_bits_cone_b, no_stop_cells, no_stop_bits, nullptr, bit_b);
+ next_seed_b.clear();
+
+ pool<Cell*> short_cells_cone_a, short_cells_cone_b;
+ pool<SigBit> short_bits_cone_a, short_bits_cone_b;
+ pool<SigBit> input_bits;
+
+ for (auto bit_a : seed_a)
+ find_input_cone(next_seed_a, short_cells_cone_a, short_bits_cone_a, full_cells_cone_b, full_bits_cone_b, &input_bits, bit_a);
+ next_seed_a.swap(seed_a);
+
+ for (auto bit_b : seed_b)
+ find_input_cone(next_seed_b, short_cells_cone_b, short_bits_cone_b, full_cells_cone_a, full_bits_cone_a, &input_bits, bit_b);
+ next_seed_b.swap(seed_b);
+
+ pool<Cell*> problem_cells;
+ problem_cells.insert(short_cells_cone_a.begin(), short_cells_cone_a.end());
+ problem_cells.insert(short_cells_cone_b.begin(), short_cells_cone_b.end());
+
+ if (verbose)
+ log(" Adding %d new cells to the problem (%d A, %d B, %d shared).\n",
+ GetSize(problem_cells), GetSize(short_cells_cone_a), GetSize(short_cells_cone_b),
+ (GetSize(short_cells_cone_a) + GetSize(short_cells_cone_b)) - GetSize(problem_cells));
+
+ for (auto cell : problem_cells) {
+ auto key = pair<Cell*, int>(cell, step+1);
+ if (!imported_cells_cache.count(key) && !satgen.importCell(cell, step+1))
+ log_cmd_error("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type));
+ imported_cells_cache.insert(key);
+ }
+
+ if (satgen.model_undef) {
+ for (auto bit : input_bits)
+ ez->assume(ez->NOT(satgen.importUndefSigBit(bit, step+1)));
+ }
+
+ if (verbose)
+ log(" Problem size at t=%d: %d literals, %d clauses\n", step, ez->numCnfVariables(), ez->numCnfClauses());
+
+ if (!ez->solve(ez_context)) {
+ log(verbose ? " Proved equivalence! Marking $equiv cell as proven.\n" : " success!\n");
+ equiv_cell->setPort("\\B", equiv_cell->getPort("\\A"));
+ ez->assume(ez->NOT(ez_context));
+ return true;
+ }
+
+ if (verbose)
+ log(" Failed to prove equivalence with sequence length %d.\n", max_seq - step);
+
+ if (--step < 0) {
+ if (verbose)
+ log(" Reached sequence limit.\n");
+ break;
+ }
+
+ if (seed_a.empty() && seed_b.empty()) {
+ if (verbose)
+ log(" No nets to continue in previous time step.\n");
+ break;
+ }
+
+ if (seed_a.empty()) {
+ if (verbose)
+ log(" No nets on A-side to continue in previous time step.\n");
+ break;
+ }
+
+ if (seed_b.empty()) {
+ if (verbose)
+ log(" No nets on B-side to continue in previous time step.\n");
+ break;
+ }
+
+ if (verbose) {
+ #if 0
+ log(" Continuing analysis in previous time step with the following nets:\n");
+ for (auto bit : seed_a)
+ log(" A: %s\n", log_signal(bit));
+ for (auto bit : seed_b)
+ log(" B: %s\n", log_signal(bit));
+ #else
+ log(" Continuing analysis in previous time step with %d A- and %d B-nets.\n", GetSize(seed_a), GetSize(seed_b));
+ #endif
+ }
+ }
+
+ if (!verbose)
+ log(" failed.\n");
+
+ ez->assume(ez->NOT(ez_context));
+ return false;
+ }
+
+ int run()
+ {
+ if (GetSize(equiv_cells) > 1) {
+ SigSpec sig;
+ for (auto c : equiv_cells)
+ sig.append(sigmap(c->getPort("\\Y")));
+ log(" Grouping SAT models for %s:\n", log_signal(sig));
+ }
+
+ int counter = 0;
+ for (auto c : equiv_cells) {
+ equiv_cell = c;
+ if (run_cell())
+ counter++;
+ }
+ return counter;
+ }
+
+};
+
+struct EquivSimplePass : public Pass {
+ EquivSimplePass() : Pass("equiv_simple", "try proving simple $equiv instances") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" equiv_simple [options] [selection]\n");
+ log("\n");
+ log("This command tries to prove $equiv cells using a simple direct SAT approach.\n");
+ log("\n");
+ log(" -v\n");
+ log(" verbose output\n");
+ log("\n");
+ log(" -undef\n");
+ log(" enable modelling of undef states\n");
+ log("\n");
+ log(" -nogroup\n");
+ log(" disabling grouping of $equiv cells by output wire\n");
+ log("\n");
+ log(" -seq <N>\n");
+ log(" the max. number of time steps to be considered (default = 1)\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, Design *design)
+ {
+ bool verbose = false, model_undef = false, nogroup = false;
+ int success_counter = 0;
+ int max_seq = 1;
+
+ log_header(design, "Executing EQUIV_SIMPLE pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-v") {
+ verbose = true;
+ continue;
+ }
+ if (args[argidx] == "-undef") {
+ model_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-nogroup") {
+ nogroup = true;
+ continue;
+ }
+ if (args[argidx] == "-seq" && argidx+1 < args.size()) {
+ max_seq = atoi(args[++argidx].c_str());
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ CellTypes ct;
+ ct.setup_internals();
+ ct.setup_stdcells();
+
+ for (auto module : design->selected_modules())
+ {
+ SigMap sigmap(module);
+ dict<SigBit, Cell*> bit2driver;
+ dict<SigBit, dict<SigBit, Cell*>> unproven_equiv_cells;
+ int unproven_cells_counter = 0;
+
+ for (auto cell : module->selected_cells())
+ if (cell->type == "$equiv" && cell->getPort("\\A") != cell->getPort("\\B")) {
+ auto bit = sigmap(cell->getPort("\\Y").as_bit());
+ auto bit_group = bit;
+ if (!nogroup && bit_group.wire)
+ bit_group.offset = 0;
+ unproven_equiv_cells[bit_group][bit] = cell;
+ unproven_cells_counter++;
+ }
+
+ if (unproven_equiv_cells.empty())
+ continue;
+
+ log("Found %d unproven $equiv cells (%d groups) in %s:\n",
+ unproven_cells_counter, GetSize(unproven_equiv_cells), log_id(module));
+
+ for (auto cell : module->cells()) {
+ if (!ct.cell_known(cell->type) && !cell->type.in("$dff", "$_DFF_P_", "$_DFF_N_"))
+ continue;
+ for (auto &conn : cell->connections())
+ if (yosys_celltypes.cell_output(cell->type, conn.first))
+ for (auto bit : sigmap(conn.second))
+ bit2driver[bit] = cell;
+ }
+
+ unproven_equiv_cells.sort();
+ for (auto it : unproven_equiv_cells)
+ {
+ it.second.sort();
+
+ vector<Cell*> cells;
+ for (auto it2 : it.second)
+ cells.push_back(it2.second);
+
+ EquivSimpleWorker worker(cells, sigmap, bit2driver, max_seq, verbose, model_undef);
+ success_counter += worker.run();
+ }
+ }
+
+ log("Proved %d previously unproven $equiv cells.\n", success_counter);
+ }
+} EquivSimplePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/equiv/equiv_status.cc b/passes/equiv/equiv_status.cc
new file mode 100644
index 00000000..7b9230b3
--- /dev/null
+++ b/passes/equiv/equiv_status.cc
@@ -0,0 +1,94 @@
+/*
+ * 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"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct EquivStatusPass : public Pass {
+ EquivStatusPass() : Pass("equiv_status", "print status of equivalent checking module") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" equiv_status [options] [selection]\n");
+ log("\n");
+ log("This command prints status information for all selected $equiv cells.\n");
+ log("\n");
+ log(" -assert\n");
+ log(" produce an error if any unproven $equiv cell is found\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, Design *design)
+ {
+ bool assert_mode = false;
+ int unproven_count = 0;
+
+ log_header(design, "Executing EQUIV_STATUS pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-assert") {
+ assert_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ vector<Cell*> unproven_equiv_cells;
+ int proven_equiv_cells = 0;
+
+ for (auto cell : module->selected_cells())
+ if (cell->type == "$equiv") {
+ if (cell->getPort("\\A") != cell->getPort("\\B"))
+ unproven_equiv_cells.push_back(cell);
+ else
+ proven_equiv_cells++;
+ }
+
+ if (unproven_equiv_cells.empty() && !proven_equiv_cells) {
+ log("No $equiv cells found in %s.\n", log_id(module));
+ continue;
+ }
+
+ log("Found %d $equiv cells in %s:\n", GetSize(unproven_equiv_cells) + proven_equiv_cells, log_id(module));
+ log(" Of those cells %d are proven and %d are unproven.\n", proven_equiv_cells, GetSize(unproven_equiv_cells));
+ if (unproven_equiv_cells.empty()) {
+ log(" Equivalence successfully proven!\n");
+ } else {
+ for (auto cell : unproven_equiv_cells)
+ log(" Unproven $equiv %s: %s %s\n", log_id(cell), log_signal(cell->getPort("\\A")), log_signal(cell->getPort("\\B")));
+ }
+
+ unproven_count += GetSize(unproven_equiv_cells);
+ }
+
+ if (unproven_count != 0) {
+ log("Found a total of %d unproven $equiv cells.\n", unproven_count);
+ if (assert_mode && unproven_count != 0)
+ log_error("Found %d unproven $equiv cells in 'equiv_status -assert'.\n", unproven_count);
+ }
+ }
+} EquivStatusPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/equiv/equiv_struct.cc b/passes/equiv/equiv_struct.cc
new file mode 100644
index 00000000..c4ced6a7
--- /dev/null
+++ b/passes/equiv/equiv_struct.cc
@@ -0,0 +1,367 @@
+/*
+ * 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 EquivStructWorker
+{
+ Module *module;
+ SigMap sigmap;
+ SigMap equiv_bits;
+ bool mode_fwd;
+ bool mode_icells;
+ int merge_count;
+
+ const pool<IdString> &fwonly_cells;
+
+ struct merge_key_t
+ {
+ IdString type;
+ vector<pair<IdString, Const>> parameters;
+ vector<pair<IdString, int>> port_sizes;
+ vector<tuple<IdString, int, SigBit>> connections;
+
+ bool operator==(const merge_key_t &other) const {
+ return type == other.type && connections == other.connections &&
+ parameters == other.parameters && port_sizes == other.port_sizes;
+ }
+
+ unsigned int hash() const {
+ unsigned int h = mkhash_init;
+ h = mkhash(h, mkhash(type));
+ h = mkhash(h, mkhash(parameters));
+ h = mkhash(h, mkhash(connections));
+ return h;
+ }
+ };
+
+ dict<merge_key_t, pool<IdString>> merge_cache;
+ pool<merge_key_t> fwd_merge_cache, bwd_merge_cache;
+
+ void merge_cell_pair(Cell *cell_a, Cell *cell_b)
+ {
+ SigMap merged_map;
+ merge_count++;
+
+ SigSpec inputs_a, inputs_b;
+ vector<string> input_names;
+
+ for (auto &port_a : cell_a->connections())
+ {
+ SigSpec bits_a = sigmap(port_a.second);
+ SigSpec bits_b = sigmap(cell_b->getPort(port_a.first));
+
+ log_assert(GetSize(bits_a) == GetSize(bits_b));
+
+ if (!cell_a->output(port_a.first))
+ for (int i = 0; i < GetSize(bits_a); i++)
+ if (bits_a[i] != bits_b[i]) {
+ inputs_a.append(bits_a[i]);
+ inputs_b.append(bits_b[i]);
+ input_names.push_back(GetSize(bits_a) == 1 ? port_a.first.str() :
+ stringf("%s[%d]", log_id(port_a.first), i));
+ }
+ }
+
+ for (int i = 0; i < GetSize(inputs_a); i++) {
+ SigBit bit_a = inputs_a[i], bit_b = inputs_b[i];
+ SigBit bit_y = module->addWire(NEW_ID);
+ log(" New $equiv for input %s: A: %s, B: %s, Y: %s\n",
+ input_names[i].c_str(), log_signal(bit_a), log_signal(bit_b), log_signal(bit_y));
+ module->addEquiv(NEW_ID, bit_a, bit_b, bit_y);
+ merged_map.add(bit_a, bit_y);
+ merged_map.add(bit_b, bit_y);
+ }
+
+ std::vector<IdString> outport_names, inport_names;
+
+ for (auto &port_a : cell_a->connections())
+ if (cell_a->output(port_a.first))
+ outport_names.push_back(port_a.first);
+ else
+ inport_names.push_back(port_a.first);
+
+ for (auto &pn : inport_names)
+ cell_a->setPort(pn, merged_map(sigmap(cell_a->getPort(pn))));
+
+ for (auto &pn : outport_names) {
+ SigSpec sig_a = cell_a->getPort(pn);
+ SigSpec sig_b = cell_b->getPort(pn);
+ module->connect(sig_b, sig_a);
+ }
+
+ auto merged_attr = cell_b->get_strpool_attribute("\\equiv_merged");
+ merged_attr.insert(log_id(cell_b));
+ cell_a->add_strpool_attribute("\\equiv_merged", merged_attr);
+ module->remove(cell_b);
+ }
+
+ EquivStructWorker(Module *module, bool mode_fwd, bool mode_icells, const pool<IdString> &fwonly_cells, int iter_num) :
+ module(module), sigmap(module), equiv_bits(module),
+ mode_fwd(mode_fwd), mode_icells(mode_icells), merge_count(0), fwonly_cells(fwonly_cells)
+ {
+ log(" Starting iteration %d.\n", iter_num);
+
+ pool<SigBit> equiv_inputs;
+ pool<IdString> cells;
+
+ for (auto cell : module->selected_cells())
+ if (cell->type == "$equiv") {
+ SigBit sig_a = sigmap(cell->getPort("\\A").as_bit());
+ SigBit sig_b = sigmap(cell->getPort("\\B").as_bit());
+ equiv_bits.add(sig_b, sig_a);
+ equiv_inputs.insert(sig_a);
+ equiv_inputs.insert(sig_b);
+ cells.insert(cell->name);
+ } else {
+ if (mode_icells || module->design->module(cell->type))
+ cells.insert(cell->name);
+ }
+
+ for (auto cell : module->selected_cells())
+ if (cell->type == "$equiv") {
+ SigBit sig_a = sigmap(cell->getPort("\\A").as_bit());
+ SigBit sig_b = sigmap(cell->getPort("\\B").as_bit());
+ SigBit sig_y = sigmap(cell->getPort("\\Y").as_bit());
+ if (sig_a == sig_b && equiv_inputs.count(sig_y)) {
+ log(" Purging redundant $equiv cell %s.\n", log_id(cell));
+ module->connect(sig_y, sig_a);
+ module->remove(cell);
+ merge_count++;
+ }
+ }
+
+ if (merge_count > 0)
+ return;
+
+ for (auto cell_name : cells)
+ {
+ merge_key_t key;
+ vector<tuple<IdString, int, SigBit>> fwd_connections;
+
+ Cell *cell = module->cell(cell_name);
+ key.type = cell->type;
+
+ for (auto &it : cell->parameters)
+ key.parameters.push_back(it);
+ std::sort(key.parameters.begin(), key.parameters.end());
+
+ for (auto &it : cell->connections())
+ key.port_sizes.push_back(make_pair(it.first, GetSize(it.second)));
+ std::sort(key.port_sizes.begin(), key.port_sizes.end());
+
+ for (auto &conn : cell->connections())
+ {
+ if (cell->input(conn.first)) {
+ SigSpec sig = sigmap(conn.second);
+ for (int i = 0; i < GetSize(sig); i++)
+ fwd_connections.push_back(make_tuple(conn.first, i, sig[i]));
+ }
+
+ if (cell->output(conn.first)) {
+ SigSpec sig = equiv_bits(conn.second);
+ for (int i = 0; i < GetSize(sig); i++) {
+ key.connections.clear();
+ key.connections.push_back(make_tuple(conn.first, i, sig[i]));
+
+ if (merge_cache.count(key))
+ bwd_merge_cache.insert(key);
+ merge_cache[key].insert(cell_name);
+ }
+ }
+ }
+
+ std::sort(fwd_connections.begin(), fwd_connections.end());
+ key.connections.swap(fwd_connections);
+
+ if (merge_cache.count(key))
+ fwd_merge_cache.insert(key);
+ merge_cache[key].insert(cell_name);
+ }
+
+ for (int phase = 0; phase < 2; phase++)
+ {
+ auto &queue = phase ? bwd_merge_cache : fwd_merge_cache;
+
+ for (auto &key : queue)
+ {
+ const char *strategy = nullptr;
+ vector<Cell*> gold_cells, gate_cells, other_cells;
+ vector<pair<Cell*, Cell*>> cell_pairs;
+ IdString cells_type;
+
+ for (auto cell_name : merge_cache[key]) {
+ Cell *c = module->cell(cell_name);
+ if (c != nullptr) {
+ string n = cell_name.str();
+ cells_type = c->type;
+ if (GetSize(n) > 5 && n.substr(GetSize(n)-5) == "_gold")
+ gold_cells.push_back(c);
+ else if (GetSize(n) > 5 && n.substr(GetSize(n)-5) == "_gate")
+ gate_cells.push_back(c);
+ else
+ other_cells.push_back(c);
+ }
+ }
+
+ if (phase && fwonly_cells.count(cells_type))
+ continue;
+
+ if (GetSize(gold_cells) > 1 || GetSize(gate_cells) > 1 || GetSize(other_cells) > 1)
+ {
+ strategy = "deduplicate";
+ for (int i = 0; i+1 < GetSize(gold_cells); i += 2)
+ cell_pairs.push_back(make_pair(gold_cells[i], gold_cells[i+1]));
+ for (int i = 0; i+1 < GetSize(gate_cells); i += 2)
+ cell_pairs.push_back(make_pair(gate_cells[i], gate_cells[i+1]));
+ for (int i = 0; i+1 < GetSize(other_cells); i += 2)
+ cell_pairs.push_back(make_pair(other_cells[i], other_cells[i+1]));
+ goto run_strategy;
+ }
+
+ if (GetSize(gold_cells) == 1 && GetSize(gate_cells) == 1)
+ {
+ strategy = "gold-gate-pairs";
+ cell_pairs.push_back(make_pair(gold_cells[0], gate_cells[0]));
+ goto run_strategy;
+ }
+
+ if (GetSize(gold_cells) == 1 && GetSize(other_cells) == 1)
+ {
+ strategy = "gold-guess";
+ cell_pairs.push_back(make_pair(gold_cells[0], other_cells[0]));
+ goto run_strategy;
+ }
+
+ if (GetSize(other_cells) == 1 && GetSize(gate_cells) == 1)
+ {
+ strategy = "gate-guess";
+ cell_pairs.push_back(make_pair(other_cells[0], gate_cells[0]));
+ goto run_strategy;
+ }
+
+ log_assert(GetSize(gold_cells) + GetSize(gate_cells) + GetSize(other_cells) < 2);
+ continue;
+
+ run_strategy:
+ int total_group_size = GetSize(gold_cells) + GetSize(gate_cells) + GetSize(other_cells);
+ log(" %s merging %d %s cells (from group of %d) using strategy %s:\n", phase ? "Bwd" : "Fwd",
+ 2*GetSize(cell_pairs), log_id(cells_type), total_group_size, strategy);
+ for (auto it : cell_pairs) {
+ log(" Merging cells %s and %s.\n", log_id(it.first), log_id(it.second));
+ merge_cell_pair(it.first, it.second);
+ }
+ }
+
+ if (merge_count > 0)
+ return;
+ }
+
+ log(" Nothing to merge.\n");
+ }
+};
+
+struct EquivStructPass : public Pass {
+ EquivStructPass() : Pass("equiv_struct", "structural equivalence checking") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" equiv_struct [options] [selection]\n");
+ log("\n");
+ log("This command adds additional $equiv cells based on the assumption that the\n");
+ log("gold and gate circuit are structurally equivalent. Note that this can introduce\n");
+ log("bad $equiv cells in cases where the netlists are not structurally equivalent,\n");
+ log("for example when analyzing circuits with cells with commutative inputs. This\n");
+ log("command will also de-duplicate gates.\n");
+ log("\n");
+ log(" -fwd\n");
+ log(" by default this command performans forward sweeps until nothing can\n");
+ log(" be merged by forwards sweeps, then backward sweeps until forward\n");
+ log(" sweeps are effective again. with this option set only forward sweeps\n");
+ log(" are performed.\n");
+ log("\n");
+ log(" -fwonly <cell_type>\n");
+ log(" add the specified cell type to the list of cell types that are only\n");
+ log(" merged in forward sweeps and never in backward sweeps. $equiv is in\n");
+ log(" this list automatically.\n");
+ log("\n");
+ log(" -icells\n");
+ log(" by default, the internal RTL and gate cell types are ignored. add\n");
+ log(" this option to also process those cell types with this command.\n");
+ log("\n");
+ log(" -maxiter <N>\n");
+ log(" maximum number of iterations to run before aborting\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, Design *design)
+ {
+ pool<IdString> fwonly_cells({ "$equiv" });
+ bool mode_icells = false;
+ bool mode_fwd = false;
+ int max_iter = -1;
+
+ log_header(design, "Executing EQUIV_STRUCT pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-fwd") {
+ mode_fwd = true;
+ continue;
+ }
+ if (args[argidx] == "-icells") {
+ mode_icells = true;
+ continue;
+ }
+ if (args[argidx] == "-fwonly" && argidx+1 < args.size()) {
+ fwonly_cells.insert(RTLIL::escape_id(args[++argidx]));
+ continue;
+ }
+ if (args[argidx] == "-maxiter" && argidx+1 < args.size()) {
+ max_iter = atoi(args[++argidx].c_str());
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules()) {
+ int module_merge_count = 0;
+ log("Running equiv_struct on module %s:\n", log_id(module));
+ for (int iter = 0;; iter++) {
+ if (iter == max_iter) {
+ log(" Reached iteration limit of %d.\n", iter);
+ break;
+ }
+ EquivStructWorker worker(module, mode_fwd, mode_icells, fwonly_cells, iter+1);
+ if (worker.merge_count == 0)
+ break;
+ module_merge_count += worker.merge_count;
+ }
+ if (module_merge_count)
+ log(" Performed a total of %d merges in module %s.\n", module_merge_count, log_id(module));
+ }
+ }
+} EquivStructPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/Makefile.inc b/passes/fsm/Makefile.inc
new file mode 100644
index 00000000..38623e49
--- /dev/null
+++ b/passes/fsm/Makefile.inc
@@ -0,0 +1,11 @@
+
+OBJS += passes/fsm/fsm.o
+OBJS += passes/fsm/fsm_detect.o
+OBJS += passes/fsm/fsm_extract.o
+OBJS += passes/fsm/fsm_opt.o
+OBJS += passes/fsm/fsm_expand.o
+OBJS += passes/fsm/fsm_recode.o
+OBJS += passes/fsm/fsm_info.o
+OBJS += passes/fsm/fsm_export.o
+OBJS += passes/fsm/fsm_map.o
+
diff --git a/passes/fsm/fsm.cc b/passes/fsm/fsm.cc
new file mode 100644
index 00000000..997558b8
--- /dev/null
+++ b/passes/fsm/fsm.cc
@@ -0,0 +1,157 @@
+/*
+ * 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/register.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct FsmPass : public Pass {
+ FsmPass() : Pass("fsm", "extract and optimize finite state machines") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" fsm [options] [selection]\n");
+ log("\n");
+ log("This pass calls all the other fsm_* passes in a useful order. This performs\n");
+ log("FSM extraction and optimization. It also calls opt_clean as needed:\n");
+ log("\n");
+ log(" fsm_detect unless got option -nodetect\n");
+ log(" fsm_extract\n");
+ log("\n");
+ log(" fsm_opt\n");
+ log(" opt_clean\n");
+ log(" fsm_opt\n");
+ log("\n");
+ log(" fsm_expand if got option -expand\n");
+ log(" opt_clean if got option -expand\n");
+ log(" fsm_opt if got option -expand\n");
+ log("\n");
+ log(" fsm_recode unless got option -norecode\n");
+ log("\n");
+ log(" fsm_info\n");
+ log("\n");
+ log(" fsm_export if got option -export\n");
+ log(" fsm_map unless got option -nomap\n");
+ log("\n");
+ log("Options:\n");
+ log("\n");
+ log(" -expand, -norecode, -export, -nomap\n");
+ log(" enable or disable passes as indicated above\n");
+ log("\n");
+ log(" -fullexpand\n");
+ log(" call expand with -full option\n");
+ log("\n");
+ log(" -encoding type\n");
+ log(" -fm_set_fsm_file file\n");
+ log(" -encfile file\n");
+ log(" passed through to fsm_recode pass\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool flag_nomap = false;
+ bool flag_norecode = false;
+ bool flag_nodetect = false;
+ bool flag_expand = false;
+ bool flag_fullexpand = false;
+ bool flag_export = false;
+ std::string fm_set_fsm_file_opt;
+ std::string encfile_opt;
+ std::string encoding_opt;
+
+ log_header(design, "Executing FSM pass (extract and optimize FSM).\n");
+ log_push();
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-fm_set_fsm_file" && argidx+1 < args.size() && fm_set_fsm_file_opt.empty()) {
+ fm_set_fsm_file_opt = " -fm_set_fsm_file " + args[++argidx];
+ continue;
+ }
+ if (arg == "-encfile" && argidx+1 < args.size() && encfile_opt.empty()) {
+ encfile_opt = " -encfile " + args[++argidx];
+ continue;
+ }
+ if (arg == "-encoding" && argidx+1 < args.size() && encoding_opt.empty()) {
+ encoding_opt = " -encoding " + args[++argidx];
+ continue;
+ }
+ if (arg == "-nodetect") {
+ flag_nodetect = true;
+ continue;
+ }
+ if (arg == "-norecode") {
+ flag_norecode = true;
+ continue;
+ }
+ if (arg == "-nomap") {
+ flag_nomap = true;
+ continue;
+ }
+ if (arg == "-expand") {
+ flag_expand = true;
+ continue;
+ }
+ if (arg == "-fullexpand") {
+ flag_fullexpand = true;
+ continue;
+ }
+ if (arg == "-export") {
+ flag_export = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!flag_nodetect)
+ Pass::call(design, "fsm_detect");
+ Pass::call(design, "fsm_extract");
+
+ Pass::call(design, "fsm_opt");
+ Pass::call(design, "opt_clean");
+ Pass::call(design, "fsm_opt");
+
+ if (flag_expand || flag_fullexpand) {
+ Pass::call(design, flag_fullexpand ? "fsm_expand -full" : "fsm_expand");
+ Pass::call(design, "opt_clean");
+ Pass::call(design, "fsm_opt");
+ }
+
+ if (!flag_norecode)
+ Pass::call(design, "fsm_recode" + fm_set_fsm_file_opt + encfile_opt + encoding_opt);
+ Pass::call(design, "fsm_info");
+
+ if (flag_export)
+ Pass::call(design, "fsm_export");
+
+ if (!flag_nomap)
+ Pass::call(design, "fsm_map");
+
+ log_pop();
+ }
+} FsmPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc
new file mode 100644
index 00000000..6a560f16
--- /dev/null
+++ b/passes/fsm/fsm_detect.cc
@@ -0,0 +1,316 @@
+/*
+ * 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/log.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/consteval.h"
+#include "kernel/celltypes.h"
+#include "fsmdata.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+static RTLIL::Module *module;
+static SigMap assign_map;
+typedef std::pair<RTLIL::Cell*, RTLIL::IdString> sig2driver_entry_t;
+static SigSet<sig2driver_entry_t> sig2driver, sig2user;
+static std::set<RTLIL::Cell*> muxtree_cells;
+static SigPool sig_at_port;
+
+static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool<Cell*> &recursion_monitor)
+{
+ if (sig.is_fully_const() || old_sig == sig) {
+ return true;
+ }
+
+ if (sig_at_port.check_any(assign_map(sig))) {
+ return false;
+ }
+
+ std::set<sig2driver_entry_t> cellport_list;
+ sig2driver.find(sig, cellport_list);
+ for (auto &cellport : cellport_list)
+ {
+ if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux") || cellport.second != "\\Y") {
+ return false;
+ }
+
+ if (recursion_monitor.count(cellport.first)) {
+ log_warning("logic loop in mux tree at signal %s in module %s.\n",
+ log_signal(sig), RTLIL::id2cstr(module->name));
+ return false;
+ }
+
+ recursion_monitor.insert(cellport.first);
+
+ RTLIL::SigSpec sig_a = assign_map(cellport.first->getPort("\\A"));
+ RTLIL::SigSpec sig_b = assign_map(cellport.first->getPort("\\B"));
+
+ if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor)) {
+ recursion_monitor.erase(cellport.first);
+ return false;
+ }
+
+ for (int i = 0; i < sig_b.size(); i += sig_a.size())
+ if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor)) {
+ recursion_monitor.erase(cellport.first);
+ return false;
+ }
+
+ recursion_monitor.erase(cellport.first);
+ muxtree_cells.insert(cellport.first);
+ }
+
+ return true;
+}
+
+static bool check_state_users(RTLIL::SigSpec sig)
+{
+ if (sig_at_port.check_any(assign_map(sig)))
+ return false;
+
+ std::set<sig2driver_entry_t> cellport_list;
+ sig2user.find(sig, cellport_list);
+ for (auto &cellport : cellport_list) {
+ RTLIL::Cell *cell = cellport.first;
+ if (muxtree_cells.count(cell) > 0)
+ continue;
+ if (cell->type == "$logic_not" && assign_map(cell->getPort("\\A")) == sig)
+ continue;
+ if (cellport.second != "\\A" && cellport.second != "\\B")
+ return false;
+ if (!cell->hasPort("\\A") || !cell->hasPort("\\B") || !cell->hasPort("\\Y"))
+ return false;
+ for (auto &port_it : cell->connections())
+ if (port_it.first != "\\A" && port_it.first != "\\B" && port_it.first != "\\Y")
+ return false;
+ if (assign_map(cell->getPort("\\A")) == sig && cell->getPort("\\B").is_fully_const())
+ continue;
+ if (assign_map(cell->getPort("\\B")) == sig && cell->getPort("\\A").is_fully_const())
+ continue;
+ return false;
+ }
+
+ return true;
+}
+
+static void detect_fsm(RTLIL::Wire *wire)
+{
+ bool has_fsm_encoding_attr = wire->attributes.count("\\fsm_encoding") > 0 && wire->attributes.at("\\fsm_encoding").decode_string() != "none";
+ bool has_fsm_encoding_none = wire->attributes.count("\\fsm_encoding") > 0 && wire->attributes.at("\\fsm_encoding").decode_string() == "none";
+ bool has_init_attr = wire->attributes.count("\\init") > 0;
+ bool is_module_port = sig_at_port.check_any(assign_map(RTLIL::SigSpec(wire)));
+ bool looks_like_state_reg = false, looks_like_good_state_reg = false;
+ bool is_self_resetting = false;
+
+ if (has_fsm_encoding_none)
+ return;
+
+ if (wire->width <= 1) {
+ if (has_fsm_encoding_attr) {
+ log_warning("Removing fsm_encoding attribute from 1-bit net: %s.%s\n", log_id(wire->module), log_id(wire));
+ wire->attributes.erase("\\fsm_encoding");
+ }
+ return;
+ }
+
+ std::set<sig2driver_entry_t> cellport_list;
+ sig2driver.find(RTLIL::SigSpec(wire), cellport_list);
+
+ for (auto &cellport : cellport_list)
+ {
+ if ((cellport.first->type != "$dff" && cellport.first->type != "$adff") || cellport.second != "\\Q")
+ continue;
+
+ muxtree_cells.clear();
+ pool<Cell*> recursion_monitor;
+ RTLIL::SigSpec sig_q = assign_map(cellport.first->getPort("\\Q"));
+ RTLIL::SigSpec sig_d = assign_map(cellport.first->getPort("\\D"));
+
+ if (sig_q != assign_map(wire))
+ continue;
+
+ looks_like_state_reg = check_state_mux_tree(sig_q, sig_d, recursion_monitor);
+ looks_like_good_state_reg = check_state_users(sig_q);
+
+ if (!looks_like_state_reg)
+ break;
+
+ ConstEval ce(wire->module);
+
+ std::set<sig2driver_entry_t> cellport_list;
+ sig2user.find(sig_q, cellport_list);
+
+ for (auto &cellport : cellport_list)
+ {
+ RTLIL::Cell *cell = cellport.first;
+ bool set_output = false, clr_output = false;
+
+ if (cell->type == "$ne")
+ set_output = true;
+
+ if (cell->type == "$eq")
+ clr_output = true;
+
+ if (!set_output && !clr_output) {
+ clr_output = true;
+ for (auto &port_it : cell->connections())
+ if (port_it.first != "\\A" || port_it.first != "\\Y")
+ clr_output = false;
+ }
+
+ if (set_output || clr_output) {
+ for (auto &port_it : cell->connections())
+ if (cell->output(port_it.first)) {
+ SigSpec sig = assign_map(port_it.second);
+ Const val(set_output ? State::S1 : State::S0, GetSize(sig));
+ ce.set(sig, val);
+ }
+ }
+ }
+
+ SigSpec sig_y = sig_d, sig_undef;
+ if (ce.eval(sig_y, sig_undef))
+ is_self_resetting = true;
+ }
+
+ if (has_fsm_encoding_attr)
+ {
+ vector<string> warnings;
+
+ if (is_module_port)
+ 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");
+
+ if (has_init_attr)
+ warnings.push_back("Init value on fsm state registers are ignored. Possible simulation-synthesis mismatch!");
+
+ if (!looks_like_state_reg)
+ warnings.push_back("Doesn't look like a proper FSM. Possible simulation-synthesis mismatch!\n");
+
+ if (is_self_resetting)
+ warnings.push_back("FSM seems to be self-resetting. Possible simulation-synthesis mismatch!\n");
+
+ if (!warnings.empty()) {
+ string warnmsg = stringf("Regarding the user-specified fsm_encoding attribute on %s.%s:\n", log_id(wire->module), log_id(wire));
+ for (auto w : warnings) warnmsg += " " + w;
+ log_warning("%s", warnmsg.c_str());
+ } else {
+ log("FSM state register %s.%s already has fsm_encoding attribute.\n", log_id(wire->module), log_id(wire));
+ }
+ }
+ else
+ if (looks_like_state_reg && looks_like_good_state_reg && !has_init_attr && !is_module_port && !is_self_resetting)
+ {
+ log("Found FSM state register %s.%s.\n", log_id(wire->module), log_id(wire));
+ wire->attributes["\\fsm_encoding"] = RTLIL::Const("auto");
+ }
+ else
+ if (looks_like_state_reg)
+ {
+ log("Not marking %s.%s as FSM state register:\n", log_id(wire->module), log_id(wire));
+
+ if (is_module_port)
+ log(" Register is connected to module port.\n");
+
+ if (!looks_like_good_state_reg)
+ log(" Users of register don't seem to benefit from recoding.\n");
+
+ if (has_init_attr)
+ log(" Register has an initialization value.");
+
+ if (is_self_resetting)
+ log(" Circuit seems to be self-resetting.\n");
+ }
+}
+
+struct FsmDetectPass : public Pass {
+ FsmDetectPass() : Pass("fsm_detect", "finding FSMs in design") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" fsm_detect [selection]\n");
+ log("\n");
+ log("This pass detects finite state machines by identifying the state signal.\n");
+ log("The state signal is then marked by setting the attribute 'fsm_encoding'\n");
+ log("on the state signal to \"auto\".\n");
+ log("\n");
+ log("Existing 'fsm_encoding' attributes are not changed by this pass.\n");
+ log("\n");
+ log("Signals can be protected from being detected by this pass by setting the\n");
+ log("'fsm_encoding' attribute to \"none\".\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing FSM_DETECT pass (finding FSMs in design).\n");
+ extra_args(args, 1, design);
+
+ CellTypes ct;
+ ct.setup_internals();
+ ct.setup_internals_mem();
+ ct.setup_stdcells();
+ ct.setup_stdcells_mem();
+
+ for (auto &mod_it : design->modules_)
+ {
+ if (!design->selected(mod_it.second))
+ continue;
+
+ module = mod_it.second;
+ assign_map.set(module);
+
+ sig2driver.clear();
+ sig2user.clear();
+ sig_at_port.clear();
+ for (auto &cell_it : module->cells_)
+ for (auto &conn_it : cell_it.second->connections()) {
+ if (ct.cell_output(cell_it.second->type, conn_it.first) || !ct.cell_known(cell_it.second->type)) {
+ RTLIL::SigSpec sig = conn_it.second;
+ assign_map.apply(sig);
+ sig2driver.insert(sig, sig2driver_entry_t(cell_it.second, conn_it.first));
+ }
+ if (!ct.cell_known(cell_it.second->type) || ct.cell_input(cell_it.second->type, conn_it.first)) {
+ RTLIL::SigSpec sig = conn_it.second;
+ assign_map.apply(sig);
+ sig2user.insert(sig, sig2driver_entry_t(cell_it.second, conn_it.first));
+ }
+ }
+
+ for (auto &wire_it : module->wires_)
+ if (wire_it.second->port_id != 0)
+ sig_at_port.add(assign_map(RTLIL::SigSpec(wire_it.second)));
+
+ for (auto &wire_it : module->wires_)
+ if (design->selected(module, wire_it.second))
+ detect_fsm(wire_it.second);
+ }
+
+ assign_map.clear();
+ sig2driver.clear();
+ sig2user.clear();
+ muxtree_cells.clear();
+ }
+} FsmDetectPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsm_expand.cc b/passes/fsm/fsm_expand.cc
new file mode 100644
index 00000000..e7b9dcf9
--- /dev/null
+++ b/passes/fsm/fsm_expand.cc
@@ -0,0 +1,289 @@
+/*
+ * 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/log.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/consteval.h"
+#include "kernel/celltypes.h"
+#include "fsmdata.h"
+#include <string.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct FsmExpand
+{
+ RTLIL::Module *module;
+ RTLIL::Cell *fsm_cell;
+ bool full_mode;
+
+ SigMap assign_map;
+ SigSet<RTLIL::Cell*, RTLIL::sort_by_name_id<RTLIL::Cell>> sig2driver, sig2user;
+ CellTypes ct;
+
+ std::set<RTLIL::Cell*, RTLIL::sort_by_name_id<RTLIL::Cell>> merged_set;
+ std::set<RTLIL::Cell*, RTLIL::sort_by_name_id<RTLIL::Cell>> current_set;
+ std::set<RTLIL::Cell*, RTLIL::sort_by_name_id<RTLIL::Cell>> no_candidate_set;
+
+ bool already_optimized;
+ int limit_transitions;
+
+ bool is_cell_merge_candidate(RTLIL::Cell *cell)
+ {
+ if (full_mode || cell->type == "$_MUX_")
+ return true;
+
+ if (cell->type == "$mux" || cell->type == "$pmux")
+ if (cell->getPort("\\A").size() < 2)
+ return true;
+
+ RTLIL::SigSpec new_signals;
+ if (cell->hasPort("\\A"))
+ new_signals.append(assign_map(cell->getPort("\\A")));
+ if (cell->hasPort("\\B"))
+ new_signals.append(assign_map(cell->getPort("\\B")));
+ if (cell->hasPort("\\S"))
+ new_signals.append(assign_map(cell->getPort("\\S")));
+ if (cell->hasPort("\\Y"))
+ new_signals.append(assign_map(cell->getPort("\\Y")));
+
+ new_signals.sort_and_unify();
+ new_signals.remove_const();
+
+ new_signals.remove(assign_map(fsm_cell->getPort("\\CTRL_IN")));
+ new_signals.remove(assign_map(fsm_cell->getPort("\\CTRL_OUT")));
+
+ if (new_signals.size() > 3)
+ return false;
+
+ return true;
+ }
+
+ void create_current_set()
+ {
+ std::vector<RTLIL::Cell*> cell_list;
+
+ for (auto c : sig2driver.find(assign_map(fsm_cell->getPort("\\CTRL_IN"))))
+ cell_list.push_back(c);
+
+ for (auto c : sig2user.find(assign_map(fsm_cell->getPort("\\CTRL_OUT"))))
+ cell_list.push_back(c);
+
+ current_set.clear();
+ for (auto c : cell_list)
+ {
+ if (merged_set.count(c) > 0 || current_set.count(c) > 0 || no_candidate_set.count(c) > 0)
+ continue;
+ for (auto &p : c->connections()) {
+ if (p.first != "\\A" && p.first != "\\B" && p.first != "\\S" && p.first != "\\Y")
+ goto next_cell;
+ }
+ if (!is_cell_merge_candidate(c)) {
+ no_candidate_set.insert(c);
+ continue;
+ }
+ current_set.insert(c);
+ next_cell:;
+ }
+ }
+
+ void optimze_as_needed()
+ {
+ if (already_optimized)
+ return;
+
+ int trans_num = fsm_cell->parameters["\\TRANS_NUM"].as_int();
+ if (trans_num > limit_transitions)
+ {
+ log(" grown transition table to %d entries -> optimize.\n", trans_num);
+ FsmData::optimize_fsm(fsm_cell, module);
+ already_optimized = true;
+
+ trans_num = fsm_cell->parameters["\\TRANS_NUM"].as_int();
+ log(" transition table size after optimizaton: %d\n", trans_num);
+ limit_transitions = 16 * trans_num;
+ }
+ }
+
+ void merge_cell_into_fsm(RTLIL::Cell *cell)
+ {
+ optimze_as_needed();
+
+ log(" merging %s cell %s.\n", cell->type.c_str(), cell->name.c_str());
+ merged_set.insert(cell);
+ already_optimized = false;
+
+ RTLIL::SigSpec input_sig, output_sig;
+
+ for (auto &p : cell->connections())
+ if (ct.cell_output(cell->type, p.first))
+ output_sig.append(assign_map(p.second));
+ else
+ input_sig.append(assign_map(p.second));
+ input_sig.sort_and_unify();
+ input_sig.remove_const();
+
+ std::vector<RTLIL::Const> truth_tab;
+
+ for (int i = 0; i < (1 << input_sig.size()); i++) {
+ RTLIL::Const in_val(i, input_sig.size());
+ RTLIL::SigSpec A, B, S;
+ if (cell->hasPort("\\A"))
+ A = assign_map(cell->getPort("\\A"));
+ if (cell->hasPort("\\B"))
+ B = assign_map(cell->getPort("\\B"));
+ if (cell->hasPort("\\S"))
+ S = assign_map(cell->getPort("\\S"));
+ A.replace(input_sig, RTLIL::SigSpec(in_val));
+ B.replace(input_sig, RTLIL::SigSpec(in_val));
+ S.replace(input_sig, RTLIL::SigSpec(in_val));
+ log_assert(A.is_fully_const());
+ log_assert(B.is_fully_const());
+ log_assert(S.is_fully_const());
+ truth_tab.push_back(ct.eval(cell, A.as_const(), B.as_const(), S.as_const()));
+ }
+
+ FsmData fsm_data;
+ fsm_data.copy_from_cell(fsm_cell);
+
+ fsm_data.num_inputs += input_sig.size();
+ RTLIL::SigSpec new_ctrl_in = fsm_cell->getPort("\\CTRL_IN");
+ new_ctrl_in.append(input_sig);
+ fsm_cell->setPort("\\CTRL_IN", new_ctrl_in);
+
+ fsm_data.num_outputs += output_sig.size();
+ RTLIL::SigSpec new_ctrl_out = fsm_cell->getPort("\\CTRL_OUT");
+ new_ctrl_out.append(output_sig);
+ fsm_cell->setPort("\\CTRL_OUT", new_ctrl_out);
+
+ std::vector<FsmData::transition_t> new_transition_table;
+ for (auto &tr : fsm_data.transition_table) {
+ for (int i = 0; i < (1 << input_sig.size()); i++) {
+ FsmData::transition_t new_tr = tr;
+ RTLIL::Const in_val(i, input_sig.size());
+ RTLIL::Const out_val = truth_tab[i];
+ RTLIL::SigSpec ctrl_in = new_tr.ctrl_in;
+ RTLIL::SigSpec ctrl_out = new_tr.ctrl_out;
+ ctrl_in.append(in_val);
+ ctrl_out.append(out_val);
+ new_tr.ctrl_in = ctrl_in.as_const();
+ new_tr.ctrl_out = ctrl_out.as_const();
+ new_transition_table.push_back(new_tr);
+ }
+ }
+ fsm_data.transition_table.swap(new_transition_table);
+ new_transition_table.clear();
+
+ fsm_data.copy_to_cell(fsm_cell);
+ }
+
+ FsmExpand(RTLIL::Cell *cell, RTLIL::Design *design, RTLIL::Module *mod, bool full)
+ {
+ module = mod;
+ fsm_cell = cell;
+ full_mode = full;
+
+ assign_map.set(module);
+ ct.setup_internals();
+ ct.setup_stdcells();
+
+ for (auto &cell_it : module->cells_) {
+ RTLIL::Cell *c = cell_it.second;
+ if (ct.cell_known(c->type) && design->selected(mod, c))
+ for (auto &p : c->connections()) {
+ if (ct.cell_output(c->type, p.first))
+ sig2driver.insert(assign_map(p.second), c);
+ else
+ sig2user.insert(assign_map(p.second), c);
+ }
+ }
+ }
+
+ void execute()
+ {
+ log("\n");
+ log("Expanding FSM `%s' from module `%s':\n", fsm_cell->name.c_str(), module->name.c_str());
+
+ already_optimized = false;
+ limit_transitions = 16 * fsm_cell->parameters["\\TRANS_NUM"].as_int();
+
+ for (create_current_set(); current_set.size() > 0; create_current_set()) {
+ for (auto c : current_set)
+ merge_cell_into_fsm(c);
+ }
+
+ for (auto c : merged_set)
+ module->remove(c);
+
+ if (merged_set.size() > 0 && !already_optimized)
+ FsmData::optimize_fsm(fsm_cell, module);
+
+ log(" merged %d cells into FSM.\n", GetSize(merged_set));
+ }
+};
+
+struct FsmExpandPass : public Pass {
+ FsmExpandPass() : Pass("fsm_expand", "expand FSM cells by merging logic into it") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" fsm_expand [-full] [selection]\n");
+ log("\n");
+ log("The fsm_extract pass is conservative about the cells that belong to a finite\n");
+ log("state machine. This pass can be used to merge additional auxiliary gates into\n");
+ log("the finite state machine.\n");
+ log("\n");
+ log("By default, fsm_expand is still a bit conservative regarding merging larger\n");
+ log("word-wide cells. Call with -full to consider all cells for merging.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool full_mode = false;
+
+ log_header(design, "Executing FSM_EXPAND pass (merging auxiliary logic into FSMs).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-full") {
+ full_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto &mod_it : design->modules_) {
+ if (!design->selected(mod_it.second))
+ continue;
+ std::vector<RTLIL::Cell*> fsm_cells;
+ for (auto &cell_it : mod_it.second->cells_)
+ if (cell_it.second->type == "$fsm" && design->selected(mod_it.second, cell_it.second))
+ fsm_cells.push_back(cell_it.second);
+ for (auto c : fsm_cells) {
+ FsmExpand fsm_expand(c, design, mod_it.second, full_mode);
+ fsm_expand.execute();
+ }
+ }
+ }
+} FsmExpandPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsm_export.cc b/passes/fsm/fsm_export.cc
new file mode 100644
index 00000000..1cbfcfae
--- /dev/null
+++ b/passes/fsm/fsm_export.cc
@@ -0,0 +1,189 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Martin Schmölzer <martin@schmoelzer.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/log.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/consteval.h"
+#include "kernel/celltypes.h"
+#include "fsmdata.h"
+#include <string>
+#include <iostream>
+#include <fstream>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+/**
+ * Convert a signal into a KISS-compatible textual representation.
+ */
+std::string kiss_convert_signal(const RTLIL::SigSpec &sig) {
+ log_assert(sig.is_fully_const());
+ return sig.as_const().as_string();
+}
+
+/**
+ * Create a KISS2 file from a cell.
+ *
+ * The destination file name is taken from the fsm_export attribute if present,
+ * e.g. (* fsm_export="filename.kiss2" *). If this attribute is not present,
+ * the file name will be assembled from the module and cell names.
+ *
+ * @param module pointer to module which contains the FSM cell.
+ * @param cell pointer to the FSM cell which should be exported.
+ */
+void write_kiss2(struct RTLIL::Module *module, struct RTLIL::Cell *cell, std::string filename, bool origenc) {
+ dict<RTLIL::IdString, RTLIL::Const>::iterator attr_it;
+ FsmData fsm_data;
+ FsmData::transition_t tr;
+ std::ofstream kiss_file;
+ std::string kiss_name;
+ size_t i;
+
+ attr_it = cell->attributes.find("\\fsm_export");
+ if (!filename.empty()) {
+ kiss_name.assign(filename);
+ } else if (attr_it != cell->attributes.end() && attr_it->second.decode_string() != "") {
+ kiss_name.assign(attr_it->second.decode_string());
+ }
+ else {
+ kiss_name.assign(log_id(module) + std::string("-") + log_id(cell) + ".kiss2");
+ }
+
+ log("\n");
+ log("Exporting FSM `%s' from module `%s' to file `%s'.\n",
+ cell->name.c_str(),
+ module->name.c_str(),
+ kiss_name.c_str());
+
+ kiss_file.open(kiss_name, std::ios::out | std::ios::trunc);
+
+ if (!kiss_file.is_open()) {
+ log_error("Could not open file \"%s\" with write access.\n", kiss_name.c_str());
+ }
+
+ fsm_data.copy_from_cell(cell);
+
+ kiss_file << ".i " << std::dec << fsm_data.num_inputs << std::endl;
+ kiss_file << ".o " << std::dec << fsm_data.num_outputs << std::endl;
+ kiss_file << ".p " << std::dec << fsm_data.transition_table.size() << std::endl;
+ kiss_file << ".s " << std::dec << fsm_data.state_table.size() << std::endl;
+ if (origenc) {
+ kiss_file << ".r " << kiss_convert_signal(fsm_data.state_table[fsm_data.reset_state]) << std::endl;
+ } else {
+ kiss_file << ".r s" << std::dec << fsm_data.reset_state << std::endl;
+ }
+
+ for (i = 0; i < fsm_data.transition_table.size(); i++) {
+ tr = fsm_data.transition_table[i];
+
+ try {
+ kiss_file << kiss_convert_signal(tr.ctrl_in) << ' ';
+ if (origenc) {
+ kiss_file << kiss_convert_signal(fsm_data.state_table[tr.state_in]) << ' ';
+ kiss_file << kiss_convert_signal(fsm_data.state_table[tr.state_out]) << ' ';
+ } else {
+ kiss_file << 's' << tr.state_in << ' ';
+ kiss_file << 's' << tr.state_out << ' ';
+ }
+ kiss_file << kiss_convert_signal(tr.ctrl_out) << std::endl;
+ }
+ catch (int) {
+ kiss_file.close();
+ log_error("exporting an FSM input or output signal failed.\n");
+ }
+ }
+
+ kiss_file.close();
+}
+
+/**
+ * Exports Finite State Machines in the design to one file per FSM. Currently,
+ * only the KISS2 file format is supported.
+ */
+struct FsmExportPass : public Pass {
+ FsmExportPass() : Pass("fsm_export", "exporting FSMs to KISS2 files") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" fsm_export [-noauto] [-o filename] [-origenc] [selection]\n");
+ log("\n");
+ log("This pass creates a KISS2 file for every selected FSM. For FSMs with the\n");
+ log("'fsm_export' attribute set, the attribute value is used as filename, otherwise\n");
+ log("the module and cell name is used as filename. If the parameter '-o' is given,\n");
+ log("the first exported FSM is written to the specified filename. This overwrites\n");
+ log("the setting as specified with the 'fsm_export' attribute. All other FSMs are\n");
+ log("exported to the default name as mentioned above.\n");
+ log("\n");
+ log(" -noauto\n");
+ log(" only export FSMs that have the 'fsm_export' attribute set\n");
+ log("\n");
+ log(" -o filename\n");
+ log(" filename of the first exported FSM\n");
+ log("\n");
+ log(" -origenc\n");
+ log(" use binary state encoding as state names instead of s0, s1, ...\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ dict<RTLIL::IdString, RTLIL::Const>::iterator attr_it;
+ std::string arg;
+ bool flag_noauto = false;
+ std::string filename;
+ bool flag_origenc = false;
+ size_t argidx;
+
+ log_header(design, "Executing FSM_EXPORT pass (exporting FSMs in KISS2 file format).\n");
+
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ arg = args[argidx];
+ if (arg == "-noauto") {
+ flag_noauto = true;
+ continue;
+ }
+ if (arg == "-o") {
+ argidx++;
+ filename = args[argidx];
+ continue;
+ }
+ if (arg == "-origenc") {
+ flag_origenc = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto &mod_it : design->modules_)
+ if (design->selected(mod_it.second))
+ for (auto &cell_it : mod_it.second->cells_)
+ if (cell_it.second->type == "$fsm" && design->selected(mod_it.second, cell_it.second)) {
+ attr_it = cell_it.second->attributes.find("\\fsm_export");
+ if (!flag_noauto || (attr_it != cell_it.second->attributes.end())) {
+ write_kiss2(mod_it.second, cell_it.second, filename, flag_origenc);
+ filename.clear();
+ }
+ }
+ }
+} FsmExportPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsm_extract.cc b/passes/fsm/fsm_extract.cc
new file mode 100644
index 00000000..8a4ee3f2
--- /dev/null
+++ b/passes/fsm/fsm_extract.cc
@@ -0,0 +1,480 @@
+/*
+ * 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.
+ *
+ */
+
+// [[CITE]]
+// Yiqiong Shi; Chan Wai Ting; Bah-Hwee Gwee; Ye Ren, "A highly efficient method for extracting FSMs from flattened gate-level netlist,"
+// Circuits and Systems (ISCAS), Proceedings of 2010 IEEE International Symposium on , vol., no., pp.2610,2613, May 30 2010-June 2 2010
+// doi: 10.1109/ISCAS.2010.5537093
+
+#include "kernel/log.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/consteval.h"
+#include "kernel/celltypes.h"
+#include "fsmdata.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+static RTLIL::Module *module;
+static SigMap assign_map;
+typedef std::pair<RTLIL::IdString, RTLIL::IdString> sig2driver_entry_t;
+static SigSet<sig2driver_entry_t> sig2driver, sig2trigger;
+static std::map<RTLIL::SigBit, std::set<RTLIL::SigBit>> exclusive_ctrls;
+
+static bool find_states(RTLIL::SigSpec sig, const RTLIL::SigSpec &dff_out, RTLIL::SigSpec &ctrl, std::map<RTLIL::Const, int> &states, RTLIL::Const *reset_state = NULL)
+{
+ sig.extend_u0(dff_out.size(), false);
+
+ if (sig == dff_out)
+ return true;
+
+ assign_map.apply(sig);
+ if (sig.is_fully_const()) {
+ if (sig.is_fully_def() && states.count(sig.as_const()) == 0) {
+ log(" found state code: %s\n", log_signal(sig));
+ states[sig.as_const()] = -1;
+ }
+ return true;
+ }
+
+ std::set<sig2driver_entry_t> cellport_list;
+ sig2driver.find(sig, cellport_list);
+
+ if (GetSize(cellport_list) > 1) {
+ log(" found %d combined drivers for state signal %s.\n", GetSize(cellport_list), log_signal(sig));
+ return false;
+ }
+
+ if (GetSize(cellport_list) < 1) {
+ log(" found no driver for state signal %s.\n", log_signal(sig));
+ return false;
+ }
+
+ for (auto &cellport : cellport_list)
+ {
+ RTLIL::Cell *cell = module->cells_.at(cellport.first);
+ if ((cell->type != "$mux" && cell->type != "$pmux") || cellport.second != "\\Y") {
+ log(" unexpected cell type %s (%s) found in state selection tree.\n", cell->type.c_str(), cell->name.c_str());
+ return false;
+ }
+
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
+ RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B"));
+ RTLIL::SigSpec sig_s = assign_map(cell->getPort("\\S"));
+ RTLIL::SigSpec sig_y = assign_map(cell->getPort("\\Y"));
+
+ RTLIL::SigSpec sig_aa = sig;
+ sig_aa.replace(sig_y, sig_a);
+
+ RTLIL::SigSpec sig_bb;
+ for (int i = 0; i < GetSize(sig_b)/GetSize(sig_a); i++) {
+ RTLIL::SigSpec s = sig;
+ s.replace(sig_y, sig_b.extract(i*GetSize(sig_a), GetSize(sig_a)));
+ sig_bb.append(s);
+ }
+
+ if (reset_state && RTLIL::SigSpec(*reset_state).is_fully_undef())
+ do {
+ SigSpec new_reset_state;
+ if (sig_aa.is_fully_def())
+ new_reset_state = sig_aa.as_const();
+ else if (sig_bb.is_fully_def())
+ new_reset_state = sig_bb.as_const();
+ else
+ break;
+ new_reset_state.extend_u0(GetSize(*reset_state));
+ *reset_state = new_reset_state.as_const();
+ log(" found reset state: %s (guessed from mux tree)\n", log_signal(*reset_state));
+ } while (0);
+
+ for (auto sig_s_bit : sig_s) {
+ if (ctrl.extract(sig_s_bit).empty()) {
+ log(" found ctrl input: %s\n", log_signal(sig_s_bit));
+ ctrl.append(sig_s_bit);
+ }
+ }
+
+ if (!find_states(sig_aa, dff_out, ctrl, states))
+ return false;
+
+ for (int i = 0; i < GetSize(sig_bb)/GetSize(sig_aa); i++) {
+ if (!find_states(sig_bb.extract(i*GetSize(sig_aa), GetSize(sig_aa)), dff_out, ctrl, states))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static RTLIL::Const sig2const(ConstEval &ce, RTLIL::SigSpec sig, RTLIL::State noconst_state, RTLIL::SigSpec dont_care = RTLIL::SigSpec())
+{
+ if (dont_care.size() > 0) {
+ for (int i = 0; i < GetSize(sig); i++)
+ if (dont_care.extract(sig[i]).size() > 0)
+ sig[i] = noconst_state;
+ }
+
+ ce.assign_map.apply(sig);
+ ce.values_map.apply(sig);
+
+ for (int i = 0; i < GetSize(sig); i++)
+ if (sig[i].wire != NULL)
+ sig[i] = noconst_state;
+
+ return sig.as_const();
+}
+
+static void find_transitions(ConstEval &ce, ConstEval &ce_nostop, FsmData &fsm_data, std::map<RTLIL::Const, int> &states, int state_in, RTLIL::SigSpec ctrl_in, RTLIL::SigSpec ctrl_out, RTLIL::SigSpec dff_in, RTLIL::SigSpec dont_care)
+{
+ bool undef_bit_in_next_state_mode = false;
+ RTLIL::SigSpec undef, constval;
+
+ if (ce.eval(ctrl_out, undef) && ce.eval(dff_in, undef))
+ {
+ if (0) {
+undef_bit_in_next_state:
+ for (auto &bit : dff_in)
+ if (bit.wire != nullptr) bit = RTLIL::Sm;
+ for (auto &bit : ctrl_out)
+ if (bit.wire != nullptr) bit = RTLIL::Sm;
+ undef_bit_in_next_state_mode = true;
+ }
+
+ log_assert(ctrl_out.is_fully_const() && dff_in.is_fully_const());
+
+ FsmData::transition_t tr;
+ tr.ctrl_in = sig2const(ce, ctrl_in, RTLIL::State::Sa, dont_care);
+ tr.ctrl_out = sig2const(ce, ctrl_out, RTLIL::State::Sx);
+
+ std::map<RTLIL::SigBit, int> ctrl_in_bit_indices;
+ for (int i = 0; i < GetSize(ctrl_in); i++)
+ ctrl_in_bit_indices[ctrl_in[i]] = i;
+
+ for (auto &it : ctrl_in_bit_indices)
+ if (tr.ctrl_in.bits.at(it.second) == RTLIL::S1 && exclusive_ctrls.count(it.first) != 0)
+ for (auto &dc_bit : exclusive_ctrls.at(it.first))
+ if (ctrl_in_bit_indices.count(dc_bit))
+ tr.ctrl_in.bits.at(ctrl_in_bit_indices.at(dc_bit)) = RTLIL::State::Sa;
+
+ RTLIL::Const log_state_in = RTLIL::Const(RTLIL::State::Sx, fsm_data.state_bits);
+ if (state_in >= 0)
+ 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_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" : "");
+ return;
+ }
+
+ tr.state_in = state_in;
+ tr.state_out = states.at(ce.values_map(ce.assign_map(dff_in)).as_const());
+
+ if (dff_in.is_fully_def()) {
+ fsm_data.transition_table.push_back(tr);
+ log(" transition: %10s %s -> %10s %s\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));
+ } else {
+ log(" transition: %10s %s -> %10s %s <ignored undef transistion!>\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));
+ }
+ return;
+ }
+
+ for (auto &bit : dff_in)
+ if (bit == RTLIL::Sx)
+ goto undef_bit_in_next_state;
+
+ log_assert(undef.size() > 0);
+ log_assert(ce.stop_signals.check_all(undef));
+
+ undef = undef.extract(0, 1);
+ constval = undef;
+
+ if (ce_nostop.eval(constval))
+ {
+ ce.push();
+ dont_care.append(undef);
+ ce.set(undef, constval.as_const());
+ if (exclusive_ctrls.count(undef) && constval == RTLIL::S1)
+ for (auto &bit : exclusive_ctrls.at(undef)) {
+ RTLIL::SigSpec bitval = bit;
+ if (ce.eval(bitval) && bitval != RTLIL::S0)
+ goto found_contradiction_1;
+ else
+ ce.set(bit, RTLIL::S0);
+ }
+ find_transitions(ce, ce_nostop, fsm_data, states, state_in, ctrl_in, ctrl_out, dff_in, dont_care);
+ found_contradiction_1:
+ ce.pop();
+ }
+ else
+ {
+ ce.push(), ce_nostop.push();
+ ce.set(undef, RTLIL::S0);
+ ce_nostop.set(undef, RTLIL::S0);
+ find_transitions(ce, ce_nostop, fsm_data, states, state_in, ctrl_in, ctrl_out, dff_in, dont_care);
+ ce.pop(), ce_nostop.pop();
+
+ ce.push(), ce_nostop.push();
+ ce.set(undef, RTLIL::S1);
+ ce_nostop.set(undef, RTLIL::S1);
+ if (exclusive_ctrls.count(undef))
+ for (auto &bit : exclusive_ctrls.at(undef)) {
+ RTLIL::SigSpec bitval = bit;
+ if ((ce.eval(bitval) || ce_nostop.eval(bitval)) && bitval != RTLIL::S0)
+ goto found_contradiction_2;
+ else
+ ce.set(bit, RTLIL::S0), ce_nostop.set(bit, RTLIL::S0);
+ }
+ find_transitions(ce, ce_nostop, fsm_data, states, state_in, ctrl_in, ctrl_out, dff_in, dont_care);
+ found_contradiction_2:
+ ce.pop(), ce_nostop.pop();
+ }
+}
+
+static void extract_fsm(RTLIL::Wire *wire)
+{
+ log("Extracting FSM `%s' from module `%s'.\n", wire->name.c_str(), module->name.c_str());
+
+ // get input and output signals for state ff
+
+ RTLIL::SigSpec dff_out = assign_map(RTLIL::SigSpec(wire));
+ RTLIL::SigSpec dff_in(RTLIL::State::Sm, wire->width);
+ RTLIL::Const reset_state(RTLIL::State::Sx, wire->width);
+
+ RTLIL::SigSpec clk = RTLIL::S0;
+ RTLIL::SigSpec arst = RTLIL::S0;
+ bool clk_polarity = true;
+ bool arst_polarity = true;
+
+ std::set<sig2driver_entry_t> cellport_list;
+ sig2driver.find(dff_out, cellport_list);
+ for (auto &cellport : cellport_list) {
+ RTLIL::Cell *cell = module->cells_.at(cellport.first);
+ if ((cell->type != "$dff" && cell->type != "$adff") || cellport.second != "\\Q")
+ continue;
+ log(" found %s cell for state register: %s\n", cell->type.c_str(), cell->name.c_str());
+ RTLIL::SigSpec sig_q = assign_map(cell->getPort("\\Q"));
+ RTLIL::SigSpec sig_d = assign_map(cell->getPort("\\D"));
+ clk = cell->getPort("\\CLK");
+ clk_polarity = cell->parameters["\\CLK_POLARITY"].as_bool();
+ if (cell->type == "$adff") {
+ arst = cell->getPort("\\ARST");
+ arst_polarity = cell->parameters["\\ARST_POLARITY"].as_bool();
+ reset_state = cell->parameters["\\ARST_VALUE"];
+ }
+ sig_q.replace(dff_out, sig_d, &dff_in);
+ break;
+ }
+
+ log(" root of input selection tree: %s\n", log_signal(dff_in));
+ if (dff_in.has_marked_bits()) {
+ log(" fsm extraction failed: incomplete input selection tree root.\n");
+ return;
+ }
+
+ // find states and control inputs
+
+ RTLIL::SigSpec ctrl_in;
+ std::map<RTLIL::Const, int> states;
+ if (!arst.is_fully_const()) {
+ log(" found reset state: %s (from async reset)\n", log_signal(reset_state));
+ states[reset_state] = -1;
+ }
+ if (!find_states(dff_in, dff_out, ctrl_in, states, &reset_state)) {
+ log(" fsm extraction failed: state selection tree is not closed.\n");
+ return;
+ }
+ if (GetSize(states) <= 1) {
+ log(" fsm extraction failed: at least two states are required.\n");
+ return;
+ }
+
+ // find control outputs
+ // (add the state signals to the list of control outputs. if everything goes right, this signals
+ // become unused and can then be removed from the fsm control output)
+
+ RTLIL::SigSpec ctrl_out = dff_in;
+ cellport_list.clear();
+ sig2trigger.find(dff_out, cellport_list);
+ for (auto &cellport : cellport_list) {
+ RTLIL::Cell *cell = module->cells_.at(cellport.first);
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
+ RTLIL::SigSpec sig_b;
+ if (cell->hasPort("\\B"))
+ sig_b = assign_map(cell->getPort("\\B"));
+ RTLIL::SigSpec sig_y = assign_map(cell->getPort("\\Y"));
+ if (cellport.second == "\\A" && !sig_b.is_fully_const())
+ continue;
+ if (cellport.second == "\\B" && !sig_a.is_fully_const())
+ continue;
+ log(" found ctrl output: %s\n", log_signal(sig_y));
+ ctrl_out.append(sig_y);
+ }
+ ctrl_in.remove(ctrl_out);
+
+ ctrl_in.sort_and_unify();
+ ctrl_out.sort_and_unify();
+
+ log(" ctrl inputs: %s\n", log_signal(ctrl_in));
+ log(" ctrl outputs: %s\n", log_signal(ctrl_out));
+
+ // Initialize fsm data struct
+
+ FsmData fsm_data;
+ fsm_data.num_inputs = ctrl_in.size();
+ fsm_data.num_outputs = ctrl_out.size();
+ fsm_data.state_bits = wire->width;
+ fsm_data.reset_state = -1;
+ for (auto &it : states) {
+ it.second = fsm_data.state_table.size();
+ fsm_data.state_table.push_back(it.first);
+ }
+ if (!arst.is_fully_const() || RTLIL::SigSpec(reset_state).is_fully_def())
+ fsm_data.reset_state = states[reset_state];
+
+ // Create transition table
+
+ ConstEval ce(module), ce_nostop(module);
+ ce.stop(ctrl_in);
+ for (int state_idx = 0; state_idx < int(fsm_data.state_table.size()); state_idx++) {
+ ce.push(), ce_nostop.push();
+ ce.set(dff_out, fsm_data.state_table[state_idx]);
+ ce_nostop.set(dff_out, fsm_data.state_table[state_idx]);
+ find_transitions(ce, ce_nostop, fsm_data, states, state_idx, ctrl_in, ctrl_out, dff_in, RTLIL::SigSpec());
+ ce.pop(), ce_nostop.pop();
+ }
+
+ // create fsm cell
+
+ RTLIL::Cell *fsm_cell = module->addCell(stringf("$fsm$%s$%d", wire->name.c_str(), autoidx++), "$fsm");
+ fsm_cell->setPort("\\CLK", clk);
+ fsm_cell->setPort("\\ARST", arst);
+ fsm_cell->parameters["\\CLK_POLARITY"] = clk_polarity ? RTLIL::S1 : RTLIL::S0;
+ fsm_cell->parameters["\\ARST_POLARITY"] = arst_polarity ? RTLIL::S1 : RTLIL::S0;
+ fsm_cell->setPort("\\CTRL_IN", ctrl_in);
+ fsm_cell->setPort("\\CTRL_OUT", ctrl_out);
+ fsm_cell->parameters["\\NAME"] = RTLIL::Const(wire->name.str());
+ fsm_cell->attributes = wire->attributes;
+ fsm_data.copy_to_cell(fsm_cell);
+
+ // rename original state wire
+
+ module->wires_.erase(wire->name);
+ wire->attributes.erase("\\fsm_encoding");
+ wire->name = stringf("$fsm$oldstate%s", wire->name.c_str());
+ module->wires_[wire->name] = wire;
+
+ // unconnect control outputs from old drivers
+
+ cellport_list.clear();
+ sig2driver.find(ctrl_out, cellport_list);
+ for (auto &cellport : cellport_list) {
+ RTLIL::Cell *cell = module->cells_.at(cellport.first);
+ RTLIL::SigSpec port_sig = assign_map(cell->getPort(cellport.second));
+ RTLIL::SigSpec unconn_sig = port_sig.extract(ctrl_out);
+ RTLIL::Wire *unconn_wire = module->addWire(stringf("$fsm_unconnect$%s$%d", log_signal(unconn_sig), autoidx++), unconn_sig.size());
+ port_sig.replace(unconn_sig, RTLIL::SigSpec(unconn_wire), &cell->connections_[cellport.second]);
+ }
+}
+
+struct FsmExtractPass : public Pass {
+ FsmExtractPass() : Pass("fsm_extract", "extracting FSMs in design") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" fsm_extract [selection]\n");
+ log("\n");
+ log("This pass operates on all signals marked as FSM state signals using the\n");
+ log("'fsm_encoding' attribute. It consumes the logic that creates the state signal\n");
+ log("and uses the state signal to generate control signal and replaces it with an\n");
+ log("FSM cell.\n");
+ log("\n");
+ log("The generated FSM cell still generates the original state signal with its\n");
+ log("original encoding. The 'fsm_opt' pass can be used in combination with the\n");
+ log("'opt_clean' pass to eliminate this signal.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing FSM_EXTRACT pass (extracting FSM from design).\n");
+ extra_args(args, 1, design);
+
+ CellTypes ct;
+ ct.setup_internals();
+ ct.setup_internals_mem();
+ ct.setup_stdcells();
+ ct.setup_stdcells_mem();
+
+ for (auto &mod_it : design->modules_)
+ {
+ if (!design->selected(mod_it.second))
+ continue;
+
+ module = mod_it.second;
+ assign_map.set(module);
+
+ sig2driver.clear();
+ sig2trigger.clear();
+ exclusive_ctrls.clear();
+ for (auto cell : module->cells()) {
+ for (auto &conn_it : cell->connections()) {
+ if (ct.cell_output(cell->type, conn_it.first) || !ct.cell_known(cell->type)) {
+ RTLIL::SigSpec sig = conn_it.second;
+ assign_map.apply(sig);
+ sig2driver.insert(sig, sig2driver_entry_t(cell->name, conn_it.first));
+ }
+ if (ct.cell_input(cell->type, conn_it.first) && cell->hasPort("\\Y") &&
+ cell->getPort("\\Y").size() == 1 && (conn_it.first == "\\A" || conn_it.first == "\\B")) {
+ RTLIL::SigSpec sig = conn_it.second;
+ assign_map.apply(sig);
+ sig2trigger.insert(sig, sig2driver_entry_t(cell->name, conn_it.first));
+ }
+ }
+ if (cell->type == "$pmux") {
+ RTLIL::SigSpec sel_sig = assign_map(cell->getPort("\\S"));
+ for (auto &bit1 : sel_sig)
+ for (auto &bit2 : sel_sig)
+ if (bit1 != bit2)
+ exclusive_ctrls[bit1].insert(bit2);
+ }
+ }
+
+ std::vector<RTLIL::Wire*> wire_list;
+ for (auto &wire_it : module->wires_)
+ if (wire_it.second->attributes.count("\\fsm_encoding") > 0 && wire_it.second->attributes["\\fsm_encoding"].decode_string() != "none")
+ if (design->selected(module, wire_it.second))
+ wire_list.push_back(wire_it.second);
+ for (auto wire : wire_list)
+ extract_fsm(wire);
+ }
+
+ assign_map.clear();
+ sig2driver.clear();
+ sig2trigger.clear();
+ }
+} FsmExtractPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsm_info.cc b/passes/fsm/fsm_info.cc
new file mode 100644
index 00000000..2cc1a7d5
--- /dev/null
+++ b/passes/fsm/fsm_info.cc
@@ -0,0 +1,62 @@
+/*
+ * 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/log.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/consteval.h"
+#include "kernel/celltypes.h"
+#include "fsmdata.h"
+#include <string.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct FsmInfoPass : public Pass {
+ FsmInfoPass() : Pass("fsm_info", "print information on finite state machines") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" fsm_info [selection]\n");
+ log("\n");
+ log("This pass dumps all internal information on FSM cells. It can be useful for\n");
+ log("analyzing the synthesis process and is called automatically by the 'fsm'\n");
+ log("pass so that this information is included in the synthesis log file.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing FSM_INFO pass (dumping all available information on FSM cells).\n");
+ extra_args(args, 1, design);
+
+ for (auto &mod_it : design->modules_)
+ if (design->selected(mod_it.second))
+ for (auto &cell_it : mod_it.second->cells_)
+ if (cell_it.second->type == "$fsm" && design->selected(mod_it.second, cell_it.second)) {
+ log("\n");
+ log("FSM `%s' from module `%s':\n", cell_it.second->name.c_str(), mod_it.first.c_str());
+ FsmData fsm_data;
+ fsm_data.copy_from_cell(cell_it.second);
+ fsm_data.log_info(cell_it.second);
+ }
+ }
+} FsmInfoPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsm_map.cc b/passes/fsm/fsm_map.cc
new file mode 100644
index 00000000..c4230375
--- /dev/null
+++ b/passes/fsm/fsm_map.cc
@@ -0,0 +1,355 @@
+/*
+ * 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/log.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/consteval.h"
+#include "kernel/celltypes.h"
+#include "fsmdata.h"
+#include <string.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+static bool pattern_is_subset(const RTLIL::Const &super_pattern, const RTLIL::Const &sub_pattern)
+{
+ log_assert(GetSize(super_pattern.bits) == GetSize(sub_pattern.bits));
+ for (int i = 0; i < GetSize(super_pattern.bits); i++)
+ if (sub_pattern.bits[i] == RTLIL::State::S0 || sub_pattern.bits[i] == RTLIL::State::S1) {
+ if (super_pattern.bits[i] == RTLIL::State::S0 || super_pattern.bits[i] == RTLIL::State::S1) {
+ if (super_pattern.bits[i] != sub_pattern.bits[i])
+ return false;
+ } else
+ return false;
+ }
+ return true;
+}
+
+static void implement_pattern_cache(RTLIL::Module *module, std::map<RTLIL::Const, std::set<int>> &pattern_cache, std::set<int> &fullstate_cache, int num_states, RTLIL::Wire *state_onehot, RTLIL::SigSpec &ctrl_in, RTLIL::SigSpec output)
+{
+ RTLIL::SigSpec cases_vector;
+
+ for (int in_state : fullstate_cache)
+ cases_vector.append(RTLIL::SigSpec(state_onehot, in_state));
+
+ for (auto &it : pattern_cache)
+ {
+ RTLIL::Const pattern = it.first;
+ RTLIL::SigSpec eq_sig_a, eq_sig_b, or_sig;
+
+ for (size_t j = 0; j < pattern.bits.size(); j++)
+ if (pattern.bits[j] == RTLIL::State::S0 || pattern.bits[j] == RTLIL::State::S1) {
+ eq_sig_a.append(ctrl_in.extract(j, 1));
+ eq_sig_b.append(RTLIL::SigSpec(pattern.bits[j]));
+ }
+
+ for (int in_state : it.second)
+ if (fullstate_cache.count(in_state) == 0)
+ or_sig.append(RTLIL::SigSpec(state_onehot, in_state));
+
+ if (or_sig.size() == 0)
+ continue;
+
+ RTLIL::SigSpec and_sig;
+
+ if (eq_sig_a.size() > 0)
+ {
+ RTLIL::Wire *eq_wire = module->addWire(NEW_ID);
+ and_sig.append(RTLIL::SigSpec(eq_wire));
+
+ RTLIL::Cell *eq_cell = module->addCell(NEW_ID, "$eq");
+ eq_cell->setPort("\\A", eq_sig_a);
+ eq_cell->setPort("\\B", eq_sig_b);
+ eq_cell->setPort("\\Y", RTLIL::SigSpec(eq_wire));
+ eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false);
+ eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(false);
+ eq_cell->parameters["\\A_WIDTH"] = RTLIL::Const(eq_sig_a.size());
+ eq_cell->parameters["\\B_WIDTH"] = RTLIL::Const(eq_sig_b.size());
+ eq_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
+ }
+
+ std::set<int> complete_in_state_cache = it.second;
+
+ for (auto &it2 : pattern_cache)
+ if (pattern_is_subset(pattern, it2.first))
+ complete_in_state_cache.insert(it2.second.begin(), it2.second.end());
+
+ if (GetSize(complete_in_state_cache) < num_states)
+ {
+ if (or_sig.size() == 1)
+ {
+ and_sig.append(or_sig);
+ }
+ else
+ {
+ RTLIL::Wire *or_wire = module->addWire(NEW_ID);
+ and_sig.append(RTLIL::SigSpec(or_wire));
+
+ RTLIL::Cell *or_cell = module->addCell(NEW_ID, "$reduce_or");
+ or_cell->setPort("\\A", or_sig);
+ or_cell->setPort("\\Y", RTLIL::SigSpec(or_wire));
+ or_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false);
+ or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(or_sig.size());
+ or_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
+ }
+ }
+
+ switch (and_sig.size())
+ {
+ case 2:
+ {
+ RTLIL::Wire *and_wire = module->addWire(NEW_ID);
+ cases_vector.append(RTLIL::SigSpec(and_wire));
+
+ RTLIL::Cell *and_cell = module->addCell(NEW_ID, "$and");
+ and_cell->setPort("\\A", and_sig.extract(0, 1));
+ and_cell->setPort("\\B", and_sig.extract(1, 1));
+ and_cell->setPort("\\Y", RTLIL::SigSpec(and_wire));
+ and_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false);
+ and_cell->parameters["\\B_SIGNED"] = RTLIL::Const(false);
+ and_cell->parameters["\\A_WIDTH"] = RTLIL::Const(1);
+ and_cell->parameters["\\B_WIDTH"] = RTLIL::Const(1);
+ and_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
+ break;
+ }
+ case 1:
+ cases_vector.append(and_sig);
+ break;
+ case 0:
+ cases_vector.append(RTLIL::SigSpec(1, 1));
+ break;
+ default:
+ log_abort();
+ }
+ }
+
+ if (cases_vector.size() > 1) {
+ RTLIL::Cell *or_cell = module->addCell(NEW_ID, "$reduce_or");
+ or_cell->setPort("\\A", cases_vector);
+ or_cell->setPort("\\Y", output);
+ or_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false);
+ or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(cases_vector.size());
+ or_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
+ } else if (cases_vector.size() == 1) {
+ module->connect(RTLIL::SigSig(output, cases_vector));
+ } else {
+ module->connect(RTLIL::SigSig(output, RTLIL::SigSpec(0, 1)));
+ }
+}
+
+static void map_fsm(RTLIL::Cell *fsm_cell, RTLIL::Module *module)
+{
+ log("Mapping FSM `%s' from module `%s'.\n", fsm_cell->name.c_str(), module->name.c_str());
+
+ FsmData fsm_data;
+ fsm_data.copy_from_cell(fsm_cell);
+
+ RTLIL::SigSpec ctrl_in = fsm_cell->getPort("\\CTRL_IN");
+ RTLIL::SigSpec ctrl_out = fsm_cell->getPort("\\CTRL_OUT");
+
+ // create state register
+
+ RTLIL::Wire *state_wire = module->addWire(module->uniquify(fsm_cell->parameters["\\NAME"].decode_string()), fsm_data.state_bits);
+ RTLIL::Wire *next_state_wire = module->addWire(NEW_ID, fsm_data.state_bits);
+
+ RTLIL::Cell *state_dff = module->addCell(NEW_ID, "");
+ if (fsm_cell->getPort("\\ARST").is_fully_const()) {
+ state_dff->type = "$dff";
+ } else {
+ state_dff->type = "$adff";
+ state_dff->parameters["\\ARST_POLARITY"] = fsm_cell->parameters["\\ARST_POLARITY"];
+ state_dff->parameters["\\ARST_VALUE"] = fsm_data.state_table[fsm_data.reset_state];
+ for (auto &bit : state_dff->parameters["\\ARST_VALUE"].bits)
+ if (bit != RTLIL::State::S1)
+ bit = RTLIL::State::S0;
+ state_dff->setPort("\\ARST", fsm_cell->getPort("\\ARST"));
+ }
+ state_dff->parameters["\\WIDTH"] = RTLIL::Const(fsm_data.state_bits);
+ state_dff->parameters["\\CLK_POLARITY"] = fsm_cell->parameters["\\CLK_POLARITY"];
+ state_dff->setPort("\\CLK", fsm_cell->getPort("\\CLK"));
+ state_dff->setPort("\\D", RTLIL::SigSpec(next_state_wire));
+ state_dff->setPort("\\Q", RTLIL::SigSpec(state_wire));
+
+ // decode state register
+
+ bool encoding_is_onehot = true;
+
+ RTLIL::Wire *state_onehot = module->addWire(NEW_ID, fsm_data.state_table.size());
+
+ for (size_t i = 0; i < fsm_data.state_table.size(); i++)
+ {
+ RTLIL::Const state = fsm_data.state_table[i];
+ RTLIL::SigSpec sig_a, sig_b;
+
+ for (size_t j = 0; j < state.bits.size(); j++)
+ if (state.bits[j] == RTLIL::State::S0 || state.bits[j] == RTLIL::State::S1) {
+ sig_a.append(RTLIL::SigSpec(state_wire, j));
+ sig_b.append(RTLIL::SigSpec(state.bits[j]));
+ }
+
+ if (sig_b == RTLIL::SigSpec(RTLIL::State::S1))
+ {
+ module->connect(RTLIL::SigSig(RTLIL::SigSpec(state_onehot, i), sig_a));
+ }
+ else
+ {
+ encoding_is_onehot = false;
+
+ RTLIL::Cell *eq_cell = module->addCell(NEW_ID, "$eq");
+ eq_cell->setPort("\\A", sig_a);
+ eq_cell->setPort("\\B", sig_b);
+ eq_cell->setPort("\\Y", RTLIL::SigSpec(state_onehot, i));
+ eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false);
+ eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(false);
+ eq_cell->parameters["\\A_WIDTH"] = RTLIL::Const(sig_a.size());
+ eq_cell->parameters["\\B_WIDTH"] = RTLIL::Const(sig_b.size());
+ eq_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
+ }
+ }
+
+ if (encoding_is_onehot)
+ state_wire->set_bool_attribute("\\onehot");
+
+ // generate next_state signal
+
+ if (GetSize(fsm_data.state_table) == 1)
+ {
+ module->connect(next_state_wire, fsm_data.state_table.front());
+ }
+ else
+ {
+ RTLIL::Wire *next_state_onehot = module->addWire(NEW_ID, fsm_data.state_table.size());
+
+ for (size_t i = 0; i < fsm_data.state_table.size(); i++)
+ {
+ std::map<RTLIL::Const, std::set<int>> pattern_cache;
+ std::set<int> fullstate_cache;
+
+ for (size_t j = 0; j < fsm_data.state_table.size(); j++)
+ fullstate_cache.insert(j);
+
+ for (auto &tr : fsm_data.transition_table) {
+ if (tr.state_out == int(i))
+ pattern_cache[tr.ctrl_in].insert(tr.state_in);
+ else
+ fullstate_cache.erase(tr.state_in);
+ }
+
+ implement_pattern_cache(module, pattern_cache, fullstate_cache, fsm_data.state_table.size(), state_onehot, ctrl_in, RTLIL::SigSpec(next_state_onehot, i));
+ }
+
+ if (encoding_is_onehot)
+ {
+ RTLIL::SigSpec next_state_sig(RTLIL::State::Sm, next_state_wire->width);
+ for (size_t i = 0; i < fsm_data.state_table.size(); i++) {
+ RTLIL::Const state = fsm_data.state_table[i];
+ int bit_idx = -1;
+ for (size_t j = 0; j < state.bits.size(); j++)
+ if (state.bits[j] == RTLIL::State::S1)
+ bit_idx = j;
+ if (bit_idx >= 0)
+ next_state_sig.replace(bit_idx, RTLIL::SigSpec(next_state_onehot, i));
+ }
+ log_assert(!next_state_sig.has_marked_bits());
+ module->connect(RTLIL::SigSig(next_state_wire, next_state_sig));
+ }
+ else
+ {
+ RTLIL::SigSpec sig_a(RTLIL::State::Sx, next_state_wire->width);
+ RTLIL::SigSpec sig_b, sig_s;
+ int reset_state = fsm_data.reset_state;
+ if (reset_state < 0)
+ reset_state = 0;
+
+ for (size_t i = 0; i < fsm_data.state_table.size(); i++) {
+ RTLIL::Const state = fsm_data.state_table[i];
+ if (int(i) == fsm_data.reset_state) {
+ sig_a = RTLIL::SigSpec(state);
+ } else {
+ sig_b.append(RTLIL::SigSpec(state));
+ sig_s.append(RTLIL::SigSpec(next_state_onehot, i));
+ }
+ }
+
+ RTLIL::Cell *mux_cell = module->addCell(NEW_ID, "$pmux");
+ mux_cell->setPort("\\A", sig_a);
+ mux_cell->setPort("\\B", sig_b);
+ mux_cell->setPort("\\S", sig_s);
+ mux_cell->setPort("\\Y", RTLIL::SigSpec(next_state_wire));
+ mux_cell->parameters["\\WIDTH"] = RTLIL::Const(sig_a.size());
+ mux_cell->parameters["\\S_WIDTH"] = RTLIL::Const(sig_s.size());
+ }
+ }
+
+ // Generate ctrl_out signal
+
+ for (int i = 0; i < fsm_data.num_outputs; i++)
+ {
+ std::map<RTLIL::Const, std::set<int>> pattern_cache;
+ std::set<int> fullstate_cache;
+
+ for (size_t j = 0; j < fsm_data.state_table.size(); j++)
+ fullstate_cache.insert(j);
+
+ for (auto &tr : fsm_data.transition_table) {
+ if (tr.ctrl_out.bits[i] == RTLIL::State::S1)
+ pattern_cache[tr.ctrl_in].insert(tr.state_in);
+ else
+ fullstate_cache.erase(tr.state_in);
+ }
+
+ implement_pattern_cache(module, pattern_cache, fullstate_cache, fsm_data.state_table.size(), state_onehot, ctrl_in, ctrl_out.extract(i, 1));
+ }
+
+ // Remove FSM cell
+
+ module->remove(fsm_cell);
+}
+
+struct FsmMapPass : public Pass {
+ FsmMapPass() : Pass("fsm_map", "mapping FSMs to basic logic") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" fsm_map [selection]\n");
+ log("\n");
+ log("This pass translates FSM cells to flip-flops and logic.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing FSM_MAP pass (mapping FSMs to basic logic).\n");
+ extra_args(args, 1, design);
+
+ for (auto &mod_it : design->modules_) {
+ if (!design->selected(mod_it.second))
+ continue;
+ std::vector<RTLIL::Cell*> fsm_cells;
+ for (auto &cell_it : mod_it.second->cells_)
+ if (cell_it.second->type == "$fsm" && design->selected(mod_it.second, cell_it.second))
+ fsm_cells.push_back(cell_it.second);
+ for (auto cell : fsm_cells)
+ map_fsm(cell, mod_it.second);
+ }
+ }
+} FsmMapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsm_opt.cc b/passes/fsm/fsm_opt.cc
new file mode 100644
index 00000000..5b1da44f
--- /dev/null
+++ b/passes/fsm/fsm_opt.cc
@@ -0,0 +1,351 @@
+/*
+ * 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/log.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/consteval.h"
+#include "kernel/celltypes.h"
+#include "fsmdata.h"
+#include <string.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct FsmOpt
+{
+ FsmData fsm_data;
+ RTLIL::Cell *cell;
+ RTLIL::Module *module;
+
+ void opt_unreachable_states()
+ {
+ while (1)
+ {
+ std::set<int> unreachable_states;
+ std::vector<FsmData::transition_t> new_transition_table;
+ std::vector<RTLIL::Const> new_state_table;
+ std::map<int, int> old_to_new_state;
+
+ for (int i = 0; i < GetSize(fsm_data.state_table); i++)
+ if (i != fsm_data.reset_state)
+ unreachable_states.insert(i);
+
+ for (auto &trans : fsm_data.transition_table)
+ unreachable_states.erase(trans.state_out);
+
+ if (unreachable_states.empty())
+ break;
+
+ for (int i = 0; i < GetSize(fsm_data.state_table); i++) {
+ if (unreachable_states.count(i)) {
+ log(" Removing unreachable state %s.\n", log_signal(fsm_data.state_table[i]));
+ continue;
+ }
+ old_to_new_state[i] = GetSize(new_state_table);
+ new_state_table.push_back(fsm_data.state_table[i]);
+ }
+
+ for (auto trans : fsm_data.transition_table) {
+ if (unreachable_states.count(trans.state_in))
+ continue;
+ trans.state_in = old_to_new_state.at(trans.state_in);
+ trans.state_out = old_to_new_state.at(trans.state_out);
+ new_transition_table.push_back(trans);
+ }
+
+ 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);
+ }
+ }
+
+ bool signal_is_unused(RTLIL::SigSpec sig)
+ {
+ RTLIL::SigBit bit = sig.as_bit();
+
+ if (bit.wire == NULL || bit.wire->attributes.count("\\unused_bits") == 0)
+ return false;
+
+ char *str = strdup(bit.wire->attributes["\\unused_bits"].decode_string().c_str());
+ for (char *tok = strtok(str, " "); tok != NULL; tok = strtok(NULL, " ")) {
+ if (tok[0] && bit.offset == atoi(tok)) {
+ free(str);
+ return true;
+ }
+ }
+ free(str);
+
+ return false;
+ }
+
+ void opt_const_and_unused_inputs()
+ {
+ RTLIL::SigSpec ctrl_in = cell->getPort("\\CTRL_IN");
+ std::vector<bool> ctrl_in_used(ctrl_in.size());
+
+ std::vector<FsmData::transition_t> new_transition_table;
+ for (auto &tr : fsm_data.transition_table) {
+ for (int i = 0; i < ctrl_in.size(); i++) {
+ RTLIL::SigSpec ctrl_bit = ctrl_in.extract(i, 1);
+ if (ctrl_bit.is_fully_const()) {
+ if (tr.ctrl_in.bits[i] <= RTLIL::State::S1 && RTLIL::SigSpec(tr.ctrl_in.bits[i]) != ctrl_bit)
+ goto delete_this_transition;
+ continue;
+ }
+ if (tr.ctrl_in.bits[i] <= RTLIL::State::S1)
+ ctrl_in_used[i] = true;
+ }
+ new_transition_table.push_back(tr);
+ delete_this_transition:;
+ }
+
+ for (int i = int(ctrl_in_used.size())-1; i >= 0; i--) {
+ if (!ctrl_in_used[i]) {
+ log(" Removing unused input signal %s.\n", log_signal(cell->getPort("\\CTRL_IN").extract(i, 1)));
+ for (auto &tr : new_transition_table) {
+ RTLIL::SigSpec tmp(tr.ctrl_in);
+ tmp.remove(i, 1);
+ tr.ctrl_in = tmp.as_const();
+ }
+ RTLIL::SigSpec new_ctrl_in = cell->getPort("\\CTRL_IN");
+ new_ctrl_in.remove(i, 1);
+ cell->setPort("\\CTRL_IN", new_ctrl_in);
+ fsm_data.num_inputs--;
+ }
+ }
+
+ fsm_data.transition_table.swap(new_transition_table);
+ new_transition_table.clear();
+ }
+
+ void opt_unused_outputs()
+ {
+ for (int i = 0; i < fsm_data.num_outputs; i++) {
+ RTLIL::SigSpec sig = cell->getPort("\\CTRL_OUT").extract(i, 1);
+ if (signal_is_unused(sig)) {
+ log(" Removing unused output signal %s.\n", log_signal(sig));
+ RTLIL::SigSpec new_ctrl_out = cell->getPort("\\CTRL_OUT");
+ new_ctrl_out.remove(i, 1);
+ cell->setPort("\\CTRL_OUT", new_ctrl_out);
+ for (auto &tr : fsm_data.transition_table) {
+ RTLIL::SigSpec tmp(tr.ctrl_out);
+ tmp.remove(i, 1);
+ tr.ctrl_out = tmp.as_const();
+ }
+ fsm_data.num_outputs--;
+ i--;
+ }
+ }
+ }
+
+ void opt_alias_inputs()
+ {
+ RTLIL::SigSpec &ctrl_in = cell->connections_["\\CTRL_IN"];
+
+ for (int i = 0; i < ctrl_in.size(); i++)
+ for (int j = i+1; j < ctrl_in.size(); j++)
+ if (ctrl_in.extract(i, 1) == ctrl_in.extract(j, 1))
+ {
+ log(" Optimize handling of signal %s that is connected to inputs %d and %d.\n", log_signal(ctrl_in.extract(i, 1)), i, j);
+ std::vector<FsmData::transition_t> new_transition_table;
+
+ for (auto tr : fsm_data.transition_table)
+ {
+ RTLIL::State &si = tr.ctrl_in.bits[i];
+ RTLIL::State &sj = tr.ctrl_in.bits[j];
+
+ if (si > RTLIL::State::S1)
+ si = sj;
+ else if (sj > RTLIL::State::S1)
+ sj = si;
+
+ if (si == sj) {
+ RTLIL::SigSpec tmp(tr.ctrl_in);
+ tmp.remove(j, 1);
+ tr.ctrl_in = tmp.as_const();
+ new_transition_table.push_back(tr);
+ }
+ }
+
+ ctrl_in.remove(j--, 1);
+ fsm_data.num_inputs--;
+
+ fsm_data.transition_table.swap(new_transition_table);
+ new_transition_table.clear();
+ }
+ }
+
+ void opt_feedback_inputs()
+ {
+ RTLIL::SigSpec &ctrl_in = cell->connections_["\\CTRL_IN"];
+ RTLIL::SigSpec &ctrl_out = cell->connections_["\\CTRL_OUT"];
+
+ for (int j = 0; j < ctrl_out.size(); j++)
+ for (int i = 0; i < ctrl_in.size(); i++)
+ if (ctrl_in.extract(i, 1) == ctrl_out.extract(j, 1))
+ {
+ log(" Optimize handling of signal %s that is connected to input %d and output %d.\n", log_signal(ctrl_in.extract(i, 1)), i, j);
+ std::vector<FsmData::transition_t> new_transition_table;
+
+ for (auto tr : fsm_data.transition_table)
+ {
+ RTLIL::State &si = tr.ctrl_in.bits[i];
+ RTLIL::State &sj = tr.ctrl_out.bits[j];
+
+ if (si > RTLIL::State::S1 || si == sj) {
+ RTLIL::SigSpec tmp(tr.ctrl_in);
+ tmp.remove(i, 1);
+ tr.ctrl_in = tmp.as_const();
+ new_transition_table.push_back(tr);
+ }
+ }
+
+ ctrl_in.remove(i--, 1);
+ fsm_data.num_inputs--;
+
+ fsm_data.transition_table.swap(new_transition_table);
+ new_transition_table.clear();
+ }
+ }
+
+ void opt_find_dont_care_worker(std::set<RTLIL::Const> &set, int bit, FsmData::transition_t &tr, bool &did_something)
+ {
+ std::set<RTLIL::Const> new_set;
+
+ for (auto &pattern : set)
+ {
+ if (pattern.bits[bit] > RTLIL::State::S1) {
+ new_set.insert(pattern);
+ continue;
+ }
+
+ RTLIL::Const other_pattern = pattern;
+
+ if (pattern.bits[bit] == RTLIL::State::S1)
+ other_pattern.bits[bit] = RTLIL::State::S0;
+ else
+ other_pattern.bits[bit] = RTLIL::State::S1;
+
+ if (set.count(other_pattern) > 0) {
+ log(" Merging pattern %s and %s from group (%d %d %s).\n", log_signal(pattern), log_signal(other_pattern),
+ tr.state_in, tr.state_out, log_signal(tr.ctrl_out));
+ other_pattern.bits[bit] = RTLIL::State::Sa;
+ new_set.insert(other_pattern);
+ did_something = true;
+ continue;
+ }
+
+ new_set.insert(pattern);
+ }
+
+ set.swap(new_set);
+ }
+
+ void opt_find_dont_care()
+ {
+ typedef std::pair<std::pair<int, int>, RTLIL::Const> group_t;
+ std::map<group_t, std::set<RTLIL::Const>> transitions_by_group;
+
+ for (auto &tr : fsm_data.transition_table) {
+ group_t group(std::pair<int, int>(tr.state_in, tr.state_out), tr.ctrl_out);
+ transitions_by_group[group].insert(tr.ctrl_in);
+ }
+
+ fsm_data.transition_table.clear();
+ for (auto &it : transitions_by_group)
+ {
+ FsmData::transition_t tr;
+ tr.state_in = it.first.first.first;
+ tr.state_out = it.first.first.second;
+ tr.ctrl_out = it.first.second;
+
+ bool did_something = true;
+ while (did_something) {
+ did_something = false;
+ for (int i = 0; i < fsm_data.num_inputs; i++)
+ opt_find_dont_care_worker(it.second, i, tr, did_something);
+ }
+
+ for (auto &ci : it.second) {
+ tr.ctrl_in = ci;
+ fsm_data.transition_table.push_back(tr);
+ }
+ }
+ }
+
+ FsmOpt(RTLIL::Cell *cell, RTLIL::Module *module)
+ {
+ log("Optimizing FSM `%s' from module `%s'.\n", cell->name.c_str(), module->name.c_str());
+
+ fsm_data.copy_from_cell(cell);
+ this->cell = cell;
+ this->module = module;
+
+ opt_unreachable_states();
+
+ opt_unused_outputs();
+
+ opt_alias_inputs();
+ opt_feedback_inputs();
+ opt_find_dont_care();
+
+ opt_const_and_unused_inputs();
+
+ fsm_data.copy_to_cell(cell);
+ }
+};
+
+PRIVATE_NAMESPACE_END
+
+void YOSYS_NAMESPACE_PREFIX FsmData::optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module)
+{
+ FsmOpt fsmopt(cell, module);
+}
+
+PRIVATE_NAMESPACE_BEGIN
+
+struct FsmOptPass : public Pass {
+ FsmOptPass() : Pass("fsm_opt", "optimize finite state machines") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" fsm_opt [selection]\n");
+ log("\n");
+ log("This pass optimizes FSM cells. It detects which output signals are actually\n");
+ log("not used and removes them from the FSM. This pass is usually used in\n");
+ log("combination with the 'opt_clean' pass (see also 'help fsm').\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing FSM_OPT pass (simple optimizations of FSMs).\n");
+ extra_args(args, 1, design);
+
+ for (auto &mod_it : design->modules_) {
+ if (design->selected(mod_it.second))
+ for (auto &cell_it : mod_it.second->cells_)
+ if (cell_it.second->type == "$fsm" && design->selected(mod_it.second, cell_it.second))
+ FsmData::optimize_fsm(cell_it.second, mod_it.second);
+ }
+ }
+} FsmOptPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsm_recode.cc b/passes/fsm/fsm_recode.cc
new file mode 100644
index 00000000..e1bde728
--- /dev/null
+++ b/passes/fsm/fsm_recode.cc
@@ -0,0 +1,197 @@
+/*
+ * 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/log.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/consteval.h"
+#include "kernel/celltypes.h"
+#include "fsmdata.h"
+#include <math.h>
+#include <string.h>
+#include <errno.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+static void fm_set_fsm_print(RTLIL::Cell *cell, RTLIL::Module *module, FsmData &fsm_data, const char *prefix, FILE *f)
+{
+ std::string name = cell->parameters["\\NAME"].decode_string();
+
+ fprintf(f, "set_fsm_state_vector {");
+ for (int i = fsm_data.state_bits-1; i >= 0; i--)
+ fprintf(f, " %s_reg[%d]", name[0] == '\\' ? name.substr(1).c_str() : name.c_str(), i);
+ fprintf(f, " } -name {%s_%s} {%s:/WORK/%s}\n", prefix, RTLIL::unescape_id(name).c_str(),
+ prefix, RTLIL::unescape_id(module->name).c_str());
+
+ fprintf(f, "set_fsm_encoding {");
+ for (int i = 0; i < GetSize(fsm_data.state_table); i++) {
+ fprintf(f, " s%d=2#", i);
+ for (int j = GetSize(fsm_data.state_table[i].bits)-1; j >= 0; j--)
+ fprintf(f, "%c", fsm_data.state_table[i].bits[j] == RTLIL::State::S1 ? '1' : '0');
+ }
+ fprintf(f, " } -name {%s_%s} {%s:/WORK/%s}\n",
+ prefix, RTLIL::unescape_id(name).c_str(),
+ prefix, RTLIL::unescape_id(module->name).c_str());
+}
+
+static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fsm_file, FILE *encfile, std::string default_encoding)
+{
+ std::string encoding = cell->attributes.count("\\fsm_encoding") ? cell->attributes.at("\\fsm_encoding").decode_string() : "auto";
+
+ log("Recoding FSM `%s' from module `%s' using `%s' encoding:\n", cell->name.c_str(), module->name.c_str(), encoding.c_str());
+
+ if (encoding != "none" && encoding != "user" && encoding != "one-hot" && encoding != "binary" && encoding != "auto") {
+ log(" unknown encoding `%s': using auto instead.\n", encoding.c_str());
+ encoding = "auto";
+ }
+
+ if (encoding == "none" || encoding == "user") {
+ log(" nothing to do for encoding `%s'.\n", encoding.c_str());
+ return;
+ }
+
+ FsmData fsm_data;
+ fsm_data.copy_from_cell(cell);
+
+ if (fm_set_fsm_file != NULL)
+ fm_set_fsm_print(cell, module, fsm_data, "r", fm_set_fsm_file);
+
+ if (encoding == "auto") {
+ if (!default_encoding.empty())
+ encoding = default_encoding;
+ else
+ encoding = GetSize(fsm_data.state_table) < 32 ? "one-hot" : "binary";
+ log(" mapping auto encoding to `%s` for this FSM.\n", encoding.c_str());
+ }
+
+ if (encoding == "one-hot") {
+ fsm_data.state_bits = fsm_data.state_table.size();
+ } else
+ if (encoding == "binary") {
+ int new_num_state_bits = ceil_log2(fsm_data.state_table.size());
+ if (fsm_data.state_bits == new_num_state_bits) {
+ log(" existing encoding is already a packed binary encoding.\n");
+ return;
+ }
+ fsm_data.state_bits = new_num_state_bits;
+ } else
+ log_error("FSM encoding `%s' is not supported!\n", encoding.c_str());
+
+ if (encfile)
+ fprintf(encfile, ".fsm %s %s\n", log_id(module), RTLIL::unescape_id(cell->parameters["\\NAME"].decode_string()).c_str());
+
+ int state_idx_counter = fsm_data.reset_state >= 0 ? 1 : 0;
+ for (int i = 0; i < int(fsm_data.state_table.size()); i++)
+ {
+ int state_idx = fsm_data.reset_state == i ? 0 : state_idx_counter++;
+ RTLIL::Const new_code;
+
+ if (encoding == "one-hot") {
+ new_code = RTLIL::Const(RTLIL::State::Sa, fsm_data.state_bits);
+ new_code.bits[state_idx] = RTLIL::State::S1;
+ } else
+ if (encoding == "binary") {
+ new_code = RTLIL::Const(state_idx, fsm_data.state_bits);
+ } else
+ log_abort();
+
+ log(" %s -> %s\n", fsm_data.state_table[i].as_string().c_str(), new_code.as_string().c_str());
+ if (encfile)
+ fprintf(encfile, ".map %s %s\n", fsm_data.state_table[i].as_string().c_str(), new_code.as_string().c_str());
+ fsm_data.state_table[i] = new_code;
+ }
+
+ if (fm_set_fsm_file != NULL)
+ fm_set_fsm_print(cell, module, fsm_data, "i", fm_set_fsm_file);
+
+ fsm_data.copy_to_cell(cell);
+}
+
+struct FsmRecodePass : public Pass {
+ FsmRecodePass() : Pass("fsm_recode", "recoding finite state machines") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" fsm_recode [options] [selection]\n");
+ log("\n");
+ log("This pass reassign the state encodings for FSM cells. At the moment only\n");
+ log("one-hot encoding and binary encoding is supported.\n");
+
+ log(" -encoding <type>\n");
+ log(" specify the encoding scheme used for FSMs without the\n");
+ log(" 'fsm_encoding' attribute or with the attribute set to `auto'.\n");
+ log("\n");
+ log(" -fm_set_fsm_file <file>\n");
+ log(" generate a file containing the mapping from old to new FSM encoding\n");
+ log(" in form of Synopsys Formality set_fsm_* commands.\n");
+ log("\n");
+ log(" -encfile <file>\n");
+ log(" write the mappings from old to new FSM encoding to a file in the\n");
+ log(" following format:\n");
+ log("\n");
+ log(" .fsm <module_name> <state_signal>\n");
+ log(" .map <old_bitpattern> <new_bitpattern>\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ FILE *fm_set_fsm_file = NULL;
+ FILE *encfile = NULL;
+ std::string default_encoding;
+
+ log_header(design, "Executing FSM_RECODE pass (re-assigning FSM state encoding).\n");
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-fm_set_fsm_file" && argidx+1 < args.size() && fm_set_fsm_file == NULL) {
+ fm_set_fsm_file = fopen(args[++argidx].c_str(), "w");
+ if (fm_set_fsm_file == NULL)
+ log_error("Can't open fm_set_fsm_file `%s' for writing: %s\n", args[argidx].c_str(), strerror(errno));
+ continue;
+ }
+ if (arg == "-encfile" && argidx+1 < args.size() && encfile == NULL) {
+ encfile = fopen(args[++argidx].c_str(), "w");
+ if (encfile == NULL)
+ log_error("Can't open encfile `%s' for writing: %s\n", args[argidx].c_str(), strerror(errno));
+ continue;
+ }
+ if (arg == "-encoding" && argidx+1 < args.size() && default_encoding.empty()) {
+ default_encoding = args[++argidx];
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto &mod_it : design->modules_)
+ if (design->selected(mod_it.second))
+ for (auto &cell_it : mod_it.second->cells_)
+ if (cell_it.second->type == "$fsm" && design->selected(mod_it.second, cell_it.second))
+ fsm_recode(cell_it.second, mod_it.second, fm_set_fsm_file, encfile, default_encoding);
+
+ if (fm_set_fsm_file != NULL)
+ fclose(fm_set_fsm_file);
+ if (encfile != NULL)
+ fclose(encfile);
+ }
+} FsmRecodePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsmdata.h b/passes/fsm/fsmdata.h
new file mode 100644
index 00000000..68222769
--- /dev/null
+++ b/passes/fsm/fsmdata.h
@@ -0,0 +1,178 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef FSMDATA_H
+#define FSMDATA_H
+
+#include "kernel/yosys.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+struct FsmData
+{
+ int num_inputs, num_outputs, state_bits, reset_state;
+ struct transition_t { int state_in, state_out; RTLIL::Const ctrl_in, ctrl_out; };
+ std::vector<transition_t> transition_table;
+ std::vector<RTLIL::Const> state_table;
+
+ void copy_to_cell(RTLIL::Cell *cell)
+ {
+ cell->parameters["\\CTRL_IN_WIDTH"] = RTLIL::Const(num_inputs);
+ cell->parameters["\\CTRL_OUT_WIDTH"] = RTLIL::Const(num_outputs);
+
+ int state_num_log2 = 0;
+ for (int i = state_table.size(); i > 0; i = i >> 1)
+ state_num_log2++;
+ state_num_log2 = max(state_num_log2, 1);
+
+ cell->parameters["\\STATE_BITS"] = RTLIL::Const(state_bits);
+ cell->parameters["\\STATE_NUM"] = RTLIL::Const(state_table.size());
+ cell->parameters["\\STATE_NUM_LOG2"] = RTLIL::Const(state_num_log2);
+ cell->parameters["\\STATE_RST"] = RTLIL::Const(reset_state);
+ cell->parameters["\\STATE_TABLE"] = RTLIL::Const();
+
+ for (int i = 0; i < int(state_table.size()); i++) {
+ std::vector<RTLIL::State> &bits_table = cell->parameters["\\STATE_TABLE"].bits;
+ std::vector<RTLIL::State> &bits_state = state_table[i].bits;
+ bits_table.insert(bits_table.end(), bits_state.begin(), bits_state.end());
+ }
+
+ cell->parameters["\\TRANS_NUM"] = RTLIL::Const(transition_table.size());
+ cell->parameters["\\TRANS_TABLE"] = RTLIL::Const();
+ for (int i = 0; i < int(transition_table.size()); i++)
+ {
+ std::vector<RTLIL::State> &bits_table = cell->parameters["\\TRANS_TABLE"].bits;
+ transition_t &tr = transition_table[i];
+
+ RTLIL::Const const_state_in = RTLIL::Const(tr.state_in, state_num_log2);
+ RTLIL::Const const_state_out = RTLIL::Const(tr.state_out, state_num_log2);
+ std::vector<RTLIL::State> &bits_state_in = const_state_in.bits;
+ std::vector<RTLIL::State> &bits_state_out = const_state_out.bits;
+
+ std::vector<RTLIL::State> &bits_ctrl_in = tr.ctrl_in.bits;
+ std::vector<RTLIL::State> &bits_ctrl_out = tr.ctrl_out.bits;
+
+ // append lsb first
+ bits_table.insert(bits_table.end(), bits_ctrl_out.begin(), bits_ctrl_out.end());
+ bits_table.insert(bits_table.end(), bits_state_out.begin(), bits_state_out.end());
+ bits_table.insert(bits_table.end(), bits_ctrl_in.begin(), bits_ctrl_in.end());
+ bits_table.insert(bits_table.end(), bits_state_in.begin(), bits_state_in.end());
+ }
+ }
+
+ void copy_from_cell(RTLIL::Cell *cell)
+ {
+ num_inputs = cell->parameters["\\CTRL_IN_WIDTH"].as_int();
+ num_outputs = cell->parameters["\\CTRL_OUT_WIDTH"].as_int();
+
+ state_bits = cell->parameters["\\STATE_BITS"].as_int();
+ reset_state = cell->parameters["\\STATE_RST"].as_int();
+
+ int state_num = cell->parameters["\\STATE_NUM"].as_int();
+ int state_num_log2 = cell->parameters["\\STATE_NUM_LOG2"].as_int();
+ int trans_num = cell->parameters["\\TRANS_NUM"].as_int();
+
+ if (reset_state < 0 || reset_state >= state_num)
+ reset_state = -1;
+
+ RTLIL::Const state_table = cell->parameters["\\STATE_TABLE"];
+ RTLIL::Const trans_table = cell->parameters["\\TRANS_TABLE"];
+
+ for (int i = 0; i < state_num; i++) {
+ RTLIL::Const state_code;
+ int off_begin = i*state_bits, off_end = off_begin + state_bits;
+ state_code.bits.insert(state_code.bits.begin(), state_table.bits.begin()+off_begin, state_table.bits.begin()+off_end);
+ this->state_table.push_back(state_code);
+ }
+
+ for (int i = 0; i < trans_num; i++)
+ {
+ auto off_ctrl_out = trans_table.bits.begin() + i*(num_inputs+num_outputs+2*state_num_log2);
+ auto off_state_out = off_ctrl_out + num_outputs;
+ auto off_ctrl_in = off_state_out + state_num_log2;
+ auto off_state_in = off_ctrl_in + num_inputs;
+ auto off_end = off_state_in + state_num_log2;
+
+ RTLIL::Const state_in, state_out, ctrl_in, ctrl_out;
+ ctrl_out.bits.insert(state_in.bits.begin(), off_ctrl_out, off_state_out);
+ state_out.bits.insert(state_out.bits.begin(), off_state_out, off_ctrl_in);
+ ctrl_in.bits.insert(ctrl_in.bits.begin(), off_ctrl_in, off_state_in);
+ state_in.bits.insert(state_in.bits.begin(), off_state_in, off_end);
+
+ transition_t tr;
+ tr.state_in = state_in.as_int();
+ tr.state_out = state_out.as_int();
+ tr.ctrl_in = ctrl_in;
+ tr.ctrl_out = ctrl_out;
+
+ if (tr.state_in < 0 || tr.state_in >= state_num)
+ tr.state_in = -1;
+ if (tr.state_out < 0 || tr.state_out >= state_num)
+ tr.state_out = -1;
+
+ transition_table.push_back(tr);
+ }
+ }
+
+ void log_info(RTLIL::Cell *cell)
+ {
+ log("-------------------------------------\n");
+ log("\n");
+ log(" Information on FSM %s (%s):\n", cell->name.c_str(), cell->parameters["\\NAME"].decode_string().c_str());
+ log("\n");
+ log(" Number of input signals: %3d\n", num_inputs);
+ log(" Number of output signals: %3d\n", num_outputs);
+ log(" Number of state bits: %3d\n", state_bits);
+
+ log("\n");
+ log(" Input signals:\n");
+ RTLIL::SigSpec sig_in = cell->getPort("\\CTRL_IN");
+ for (int i = 0; i < GetSize(sig_in); i++)
+ log(" %3d: %s\n", i, log_signal(sig_in[i]));
+
+ log("\n");
+ log(" Output signals:\n");
+ RTLIL::SigSpec sig_out = cell->getPort("\\CTRL_OUT");
+ for (int i = 0; i < GetSize(sig_out); i++)
+ log(" %3d: %s\n", i, log_signal(sig_out[i]));
+
+ log("\n");
+ log(" State encoding:\n");
+ for (int i = 0; i < GetSize(state_table); i++)
+ log(" %3d: %10s%s\n", i, log_signal(state_table[i], false),
+ int(i) == reset_state ? " <RESET STATE>" : "");
+
+ log("\n");
+ log(" Transition Table (state_in, ctrl_in, state_out, ctrl_out):\n");
+ for (int i = 0; i < GetSize(transition_table); i++) {
+ transition_t &tr = transition_table[i];
+ log(" %5d: %5d %s -> %5d %s\n", i, tr.state_in, log_signal(tr.ctrl_in), tr.state_out, log_signal(tr.ctrl_out));
+ }
+
+ log("\n");
+ log("-------------------------------------\n");
+ }
+
+ // implemented in fsm_opt.cc
+ static void optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module);
+};
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/passes/hierarchy/Makefile.inc b/passes/hierarchy/Makefile.inc
new file mode 100644
index 00000000..1fb669c1
--- /dev/null
+++ b/passes/hierarchy/Makefile.inc
@@ -0,0 +1,5 @@
+
+OBJS += passes/hierarchy/hierarchy.o
+OBJS += passes/hierarchy/singleton.o
+OBJS += passes/hierarchy/submod.o
+
diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc
new file mode 100644
index 00000000..e21a7a4e
--- /dev/null
+++ b/passes/hierarchy/hierarchy.cc
@@ -0,0 +1,621 @@
+/*
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <set>
+
+#ifndef _WIN32
+# include <unistd.h>
+#endif
+
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct generate_port_decl_t {
+ bool input, output;
+ string portname;
+ int index;
+};
+
+void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes, const std::vector<generate_port_decl_t> &portdecls)
+{
+ std::set<RTLIL::IdString> found_celltypes;
+
+ for (auto i1 : design->modules_)
+ for (auto i2 : i1.second->cells_)
+ {
+ RTLIL::Cell *cell = i2.second;
+ if (design->has(cell->type))
+ continue;
+ if (cell->type.substr(0, 1) == "$" && cell->type.substr(0, 3) != "$__")
+ continue;
+ for (auto &pattern : celltypes)
+ if (patmatch(pattern.c_str(), RTLIL::unescape_id(cell->type).c_str()))
+ found_celltypes.insert(cell->type);
+ }
+
+ for (auto &celltype : found_celltypes)
+ {
+ std::set<RTLIL::IdString> portnames;
+ std::set<RTLIL::IdString> parameters;
+ std::map<RTLIL::IdString, int> portwidths;
+ log("Generate module for cell type %s:\n", celltype.c_str());
+
+ for (auto i1 : design->modules_)
+ for (auto i2 : i1.second->cells_)
+ if (i2.second->type == celltype) {
+ for (auto &conn : i2.second->connections()) {
+ if (conn.first[0] != '$')
+ portnames.insert(conn.first);
+ portwidths[conn.first] = max(portwidths[conn.first], conn.second.size());
+ }
+ for (auto &para : i2.second->parameters)
+ parameters.insert(para.first);
+ }
+
+ for (auto &decl : portdecls)
+ if (decl.index > 0)
+ portnames.insert(decl.portname);
+
+ std::set<int> indices;
+ for (int i = 0; i < int(portnames.size()); i++)
+ indices.insert(i+1);
+
+ std::vector<generate_port_decl_t> ports(portnames.size());
+
+ for (auto &decl : portdecls)
+ if (decl.index > 0) {
+ portwidths[decl.portname] = max(portwidths[decl.portname], 1);
+ portwidths[decl.portname] = max(portwidths[decl.portname], portwidths[stringf("$%d", decl.index)]);
+ log(" port %d: %s [%d:0] %s\n", decl.index, decl.input ? decl.output ? "inout" : "input" : "output", portwidths[decl.portname]-1, RTLIL::id2cstr(decl.portname));
+ if (indices.count(decl.index) > ports.size())
+ log_error("Port index (%d) exceeds number of found ports (%d).\n", decl.index, int(ports.size()));
+ if (indices.count(decl.index) == 0)
+ log_error("Conflict on port index %d.\n", decl.index);
+ indices.erase(decl.index);
+ portnames.erase(decl.portname);
+ ports[decl.index-1] = decl;
+ }
+
+ while (portnames.size() > 0) {
+ RTLIL::IdString portname = *portnames.begin();
+ for (auto &decl : portdecls)
+ if (decl.index == 0 && patmatch(decl.portname.c_str(), RTLIL::unescape_id(portname).c_str())) {
+ generate_port_decl_t d = decl;
+ d.portname = portname.str();
+ d.index = *indices.begin();
+ log_assert(!indices.empty());
+ indices.erase(d.index);
+ ports[d.index-1] = d;
+ portwidths[d.portname] = max(portwidths[d.portname], 1);
+ log(" port %d: %s [%d:0] %s\n", d.index, d.input ? d.output ? "inout" : "input" : "output", portwidths[d.portname]-1, RTLIL::id2cstr(d.portname));
+ goto found_matching_decl;
+ }
+ log_error("Can't match port %s.\n", RTLIL::id2cstr(portname));
+ found_matching_decl:;
+ portnames.erase(portname);
+ }
+
+ log_assert(indices.empty());
+
+ RTLIL::Module *mod = new RTLIL::Module;
+ mod->name = celltype;
+ mod->attributes["\\blackbox"] = RTLIL::Const(1);
+ design->add(mod);
+
+ for (auto &decl : ports) {
+ RTLIL::Wire *wire = mod->addWire(decl.portname, portwidths.at(decl.portname));
+ wire->port_id = decl.index;
+ wire->port_input = decl.input;
+ wire->port_output = decl.output;
+ }
+
+ mod->fixup_ports();
+
+ for (auto &para : parameters)
+ log(" ignoring parameter %s.\n", RTLIL::id2cstr(para));
+
+ log(" module %s created.\n", RTLIL::id2cstr(mod->name));
+ }
+}
+
+bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, std::vector<std::string> &libdirs)
+{
+ bool did_something = false;
+ std::map<RTLIL::Cell*, std::pair<int, int>> array_cells;
+ std::string filename;
+
+ for (auto &cell_it : module->cells_)
+ {
+ RTLIL::Cell *cell = cell_it.second;
+
+ 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 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);
+ }
+
+ if (design->modules_.count(cell->type) == 0)
+ {
+ if (design->modules_.count("$abstract" + cell->type.str()))
+ {
+ cell->type = design->modules_.at("$abstract" + cell->type.str())->derive(design, cell->parameters);
+ cell->parameters.clear();
+ did_something = true;
+ continue;
+ }
+
+ if (cell->type[0] == '$')
+ continue;
+
+ for (auto &dir : libdirs)
+ {
+ filename = dir + "/" + RTLIL::unescape_id(cell->type) + ".v";
+ if (check_file_exists(filename)) {
+ std::vector<std::string> args;
+ args.push_back(filename);
+ Frontend::frontend_call(design, NULL, filename, "verilog");
+ goto loaded_module;
+ }
+
+ filename = dir + "/" + RTLIL::unescape_id(cell->type) + ".il";
+ if (check_file_exists(filename)) {
+ std::vector<std::string> args;
+ args.push_back(filename);
+ Frontend::frontend_call(design, NULL, filename, "ilang");
+ goto loaded_module;
+ }
+ }
+
+ if (flag_check && cell->type[0] != '$')
+ log_error("Module `%s' referenced in module `%s' in cell `%s' is not part of the design.\n",
+ cell->type.c_str(), module->name.c_str(), cell->name.c_str());
+ continue;
+
+ loaded_module:
+ 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
+ if (flag_check)
+ {
+ RTLIL::Module *mod = design->module(cell->type);
+ 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))
+ log_error("Module `%s' referenced in module `%s' in cell `%s' has only %d ports, requested port %d.\n",
+ log_id(cell->type), log_id(module), log_id(cell), GetSize(mod->ports), id);
+ } 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] != '$')
+ 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));
+ }
+
+ if (cell->parameters.size() == 0)
+ continue;
+
+ if (design->modules_.at(cell->type)->get_bool_attribute("\\blackbox"))
+ continue;
+
+ RTLIL::Module *mod = design->modules_[cell->type];
+ cell->type = mod->derive(design, cell->parameters);
+ cell->parameters.clear();
+ did_something = true;
+ }
+
+ for (auto &it : array_cells)
+ {
+ RTLIL::Cell *cell = it.first;
+ int idx = it.second.first, num = it.second.second;
+
+ if (design->modules_.count(cell->type) == 0)
+ log_error("Array cell `%s.%s' of unknown type `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
+
+ RTLIL::Module *mod = design->modules_[cell->type];
+
+ for (auto &conn : cell->connections_) {
+ int conn_size = conn.second.size();
+ RTLIL::IdString portname = conn.first;
+ if (portname.substr(0, 1) == "$") {
+ int port_id = atoi(portname.substr(1).c_str());
+ for (auto &wire_it : mod->wires_)
+ if (wire_it.second->port_id == port_id) {
+ portname = wire_it.first;
+ break;
+ }
+ }
+ if (mod->wires_.count(portname) == 0)
+ log_error("Array cell `%s.%s' connects to unknown port `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(conn.first));
+ int port_size = mod->wires_.at(portname)->width;
+ if (conn_size == port_size)
+ continue;
+ if (conn_size != port_size*num)
+ log_error("Array cell `%s.%s' has invalid port vs. signal size for port `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(conn.first));
+ conn.second = conn.second.extract(port_size*idx, port_size);
+ }
+ }
+
+ return did_something;
+}
+
+void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> &used, RTLIL::Module *mod, int indent)
+{
+ if (used.count(mod) > 0)
+ return;
+
+ if (indent == 0)
+ log("Top module: %s\n", mod->name.c_str());
+ else if (!mod->get_bool_attribute("\\blackbox"))
+ log("Used module: %*s%s\n", indent, "", mod->name.c_str());
+ used.insert(mod);
+
+ 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);
+ }
+ if (design->module(celltype))
+ hierarchy_worker(design, used, design->module(celltype), indent+4);
+ }
+}
+
+void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib)
+{
+ std::set<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> used;
+ hierarchy_worker(design, used, top, 0);
+
+ std::vector<RTLIL::Module*> del_modules;
+ for (auto &it : design->modules_)
+ if (used.count(it.second) == 0)
+ del_modules.push_back(it.second);
+
+ int del_counter = 0;
+ for (auto mod : del_modules) {
+ if (!purge_lib && mod->get_bool_attribute("\\blackbox"))
+ continue;
+ log("Removing unused module `%s'.\n", mod->name.c_str());
+ design->modules_.erase(mod->name);
+ del_counter++;
+ delete mod;
+ }
+
+ log("Removed %d unused modules.\n", del_counter);
+}
+
+bool set_keep_assert(std::map<RTLIL::Module*, bool> &cache, RTLIL::Module *mod)
+{
+ if (cache.count(mod) == 0)
+ for (auto c : mod->cells()) {
+ RTLIL::Module *m = mod->design->module(c->type);
+ if ((m != nullptr && set_keep_assert(cache, m)) || c->type.in("$assert", "$assume"))
+ return cache[mod] = true;
+ }
+ return cache[mod];
+}
+
+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);
+ db[module] = score;
+ }
+ return db.at(module);
+}
+
+struct HierarchyPass : public Pass {
+ HierarchyPass() : Pass("hierarchy", "check, expand and clean up design hierarchy") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" hierarchy [-check] [-top <module>]\n");
+ log(" hierarchy -generate <cell-types> <port-decls>\n");
+ log("\n");
+ log("In parametric designs, a module might exists in several variations with\n");
+ log("different parameter values. This pass looks at all modules in the current\n");
+ log("design an re-runs the language frontends for the parametric modules as\n");
+ log("needed.\n");
+ log("\n");
+ log(" -check\n");
+ log(" also check the design hierarchy. this generates an error when\n");
+ log(" an unknown module is used as cell type.\n");
+ log("\n");
+ log(" -purge_lib\n");
+ log(" by default the hierarchy command will not remove library (blackbox)\n");
+ log(" modules. use this option to also remove unused blackbox modules.\n");
+ log("\n");
+ log(" -libdir <directory>\n");
+ log(" search for files named <module_name>.v in the specified directory\n");
+ log(" for unknown modules and automatically run read_verilog for each\n");
+ log(" unknown module.\n");
+ log("\n");
+ log(" -keep_positionals\n");
+ log(" per default this pass also converts positional arguments in cells\n");
+ log(" to arguments using port names. this option disables this behavior.\n");
+ log("\n");
+ log(" -nokeep_asserts\n");
+ log(" per default this pass sets the \"keep\" attribute on all modules\n");
+ log(" that directly or indirectly contain one or more $assert cells. this\n");
+ log(" option disables this behavior.\n");
+ log("\n");
+ log(" -top <module>\n");
+ log(" use the specified top module to built a design hierarchy. modules\n");
+ log(" outside this tree (unused modules) are removed.\n");
+ log("\n");
+ log(" when the -top option is used, the 'top' attribute will be set on the\n");
+ log(" specified top module. otherwise a module with the 'top' attribute set\n");
+ log(" will implicitly be used as top module, if such a module exists.\n");
+ log("\n");
+ log(" -auto-top\n");
+ log(" automatically determine the top of the design hierarchy and mark it.\n");
+ log("\n");
+ log("In -generate mode this pass generates blackbox modules for the given cell\n");
+ log("types (wildcards supported). For this the design is searched for cells that\n");
+ log("match the given types and then the given port declarations are used to\n");
+ log("determine the direction of the ports. The syntax for a port declaration is:\n");
+ log("\n");
+ log(" {i|o|io}[@<num>]:<portname>\n");
+ log("\n");
+ log("Input ports are specified with the 'i' prefix, output ports with the 'o'\n");
+ log("prefix and inout ports with the 'io' prefix. The optional <num> specifies\n");
+ log("the position of the port in the parameter list (needed when instantiated\n");
+ log("using positional arguments). When <num> is not specified, the <portname> can\n");
+ log("also contain wildcard characters.\n");
+ log("\n");
+ log("This pass ignores the current selection and always operates on all modules\n");
+ log("in the current design.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing HIERARCHY pass (managing design hierarchy).\n");
+
+ bool flag_check = false;
+ bool purge_lib = false;
+ RTLIL::Module *top_mod = NULL;
+ std::vector<std::string> libdirs;
+
+ bool auto_top_mode = false;
+ bool generate_mode = false;
+ bool keep_positionals = false;
+ bool nokeep_asserts = false;
+ std::vector<std::string> generate_cells;
+ std::vector<generate_port_decl_t> generate_ports;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-generate" && !flag_check && !top_mod) {
+ generate_mode = true;
+ log("Entering generate mode.\n");
+ while (++argidx < args.size()) {
+ const char *p = args[argidx].c_str();
+ generate_port_decl_t decl;
+ if (p[0] == 'i' && p[1] == 'o')
+ decl.input = true, decl.output = true, p += 2;
+ else if (*p == 'i')
+ decl.input = true, decl.output = false, p++;
+ else if (*p == 'o')
+ decl.input = false, decl.output = true, p++;
+ else
+ goto is_celltype;
+ if (*p == '@') {
+ char *endptr;
+ decl.index = strtol(++p, &endptr, 10);
+ if (decl.index < 1)
+ goto is_celltype;
+ p = endptr;
+ } else
+ decl.index = 0;
+ if (*(p++) != ':')
+ goto is_celltype;
+ if (*p == 0)
+ goto is_celltype;
+ decl.portname = p;
+ log("Port declaration: %s", decl.input ? decl.output ? "inout" : "input" : "output");
+ if (decl.index >= 1)
+ log(" [at position %d]", decl.index);
+ log(" %s\n", decl.portname.c_str());
+ generate_ports.push_back(decl);
+ continue;
+ is_celltype:
+ log("Celltype: %s\n", args[argidx].c_str());
+ generate_cells.push_back(RTLIL::unescape_id(args[argidx]));
+ }
+ continue;
+ }
+ if (args[argidx] == "-check") {
+ flag_check = true;
+ continue;
+ }
+ if (args[argidx] == "-purge_lib") {
+ purge_lib = true;
+ continue;
+ }
+ if (args[argidx] == "-keep_positionals") {
+ keep_positionals = true;
+ continue;
+ }
+ if (args[argidx] == "-nokeep_asserts") {
+ nokeep_asserts = true;
+ continue;
+ }
+ if (args[argidx] == "-libdir" && argidx+1 < args.size()) {
+ libdirs.push_back(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-top") {
+ if (++argidx >= args.size())
+ log_cmd_error("Option -top requires an additional argument!\n");
+ top_mod = design->modules_.count(RTLIL::escape_id(args[argidx])) ? design->modules_.at(RTLIL::escape_id(args[argidx])) : NULL;
+ if (top_mod == NULL && design->modules_.count("$abstract" + RTLIL::escape_id(args[argidx]))) {
+ dict<RTLIL::IdString, RTLIL::Const> empty_parameters;
+ design->modules_.at("$abstract" + RTLIL::escape_id(args[argidx]))->derive(design, empty_parameters);
+ top_mod = design->modules_.count(RTLIL::escape_id(args[argidx])) ? design->modules_.at(RTLIL::escape_id(args[argidx])) : NULL;
+ }
+ if (top_mod == NULL)
+ log_cmd_error("Module `%s' not found!\n", args[argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-auto-top") {
+ auto_top_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design, false);
+
+ if (generate_mode) {
+ generate(design, generate_cells, generate_ports);
+ return;
+ }
+
+ log_push();
+
+ if (top_mod == nullptr)
+ for (auto &mod_it : design->modules_)
+ if (mod_it.second->get_bool_attribute("\\top"))
+ top_mod = mod_it.second;
+
+ if (top_mod == nullptr && auto_top_mode) {
+ log_header(design, "Finding top of design hierarchy..\n");
+ dict<Module*, int> db;
+ for (Module *mod : design->selected_modules()) {
+ int score = find_top_mod_score(design, mod, db);
+ log("root of %3d design levels: %-20s\n", score, log_id(mod));
+ if (!top_mod || score > db[top_mod])
+ top_mod = mod;
+ }
+ if (top_mod != nullptr)
+ log("Automatically selected %s as design top module.\n", log_id(top_mod));
+ }
+
+ bool did_something = true;
+ while (did_something)
+ {
+ did_something = false;
+
+ std::set<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> used_modules;
+ if (top_mod != NULL) {
+ log_header(design, "Analyzing design hierarchy..\n");
+ hierarchy_worker(design, used_modules, top_mod, 0);
+ } else {
+ for (auto mod : design->modules())
+ used_modules.insert(mod);
+ }
+
+ for (auto module : used_modules) {
+ if (expand_module(design, module, flag_check, libdirs))
+ did_something = true;
+ }
+ }
+
+ 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_)
+ if (mod_it.second == top_mod)
+ mod_it.second->attributes["\\top"] = RTLIL::Const(1);
+ else
+ mod_it.second->attributes.erase("\\top");
+ }
+
+ if (!nokeep_asserts) {
+ std::map<RTLIL::Module*, bool> cache;
+ for (auto mod : design->modules())
+ if (set_keep_assert(cache, mod)) {
+ log("Module %s directly or indirectly contains $assert cells -> setting \"keep\" attribute.\n", log_id(mod));
+ mod->set_bool_attribute("\\keep");
+ }
+ }
+
+ if (!keep_positionals)
+ {
+ std::set<RTLIL::Module*> pos_mods;
+ std::map<std::pair<RTLIL::Module*,int>, RTLIL::IdString> pos_map;
+ std::vector<std::pair<RTLIL::Module*,RTLIL::Cell*>> pos_work;
+
+ for (auto &mod_it : design->modules_)
+ for (auto &cell_it : mod_it.second->cells_) {
+ RTLIL::Cell *cell = cell_it.second;
+ if (design->modules_.count(cell->type) == 0)
+ continue;
+ for (auto &conn : cell->connections())
+ if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') {
+ pos_mods.insert(design->modules_.at(cell->type));
+ pos_work.push_back(std::pair<RTLIL::Module*,RTLIL::Cell*>(mod_it.second, cell));
+ break;
+ }
+ }
+
+ for (auto module : pos_mods)
+ for (auto &wire_it : module->wires_) {
+ RTLIL::Wire *wire = wire_it.second;
+ if (wire->port_id > 0)
+ pos_map[std::pair<RTLIL::Module*,int>(module, wire->port_id)] = wire->name;
+ }
+
+ for (auto &work : pos_work) {
+ RTLIL::Module *module = work.first;
+ RTLIL::Cell *cell = work.second;
+ log("Mapping positional arguments of cell %s.%s (%s).\n",
+ RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
+ dict<RTLIL::IdString, RTLIL::SigSpec> new_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);
+ std::pair<RTLIL::Module*,int> key(design->modules_.at(cell->type), id);
+ if (pos_map.count(key) == 0) {
+ log(" Failed to map positional argument %d of cell %s.%s (%s).\n",
+ id, RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
+ new_connections[conn.first] = conn.second;
+ } else
+ new_connections[pos_map.at(key)] = conn.second;
+ } else
+ new_connections[conn.first] = conn.second;
+ cell->connections_ = new_connections;
+ }
+ }
+
+ log_pop();
+ }
+} HierarchyPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/hierarchy/singleton.cc b/passes/hierarchy/singleton.cc
new file mode 100644
index 00000000..03c365fb
--- /dev/null
+++ b/passes/hierarchy/singleton.cc
@@ -0,0 +1,101 @@
+/*
+ * 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"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct SingletonPass : public Pass {
+ SingletonPass() : Pass("singleton", "create singleton modules") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" singleton [selection]\n");
+ log("\n");
+ log("By default, a module that is instantiated by several other modules is only\n");
+ log("kept once in the design. This preserves the original modularity of the design\n");
+ log("and reduces the overall size of the design in memory. But it prevents certain\n");
+ log("optimizations and other operations on the design. This pass creates singleton\n");
+ log("modules for all selected cells. The created modules are marked with the\n");
+ log("'singleton' attribute.\n");
+ log("\n");
+ log("This commands only operates on modules that by themself have the 'singleton'\n");
+ log("attribute set (the 'top' module is a singleton implicitly).\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing SINGLETON pass (creating singleton modules).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-check") {
+ // flag_check = true;
+ // continue;
+ // }
+ }
+ extra_args(args, argidx, design);
+
+ bool did_something = true;
+ int singleton_cnt = 0;
+
+ while (did_something)
+ {
+ did_something = false;
+
+ for (auto module : design->selected_modules())
+ {
+ if (!module->get_bool_attribute("\\singleton") && !module->get_bool_attribute("\\top"))
+ continue;
+
+ for (auto cell : module->selected_cells())
+ {
+ auto tmod = design->module(cell->type);
+
+ if (tmod == nullptr)
+ continue;
+
+ if (tmod->get_bool_attribute("\\blackbox"))
+ continue;
+
+ if (tmod->get_bool_attribute("\\singleton"))
+ continue;
+
+ cell->type = module->name.str() + "." + log_id(cell->name);
+ log("Creating singleton '%s'.\n", log_id(cell->type));
+
+ auto smod = tmod->clone();
+ smod->name = cell->type;
+ smod->set_bool_attribute("\\singleton");
+ design->add(smod);
+
+ did_something = true;
+ singleton_cnt++;
+ }
+ }
+ }
+
+ log("Created %d singleton modules.\n", singleton_cnt);
+ }
+} SingletonPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/hierarchy/submod.cc b/passes/hierarchy/submod.cc
new file mode 100644
index 00000000..9f312f82
--- /dev/null
+++ b/passes/hierarchy/submod.cc
@@ -0,0 +1,368 @@
+/*
+ * 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/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <set>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct SubmodWorker
+{
+ CellTypes ct;
+ RTLIL::Design *design;
+ RTLIL::Module *module;
+
+ bool copy_mode;
+ std::string opt_name;
+
+ struct SubModule
+ {
+ std::string name, full_name;
+ std::set<RTLIL::Cell*> cells;
+ };
+
+ std::map<std::string, SubModule> submodules;
+
+ struct wire_flags_t {
+ RTLIL::Wire *new_wire;
+ bool is_int_driven, is_int_used, is_ext_driven, is_ext_used;
+ wire_flags_t() : new_wire(NULL), is_int_driven(false), is_int_used(false), is_ext_driven(false), is_ext_used(false) { }
+ };
+ std::map<RTLIL::Wire*, wire_flags_t> wire_flags;
+ bool flag_found_something;
+
+ void flag_wire(RTLIL::Wire *wire, bool create, bool set_int_driven, bool set_int_used, bool set_ext_driven, bool set_ext_used)
+ {
+ if (wire_flags.count(wire) == 0) {
+ if (!create)
+ return;
+ wire_flags[wire] = wire_flags_t();
+ }
+ if (set_int_driven)
+ wire_flags[wire].is_int_driven = true;
+ if (set_int_used)
+ wire_flags[wire].is_int_used = true;
+ if (set_ext_driven)
+ wire_flags[wire].is_ext_driven = true;
+ if (set_ext_used)
+ wire_flags[wire].is_ext_used = true;
+ flag_found_something = true;
+ }
+
+ void flag_signal(const RTLIL::SigSpec &sig, bool create, bool set_int_driven, bool set_int_used, bool set_ext_driven, bool set_ext_used)
+ {
+ for (auto &c : sig.chunks())
+ if (c.wire != NULL)
+ flag_wire(c.wire, create, set_int_driven, set_int_used, set_ext_driven, set_ext_used);
+ }
+
+ void handle_submodule(SubModule &submod)
+ {
+ log("Creating submodule %s (%s) of module %s.\n", submod.name.c_str(), submod.full_name.c_str(), module->name.c_str());
+
+ wire_flags.clear();
+ for (RTLIL::Cell *cell : submod.cells) {
+ if (ct.cell_known(cell->type)) {
+ for (auto &conn : cell->connections())
+ flag_signal(conn.second, true, ct.cell_output(cell->type, conn.first), ct.cell_input(cell->type, conn.first), false, false);
+ } else {
+ log_warning("Port directions for cell %s (%s) are unknown. Assuming inout for all ports.\n", cell->name.c_str(), cell->type.c_str());
+ for (auto &conn : cell->connections())
+ flag_signal(conn.second, true, true, true, false, false);
+ }
+ }
+ for (auto &it : module->cells_) {
+ RTLIL::Cell *cell = it.second;
+ if (submod.cells.count(cell) > 0)
+ continue;
+ if (ct.cell_known(cell->type)) {
+ for (auto &conn : cell->connections())
+ flag_signal(conn.second, false, false, false, ct.cell_output(cell->type, conn.first), ct.cell_input(cell->type, conn.first));
+ } else {
+ flag_found_something = false;
+ for (auto &conn : cell->connections())
+ flag_signal(conn.second, false, false, false, true, true);
+ if (flag_found_something)
+ log_warning("Port directions for cell %s (%s) are unknown. Assuming inout for all ports.\n", cell->name.c_str(), cell->type.c_str());
+ }
+ }
+
+ RTLIL::Module *new_mod = new RTLIL::Module;
+ new_mod->name = submod.full_name;
+ design->add(new_mod);
+ int auto_name_counter = 1;
+
+ std::set<RTLIL::IdString> all_wire_names;
+ for (auto &it : wire_flags) {
+ all_wire_names.insert(it.first->name);
+ }
+
+ for (auto &it : wire_flags)
+ {
+ RTLIL::Wire *wire = it.first;
+ wire_flags_t &flags = it.second;
+
+ if (wire->port_input)
+ flags.is_ext_driven = true;
+ if (wire->port_output)
+ flags.is_ext_used = true;
+
+ bool new_wire_port_input = false;
+ bool new_wire_port_output = false;
+
+ if (flags.is_int_driven && flags.is_ext_used)
+ new_wire_port_output = true;
+ if (flags.is_ext_driven && flags.is_int_used)
+ new_wire_port_input = true;
+
+ if (flags.is_int_driven && flags.is_ext_driven)
+ new_wire_port_input = true, new_wire_port_output = true;
+
+ std::string new_wire_name = wire->name.str();
+ if (new_wire_port_input || new_wire_port_output) {
+ while (new_wire_name[0] == '$') {
+ std::string next_wire_name = stringf("\\n%d", auto_name_counter++);
+ if (all_wire_names.count(next_wire_name) == 0) {
+ all_wire_names.insert(next_wire_name);
+ new_wire_name = next_wire_name;
+ }
+ }
+ }
+
+ RTLIL::Wire *new_wire = new_mod->addWire(new_wire_name, wire->width);
+ new_wire->port_input = new_wire_port_input;
+ new_wire->port_output = new_wire_port_output;
+ new_wire->start_offset = wire->start_offset;
+ new_wire->attributes = wire->attributes;
+
+ if (new_wire->port_input && new_wire->port_output)
+ log(" signal %s: inout %s\n", wire->name.c_str(), new_wire->name.c_str());
+ else if (new_wire->port_input)
+ log(" signal %s: input %s\n", wire->name.c_str(), new_wire->name.c_str());
+ else if (new_wire->port_output)
+ log(" signal %s: output %s\n", wire->name.c_str(), new_wire->name.c_str());
+ else
+ log(" signal %s: internal\n", wire->name.c_str());
+
+ flags.new_wire = new_wire;
+ }
+
+ new_mod->fixup_ports();
+
+ for (RTLIL::Cell *cell : submod.cells) {
+ RTLIL::Cell *new_cell = new_mod->addCell(cell->name, cell);
+ for (auto &conn : new_cell->connections_)
+ for (auto &bit : conn.second)
+ if (bit.wire != NULL) {
+ log_assert(wire_flags.count(bit.wire) > 0);
+ bit.wire = wire_flags[bit.wire].new_wire;
+ }
+ log(" cell %s (%s)\n", new_cell->name.c_str(), new_cell->type.c_str());
+ if (!copy_mode)
+ module->remove(cell);
+ }
+ submod.cells.clear();
+
+ if (!copy_mode) {
+ RTLIL::Cell *new_cell = module->addCell(submod.full_name, submod.full_name);
+ for (auto &it : wire_flags)
+ {
+ RTLIL::Wire *old_wire = it.first;
+ RTLIL::Wire *new_wire = it.second.new_wire;
+ if (new_wire->port_id > 0)
+ new_cell->setPort(new_wire->name, RTLIL::SigSpec(old_wire));
+ }
+ }
+ }
+
+ SubmodWorker(RTLIL::Design *design, RTLIL::Module *module, bool copy_mode = false, std::string opt_name = std::string()) :
+ design(design), module(module), copy_mode(copy_mode), opt_name(opt_name)
+ {
+ if (!design->selected_whole_module(module->name) && opt_name.empty())
+ return;
+
+ if (module->processes.size() > 0) {
+ log("Skipping module %s as it contains processes (run 'proc' pass first).\n", module->name.c_str());
+ return;
+ }
+
+ if (module->memories.size() > 0) {
+ log("Skipping module %s as it contains memories (run 'memory' pass first).\n", module->name.c_str());
+ return;
+ }
+
+ ct.setup_internals();
+ ct.setup_internals_mem();
+ ct.setup_stdcells();
+ ct.setup_stdcells_mem();
+ ct.setup_design(design);
+
+ if (opt_name.empty())
+ {
+ for (auto &it : module->wires_)
+ it.second->attributes.erase("\\submod");
+
+ for (auto &it : module->cells_)
+ {
+ RTLIL::Cell *cell = it.second;
+ if (cell->attributes.count("\\submod") == 0 || cell->attributes["\\submod"].bits.size() == 0) {
+ cell->attributes.erase("\\submod");
+ continue;
+ }
+
+ std::string submod_str = cell->attributes["\\submod"].decode_string();
+ cell->attributes.erase("\\submod");
+
+ if (submodules.count(submod_str) == 0) {
+ submodules[submod_str].name = submod_str;
+ submodules[submod_str].full_name = module->name.str() + "_" + submod_str;
+ while (design->modules_.count(submodules[submod_str].full_name) != 0 ||
+ module->count_id(submodules[submod_str].full_name) != 0)
+ submodules[submod_str].full_name += "_";
+ }
+
+ submodules[submod_str].cells.insert(cell);
+ }
+ }
+ else
+ {
+ for (auto &it : module->cells_)
+ {
+ RTLIL::Cell *cell = it.second;
+ if (!design->selected(module, cell))
+ continue;
+ submodules[opt_name].name = opt_name;
+ submodules[opt_name].full_name = RTLIL::escape_id(opt_name);
+ submodules[opt_name].cells.insert(cell);
+ }
+
+ if (submodules.size() == 0)
+ log("Nothing selected -> do nothing.\n");
+ }
+
+ for (auto &it : submodules)
+ handle_submodule(it.second);
+ }
+};
+
+struct SubmodPass : public Pass {
+ SubmodPass() : Pass("submod", "moving part of a module to a new submodule") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" submod [-copy] [selection]\n");
+ log("\n");
+ log("This pass identifies all cells with the 'submod' attribute and moves them to\n");
+ log("a newly created module. The value of the attribute is used as name for the\n");
+ log("cell that replaces the group of cells with the same attribute value.\n");
+ log("\n");
+ log("This pass can be used to create a design hierarchy in flat design. This can\n");
+ log("be useful for analyzing or reverse-engineering a design.\n");
+ log("\n");
+ log("This pass only operates on completely selected modules with no processes\n");
+ log("or memories.\n");
+ log("\n");
+ log("\n");
+ log(" submod -name <name> [-copy] [selection]\n");
+ log("\n");
+ log("As above, but don't use the 'submod' attribute but instead use the selection.\n");
+ log("Only objects from one module might be selected. The value of the -name option\n");
+ log("is used as the value of the 'submod' attribute above.\n");
+ log("\n");
+ log("By default the cells are 'moved' from the source module and the source module\n");
+ log("will use an instance of the new module after this command is finished. Call\n");
+ log("with -copy to not modify the source module.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing SUBMOD pass (moving cells to submodules as requested).\n");
+ log_push();
+
+ std::string opt_name;
+ bool copy_mode = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-name" && argidx+1 < args.size()) {
+ opt_name = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-copy") {
+ copy_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (opt_name.empty())
+ {
+ Pass::call(design, "opt_clean");
+ log_header(design, "Continuing SUBMOD pass.\n");
+
+ std::set<RTLIL::IdString> handled_modules;
+
+ bool did_something = true;
+ while (did_something) {
+ did_something = false;
+ std::vector<RTLIL::IdString> queued_modules;
+ for (auto &mod_it : design->modules_)
+ if (handled_modules.count(mod_it.first) == 0 && design->selected_whole_module(mod_it.first))
+ queued_modules.push_back(mod_it.first);
+ for (auto &modname : queued_modules)
+ if (design->modules_.count(modname) != 0) {
+ SubmodWorker worker(design, design->modules_[modname], copy_mode);
+ handled_modules.insert(modname);
+ did_something = true;
+ }
+ }
+
+ Pass::call(design, "opt_clean");
+ }
+ else
+ {
+ RTLIL::Module *module = NULL;
+ for (auto &mod_it : design->modules_) {
+ if (!design->selected_module(mod_it.first))
+ continue;
+ if (module != NULL)
+ log_cmd_error("More than one module selected: %s %s\n", module->name.c_str(), mod_it.first.c_str());
+ module = mod_it.second;
+ }
+ if (module == NULL)
+ log("Nothing selected -> do nothing.\n");
+ else {
+ Pass::call_on_module(design, module, "opt_clean");
+ log_header(design, "Continuing SUBMOD pass.\n");
+ SubmodWorker worker(design, module, copy_mode, opt_name);
+ }
+ }
+
+ log_pop();
+ }
+} SubmodPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/memory/Makefile.inc b/passes/memory/Makefile.inc
new file mode 100644
index 00000000..ad359c01
--- /dev/null
+++ b/passes/memory/Makefile.inc
@@ -0,0 +1,10 @@
+
+OBJS += passes/memory/memory.o
+OBJS += passes/memory/memory_dff.o
+OBJS += passes/memory/memory_share.o
+OBJS += passes/memory/memory_collect.o
+OBJS += passes/memory/memory_unpack.o
+OBJS += passes/memory/memory_bram.o
+OBJS += passes/memory/memory_map.o
+OBJS += passes/memory/memory_memx.o
+
diff --git a/passes/memory/memory.cc b/passes/memory/memory.cc
new file mode 100644
index 00000000..947d598b
--- /dev/null
+++ b/passes/memory/memory.cc
@@ -0,0 +1,102 @@
+/*
+ * 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/register.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct MemoryPass : public Pass {
+ MemoryPass() : Pass("memory", "translate memories to basic cells") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" memory [-nomap] [-nordff] [-memx] [-bram <bram_rules>] [selection]\n");
+ log("\n");
+ log("This pass calls all the other memory_* passes in a useful order:\n");
+ log("\n");
+ log(" memory_dff [-nordff] (-memx implies -nordff)\n");
+ log(" opt_clean\n");
+ log(" memory_share\n");
+ log(" opt_clean\n");
+ log(" memory_memx (when called with -memx)\n");
+ log(" memory_collect\n");
+ log(" memory_bram -rules <bram_rules> (when called with -bram)\n");
+ log(" memory_map (skipped if called with -nomap)\n");
+ log("\n");
+ log("This converts memories to word-wide DFFs and address decoders\n");
+ log("or multiport memory blocks if called with the -nomap option.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool flag_nomap = false;
+ bool flag_nordff = false;
+ bool flag_memx = false;
+ string memory_bram_opts;
+
+ log_header(design, "Executing MEMORY pass.\n");
+ log_push();
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-nomap") {
+ flag_nomap = true;
+ continue;
+ }
+ if (args[argidx] == "-nordff") {
+ flag_nordff = true;
+ continue;
+ }
+ if (args[argidx] == "-memx") {
+ flag_nordff = true;
+ flag_memx = true;
+ continue;
+ }
+ if (argidx+1 < args.size() && args[argidx] == "-bram") {
+ memory_bram_opts += " -rules " + args[++argidx];
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ Pass::call(design, flag_nordff ? "memory_dff -nordff" : "memory_dff");
+ Pass::call(design, "opt_clean");
+ Pass::call(design, "memory_share");
+ if (flag_memx)
+ Pass::call(design, "memory_memx");
+ Pass::call(design, "opt_clean");
+ Pass::call(design, "memory_collect");
+
+ if (!memory_bram_opts.empty())
+ Pass::call(design, "memory_bram" + memory_bram_opts);
+
+ if (!flag_nomap)
+ Pass::call(design, "memory_map");
+
+ log_pop();
+ }
+} MemoryPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc
new file mode 100644
index 00000000..a7f9cf38
--- /dev/null
+++ b/passes/memory/memory_bram.cc
@@ -0,0 +1,1236 @@
+/*
+ * 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"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct rules_t
+{
+ struct portinfo_t {
+ int group, index, dupidx;
+ int wrmode, enable, transp, clocks, clkpol;
+
+ SigBit sig_clock;
+ SigSpec sig_addr, sig_data, sig_en;
+ bool effective_clkpol;
+ bool make_transp;
+ bool make_outreg;
+ int mapped_port;
+ };
+
+ struct bram_t {
+ IdString name;
+ int variant;
+
+ int groups, abits, dbits, init;
+ vector<int> ports, wrmode, enable, transp, clocks, clkpol;
+
+ void dump_config() const
+ {
+ log(" bram %s # variant %d\n", log_id(name), variant);
+ log(" init %d\n", init);
+ log(" abits %d\n", abits);
+ log(" dbits %d\n", dbits);
+ log(" groups %d\n", groups);
+
+ log(" ports "); for (int v : ports) log("%4d", v); log("\n");
+ log(" wrmode"); for (int v : wrmode) log("%4d", v); log("\n");
+ log(" enable"); for (int v : enable) log("%4d", v); log("\n");
+ log(" transp"); for (int v : transp) log("%4d", v); log("\n");
+ log(" clocks"); for (int v : clocks) log("%4d", v); log("\n");
+ log(" clkpol"); for (int v : clkpol) log("%4d", v); log("\n");
+ log(" endbram\n");
+ }
+
+ void check_vectors() const
+ {
+ if (groups != GetSize(ports)) log_error("Bram %s variant %d has %d groups but only %d entries in 'ports'.\n", log_id(name), variant, groups, GetSize(ports));
+ if (groups != GetSize(wrmode)) log_error("Bram %s variant %d has %d groups but only %d entries in 'wrmode'.\n", log_id(name), variant, groups, GetSize(wrmode));
+ if (groups != GetSize(enable)) log_error("Bram %s variant %d has %d groups but only %d entries in 'enable'.\n", log_id(name), variant, groups, GetSize(enable));
+ if (groups != GetSize(transp)) log_error("Bram %s variant %d has %d groups but only %d entries in 'transp'.\n", log_id(name), variant, groups, GetSize(transp));
+ if (groups != GetSize(clocks)) log_error("Bram %s variant %d has %d groups but only %d entries in 'clocks'.\n", log_id(name), variant, groups, GetSize(clocks));
+ if (groups != GetSize(clkpol)) log_error("Bram %s variant %d has %d groups but only %d entries in 'clkpol'.\n", log_id(name), variant, groups, GetSize(clkpol));
+ }
+
+ vector<portinfo_t> make_portinfos() const
+ {
+ vector<portinfo_t> portinfos;
+ for (int i = 0; i < groups; i++)
+ for (int j = 0; j < ports[i]; j++) {
+ portinfo_t pi;
+ pi.group = i;
+ pi.index = j;
+ pi.dupidx = 0;
+ pi.wrmode = wrmode[i];
+ pi.enable = enable[i];
+ pi.transp = transp[i];
+ pi.clocks = clocks[i];
+ pi.clkpol = clkpol[i];
+ pi.mapped_port = -1;
+ pi.make_transp = false;
+ pi.make_outreg = false;
+ pi.effective_clkpol = false;
+ portinfos.push_back(pi);
+ }
+ return portinfos;
+ }
+
+ void find_variant_params(dict<IdString, Const> &variant_params, const bram_t &other) const
+ {
+ log_assert(name == other.name);
+
+ if (groups != other.groups)
+ log_error("Bram %s variants %d and %d have different values for 'groups'.\n", log_id(name), variant, other.variant);
+
+ if (abits != other.abits)
+ variant_params["\\CFG_ABITS"] = abits;
+ if (dbits != other.dbits)
+ variant_params["\\CFG_DBITS"] = dbits;
+ if (init != other.init)
+ variant_params["\\CFG_INIT"] = init;
+
+ for (int i = 0; i < groups; i++)
+ {
+ if (ports[i] != other.ports[i])
+ log_error("Bram %s variants %d and %d have different number of %c-ports.\n", log_id(name), variant, other.variant, 'A'+i);
+ if (wrmode[i] != other.wrmode[i])
+ variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[i];
+ if (enable[i] != other.enable[i])
+ variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[i];
+ if (transp[i] != other.transp[i])
+ variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[i];
+ if (clocks[i] != other.clocks[i])
+ variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[i];
+ if (clkpol[i] != other.clkpol[i])
+ variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[i];
+ }
+ }
+ };
+
+ struct match_t {
+ IdString name;
+ dict<string, int> min_limits, max_limits;
+ bool or_next_if_better, make_transp, make_outreg;
+ char shuffle_enable;
+ };
+
+ dict<IdString, vector<bram_t>> brams;
+ vector<match_t> matches;
+
+ std::ifstream infile;
+ vector<string> tokens;
+ vector<string> labels;
+ int linecount;
+
+ void syntax_error()
+ {
+ if (tokens.empty())
+ log_error("Unexpected end of rules file in line %d.\n", linecount);
+ log_error("Syntax error in rules file line %d.\n", linecount);
+ }
+
+ bool next_line()
+ {
+ string line;
+ while (std::getline(infile, line)) {
+ tokens.clear();
+ labels.clear();
+ linecount++;
+ for (string tok = next_token(line); !tok.empty(); tok = next_token(line)) {
+ if (tok[0] == '@') {
+ labels.push_back(tok.substr(1));
+ continue;
+ }
+ if (tok[0] == '#')
+ break;
+ tokens.push_back(tok);
+ }
+ if (!tokens.empty())
+ return true;
+ }
+ return false;
+ }
+
+ bool parse_single_int(const char *stmt, int &value)
+ {
+ if (GetSize(tokens) == 2 && tokens[0] == stmt) {
+ value = atoi(tokens[1].c_str());
+ return true;
+ }
+ return false;
+ }
+
+ bool parse_int_vect(const char *stmt, vector<int> &value)
+ {
+ if (GetSize(tokens) >= 2 && tokens[0] == stmt) {
+ value.resize(GetSize(tokens)-1);
+ for (int i = 1; i < GetSize(tokens); i++)
+ value[i-1] = atoi(tokens[i].c_str());
+ return true;
+ }
+ return false;
+ }
+
+ void parse_bram()
+ {
+ IdString bram_name = RTLIL::escape_id(tokens[1]);
+
+ if (GetSize(tokens) != 2)
+ syntax_error();
+
+ vector<vector<string>> lines_nolabels;
+ std::map<string, vector<vector<string>>> lines_labels;
+
+ while (next_line())
+ {
+ if (GetSize(tokens) == 1 && tokens[0] == "endbram")
+ break;
+ if (labels.empty())
+ lines_nolabels.push_back(tokens);
+ for (auto lab : labels)
+ lines_labels[lab].push_back(tokens);
+ }
+
+ std::map<string, vector<vector<string>>> variant_lines;
+
+ if (lines_labels.empty())
+ variant_lines[""] = lines_nolabels;
+ for (auto &it : lines_labels) {
+ variant_lines[it.first] = lines_nolabels;
+ variant_lines[it.first].insert(variant_lines[it.first].end(), it.second.begin(), it.second.end());
+ }
+
+ for (auto &it : variant_lines)
+ {
+ bram_t data;
+ data.name = bram_name;
+ data.variant = GetSize(brams[data.name]) + 1;
+ data.groups = 0;
+ data.abits = 0;
+ data.dbits = 0;
+ data.init = 0;
+
+ for (auto &line_tokens : it.second)
+ {
+ tokens = line_tokens;
+
+ if (parse_single_int("groups", data.groups))
+ continue;
+
+ if (parse_single_int("abits", data.abits))
+ continue;
+
+ if (parse_single_int("dbits", data.dbits))
+ continue;
+
+ if (parse_single_int("init", data.init))
+ continue;
+
+ if (parse_int_vect("ports", data.ports))
+ continue;
+
+ if (parse_int_vect("wrmode", data.wrmode))
+ continue;
+
+ if (parse_int_vect("enable", data.enable))
+ continue;
+
+ if (parse_int_vect("transp", data.transp))
+ continue;
+
+ if (parse_int_vect("clocks", data.clocks))
+ continue;
+
+ if (parse_int_vect("clkpol", data.clkpol))
+ continue;
+
+ syntax_error();
+ }
+
+ data.check_vectors();
+ brams[data.name].push_back(data);
+ }
+ }
+
+ void parse_match()
+ {
+ if (GetSize(tokens) != 2)
+ syntax_error();
+
+ match_t data;
+ data.name = RTLIL::escape_id(tokens[1]);
+ data.or_next_if_better = false;
+ data.make_transp = false;
+ data.make_outreg = false;
+ data.shuffle_enable = 0;
+
+ while (next_line())
+ {
+ if (!labels.empty())
+ syntax_error();
+
+ if (GetSize(tokens) == 1 && tokens[0] == "endmatch") {
+ matches.push_back(data);
+ break;
+ }
+
+ if (GetSize(tokens) == 3 && tokens[0] == "min") {
+ data.min_limits[tokens[1]] = atoi(tokens[2].c_str());
+ continue;
+ }
+
+ if (GetSize(tokens) == 3 && tokens[0] == "max") {
+ data.max_limits[tokens[1]] = atoi(tokens[2].c_str());
+ continue;
+ }
+
+ if (GetSize(tokens) == 2 && tokens[0] == "shuffle_enable" && GetSize(tokens[1]) == 1 && 'A' <= tokens[1][0] && tokens[1][0] <= 'Z') {
+ data.shuffle_enable = tokens[1][0];
+ continue;
+ }
+
+ if (GetSize(tokens) == 1 && tokens[0] == "make_transp") {
+ data.make_transp = true;
+ continue;
+ }
+
+ if (GetSize(tokens) == 1 && tokens[0] == "make_outreg") {
+ data.make_transp = true;
+ data.make_outreg = true;
+ continue;
+ }
+
+ if (GetSize(tokens) == 1 && tokens[0] == "or_next_if_better") {
+ data.or_next_if_better = true;
+ continue;
+ }
+
+ syntax_error();
+ }
+ }
+
+ void parse(string filename)
+ {
+ rewrite_filename(filename);
+ infile.open(filename);
+ linecount = 0;
+
+ if (infile.fail())
+ log_error("Can't open rules file `%s'.\n", filename.c_str());
+
+ while (next_line())
+ {
+ if (!labels.empty())
+ syntax_error();
+
+ if (tokens[0] == "bram") {
+ parse_bram();
+ continue;
+ }
+
+ if (tokens[0] == "match") {
+ parse_match();
+ continue;
+ }
+
+ syntax_error();
+ }
+
+ infile.close();
+ }
+};
+
+bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, const rules_t::match_t &match, dict<string, int> &match_properties, int mode)
+{
+ Module *module = cell->module;
+
+ auto portinfos = bram.make_portinfos();
+ int dup_count = 1;
+
+ pair<SigBit, bool> make_transp_clk;
+ bool enable_make_transp = false;
+ int make_transp_enbits = 0;
+
+ dict<int, pair<SigBit, bool>> clock_domains;
+ dict<int, bool> clock_polarities;
+ dict<int, bool> read_transp;
+ pool<int> clocks_wr_ports;
+ pool<int> clkpol_wr_ports;
+ int clocks_max = 0;
+ int clkpol_max = 0;
+ int transp_max = 0;
+
+ clock_polarities[0] = false;
+ clock_polarities[1] = true;
+
+ for (auto &pi : portinfos) {
+ if (pi.wrmode) {
+ clocks_wr_ports.insert(pi.clocks);
+ if (pi.clkpol > 1)
+ clkpol_wr_ports.insert(pi.clkpol);
+ }
+ clocks_max = max(clocks_max, pi.clocks);
+ clkpol_max = max(clkpol_max, pi.clkpol);
+ transp_max = max(transp_max, pi.transp);
+ }
+
+ log(" Mapping to bram type %s (variant %d):\n", log_id(bram.name), bram.variant);
+ // bram.dump_config();
+
+ int mem_size = cell->getParam("\\SIZE").as_int();
+ int mem_abits = cell->getParam("\\ABITS").as_int();
+ int mem_width = cell->getParam("\\WIDTH").as_int();
+ // int mem_offset = cell->getParam("\\OFFSET").as_int();
+
+ bool cell_init = !SigSpec(cell->getParam("\\INIT")).is_fully_undef();
+ vector<Const> initdata;
+
+ if (cell_init) {
+ Const initparam = cell->getParam("\\INIT");
+ initdata.reserve(mem_size);
+ for (int i=0; i < mem_size; i++)
+ initdata.push_back(initparam.extract(mem_width*i, mem_width, State::Sx));
+ }
+
+ int wr_ports = cell->getParam("\\WR_PORTS").as_int();
+ auto wr_clken = SigSpec(cell->getParam("\\WR_CLK_ENABLE"));
+ auto wr_clkpol = SigSpec(cell->getParam("\\WR_CLK_POLARITY"));
+ wr_clken.extend_u0(wr_ports);
+ wr_clkpol.extend_u0(wr_ports);
+
+ SigSpec wr_en = cell->getPort("\\WR_EN");
+ SigSpec wr_clk = cell->getPort("\\WR_CLK");
+ SigSpec wr_data = cell->getPort("\\WR_DATA");
+ SigSpec wr_addr = cell->getPort("\\WR_ADDR");
+
+ int rd_ports = cell->getParam("\\RD_PORTS").as_int();
+ auto rd_clken = SigSpec(cell->getParam("\\RD_CLK_ENABLE"));
+ auto rd_clkpol = SigSpec(cell->getParam("\\RD_CLK_POLARITY"));
+ auto rd_transp = SigSpec(cell->getParam("\\RD_TRANSPARENT"));
+ rd_clken.extend_u0(rd_ports);
+ rd_clkpol.extend_u0(rd_ports);
+ rd_transp.extend_u0(rd_ports);
+
+ SigSpec rd_en = cell->getPort("\\RD_EN");
+ SigSpec rd_clk = cell->getPort("\\RD_CLK");
+ SigSpec rd_data = cell->getPort("\\RD_DATA");
+ SigSpec rd_addr = cell->getPort("\\RD_ADDR");
+
+ if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0 && wr_ports > 0)
+ {
+ int bucket_size = bram.dbits / portinfos.at(match.shuffle_enable - 'A').enable;
+ log(" Shuffle bit order to accommodate enable buckets of size %d..\n", bucket_size);
+
+ // extract unshuffled data/enable bits
+
+ std::vector<SigSpec> old_wr_en;
+ std::vector<SigSpec> old_wr_data;
+ std::vector<SigSpec> old_rd_data;
+
+ for (int i = 0; i < wr_ports; i++) {
+ old_wr_en.push_back(wr_en.extract(i*mem_width, mem_width));
+ old_wr_data.push_back(wr_data.extract(i*mem_width, mem_width));
+ }
+
+ for (int i = 0; i < rd_ports; i++)
+ old_rd_data.push_back(rd_data.extract(i*mem_width, mem_width));
+
+ // analyze enable structure
+
+ std::vector<SigSpec> en_order;
+ dict<SigSpec, vector<int>> bits_wr_en;
+
+ for (int i = 0; i < mem_width; i++) {
+ SigSpec sig;
+ for (int j = 0; j < wr_ports; j++)
+ sig.append(old_wr_en[j][i]);
+ if (bits_wr_en.count(sig) == 0)
+ en_order.push_back(sig);
+ bits_wr_en[sig].push_back(i);
+ }
+
+ // re-create memory ports
+
+ 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<int> shuffle_map;
+
+ for (auto &it : en_order)
+ {
+ auto &bits = bits_wr_en.at(it);
+ int buckets = (GetSize(bits) + bucket_size - 1) / bucket_size;
+ int fillbits = buckets*bucket_size - GetSize(bits);
+ SigBit fillbit;
+
+ for (int i = 0; i < GetSize(bits); i++) {
+ for (int j = 0; j < wr_ports; j++) {
+ new_wr_en[j].append(old_wr_en[j][bits[i]]);
+ new_wr_data[j].append(old_wr_data[j][bits[i]]);
+ fillbit = old_wr_en[j][bits[i]];
+ }
+ for (int j = 0; j < rd_ports; j++)
+ new_rd_data[j].append(old_rd_data[j][bits[i]]);
+ shuffle_map.push_back(bits[i]);
+ }
+
+ for (int i = 0; i < fillbits; i++) {
+ for (int j = 0; j < wr_ports; j++) {
+ new_wr_en[j].append(fillbit);
+ new_wr_data[j].append(State::S0);
+ }
+ for (int j = 0; j < rd_ports; j++)
+ new_rd_data[j].append(State::Sx);
+ shuffle_map.push_back(-1);
+ }
+ }
+
+ log(" Results of bit order shuffling:");
+ for (int v : shuffle_map)
+ log(" %d", v);
+ log("\n");
+
+ // update mem_*, wr_*, and rd_* variables
+
+ mem_width = GetSize(new_wr_en.front());
+ wr_en = SigSpec(0, wr_ports * mem_width);
+ wr_data = SigSpec(0, wr_ports * mem_width);
+ rd_data = SigSpec(0, rd_ports * mem_width);
+
+ for (int i = 0; i < wr_ports; i++) {
+ wr_en.replace(i*mem_width, new_wr_en[i]);
+ wr_data.replace(i*mem_width, new_wr_data[i]);
+ }
+
+ for (int i = 0; i < rd_ports; i++)
+ rd_data.replace(i*mem_width, new_rd_data[i]);
+ }
+
+ // assign write ports
+
+ 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;
+ bool clkpol = wr_clkpol[cell_port_i] == State::S1;
+ SigBit clksig = wr_clk[cell_port_i];
+
+ pair<SigBit, bool> clkdom(clksig, clkpol);
+ if (!clken)
+ clkdom = pair<SigBit, bool>(State::S1, false);
+
+ log(" Write port #%d is in clock domain %s%s.\n",
+ cell_port_i, clkdom.second ? "" : "!",
+ clken ? log_signal(clkdom.first) : "~async~");
+
+ for (; bram_port_i < GetSize(portinfos); bram_port_i++)
+ {
+ auto &pi = portinfos[bram_port_i];
+ make_transp_enbits = pi.enable;
+ make_transp_clk = clkdom;
+
+ if (pi.wrmode != 1)
+ skip_bram_wport:
+ continue;
+
+ if (clken) {
+ if (pi.clocks == 0) {
+ log(" Bram port %c%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1);
+ goto skip_bram_wport;
+ }
+ if (clock_domains.count(pi.clocks) && clock_domains.at(pi.clocks) != clkdom) {
+ log(" Bram port %c%d is in a different clock domain.\n", pi.group + 'A', pi.index + 1);
+ goto skip_bram_wport;
+ }
+ if (clock_polarities.count(pi.clkpol) && clock_polarities.at(pi.clkpol) != clkpol) {
+ log(" Bram port %c%d has incompatible clock polarity.\n", pi.group + 'A', pi.index + 1);
+ goto skip_bram_wport;
+ }
+ } else {
+ if (pi.clocks != 0) {
+ log(" Bram port %c%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1);
+ goto skip_bram_wport;
+ }
+ }
+
+ SigSpec sig_en;
+ SigBit last_en_bit = State::S1;
+ for (int i = 0; i < mem_width; i++) {
+ if (pi.enable && i % (bram.dbits / pi.enable) == 0) {
+ last_en_bit = wr_en[i + cell_port_i*mem_width];
+ sig_en.append(last_en_bit);
+ }
+ if (last_en_bit != wr_en[i + cell_port_i*mem_width]) {
+ log(" Bram port %c%d has incompatible enable structure.\n", pi.group + 'A', pi.index + 1);
+ goto skip_bram_wport;
+ }
+ }
+
+ log(" Mapped to bram port %c%d.\n", pi.group + 'A', pi.index + 1);
+ pi.mapped_port = cell_port_i;
+
+ if (clken) {
+ clock_domains[pi.clocks] = clkdom;
+ clock_polarities[pi.clkpol] = clkdom.second;
+ pi.sig_clock = clkdom.first;
+ pi.effective_clkpol = clkdom.second;
+ }
+
+ pi.sig_en = sig_en;
+ pi.sig_addr = wr_addr.extract(cell_port_i*mem_abits, mem_abits);
+ pi.sig_data = wr_data.extract(cell_port_i*mem_width, mem_width);
+
+ bram_port_i++;
+ goto mapped_wr_port;
+ }
+
+ log(" Failed to map write port #%d.\n", cell_port_i);
+ return false;
+ mapped_wr_port:;
+ }
+
+ // housekeeping stuff for growing more read ports and restarting read port assignments
+
+ int grow_read_ports_cursor = -1;
+ bool try_growing_more_read_ports = false;
+ auto backup_clock_domains = clock_domains;
+ auto backup_clock_polarities = clock_polarities;
+
+ if (0) {
+grow_read_ports:;
+ vector<rules_t::portinfo_t> new_portinfos;
+ for (auto &pi : portinfos) {
+ if (pi.wrmode == 0) {
+ pi.mapped_port = -1;
+ pi.sig_clock = SigBit();
+ pi.sig_addr = SigSpec();
+ pi.sig_data = SigSpec();
+ pi.sig_en = SigSpec();
+ }
+ new_portinfos.push_back(pi);
+ if (pi.dupidx == dup_count-1) {
+ if (pi.clocks && !clocks_wr_ports[pi.clocks])
+ pi.clocks += clocks_max;
+ if (pi.clkpol > 1 && !clkpol_wr_ports[pi.clkpol])
+ pi.clkpol += clkpol_max;
+ if (pi.transp > 1)
+ pi.transp += transp_max;
+ pi.dupidx++;
+ new_portinfos.push_back(pi);
+ }
+ }
+ try_growing_more_read_ports = false;
+ portinfos.swap(new_portinfos);
+ clock_domains = backup_clock_domains;
+ clock_polarities = backup_clock_polarities;
+ dup_count++;
+ }
+
+ read_transp.clear();
+ read_transp[0] = false;
+ read_transp[1] = true;
+
+ // assign read ports
+
+ for (int cell_port_i = 0; cell_port_i < rd_ports; cell_port_i++)
+ {
+ bool clken = rd_clken[cell_port_i] == State::S1;
+ bool clkpol = rd_clkpol[cell_port_i] == State::S1;
+ bool transp = rd_transp[cell_port_i] == State::S1;
+ SigBit clksig = rd_clk[cell_port_i];
+
+ if (wr_ports == 0)
+ transp = false;
+
+ pair<SigBit, bool> clkdom(clksig, clkpol);
+ if (!clken)
+ clkdom = pair<SigBit, bool>(State::S1, false);
+
+ log(" Read port #%d is in clock domain %s%s.\n",
+ cell_port_i, clkdom.second ? "" : "!",
+ clken ? log_signal(clkdom.first) : "~async~");
+
+ for (int bram_port_i = 0; bram_port_i < GetSize(portinfos); bram_port_i++)
+ {
+ auto &pi = portinfos[bram_port_i];
+
+ if (pi.wrmode != 0 || pi.mapped_port >= 0)
+ skip_bram_rport:
+ continue;
+
+ if (clken) {
+ if (pi.clocks == 0) {
+ if (match.make_outreg) {
+ pi.make_outreg = true;
+ goto skip_bram_rport_clkcheck;
+ }
+ log(" Bram port %c%d.%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+ goto skip_bram_rport;
+ }
+ if (clock_domains.count(pi.clocks) && clock_domains.at(pi.clocks) != clkdom) {
+ log(" Bram port %c%d.%d is in a different clock domain.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+ goto skip_bram_rport;
+ }
+ if (clock_polarities.count(pi.clkpol) && clock_polarities.at(pi.clkpol) != clkpol) {
+ log(" Bram port %c%d.%d has incompatible clock polarity.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+ goto skip_bram_rport;
+ }
+ if (rd_en[cell_port_i] != State::S1 && pi.enable == 0) {
+ log(" Bram port %c%d.%d has no read enable input.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+ goto skip_bram_rport;
+ }
+ skip_bram_rport_clkcheck:
+ 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;
+ } 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;
+ }
+ }
+ } else {
+ if (pi.clocks != 0) {
+ log(" Bram port %c%d.%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+ goto skip_bram_rport;
+ }
+ }
+
+ log(" Mapped to bram port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+ pi.mapped_port = cell_port_i;
+
+ if (clken) {
+ clock_domains[pi.clocks] = clkdom;
+ clock_polarities[pi.clkpol] = clkdom.second;
+ read_transp[pi.transp] = transp;
+ pi.sig_clock = clkdom.first;
+ pi.sig_en = rd_en[cell_port_i];
+ pi.effective_clkpol = clkdom.second;
+ }
+
+ pi.sig_addr = rd_addr.extract(cell_port_i*mem_abits, mem_abits);
+ pi.sig_data = rd_data.extract(cell_port_i*mem_width, mem_width);
+
+ if (grow_read_ports_cursor < cell_port_i) {
+ grow_read_ports_cursor = cell_port_i;
+ try_growing_more_read_ports = true;
+ }
+
+ goto mapped_rd_port;
+ }
+
+ log(" Failed to map read port #%d.\n", cell_port_i);
+ if (try_growing_more_read_ports) {
+ log(" Growing more read ports by duplicating bram cells.\n");
+ goto grow_read_ports;
+ }
+ return false;
+ mapped_rd_port:;
+ }
+
+ // update properties and re-check conditions
+
+ if (mode <= 1)
+ {
+ match_properties["dups"] = dup_count;
+ match_properties["waste"] = match_properties["dups"] * match_properties["bwaste"];
+
+ int cells = ((mem_width + bram.dbits - 1) / bram.dbits) * ((mem_size + (1 << bram.abits) - 1) / (1 << bram.abits));
+ match_properties["efficiency"] = (100 * match_properties["bits"]) / (dup_count * cells * bram.dbits * (1 << bram.abits));
+
+ match_properties["dcells"] = ((mem_width + bram.dbits - 1) / bram.dbits);
+ match_properties["acells"] = ((mem_size + (1 << bram.abits) - 1) / (1 << bram.abits));
+ match_properties["cells"] = match_properties["dcells"] * match_properties["acells"] * match_properties["dups"];
+
+ log(" Updated properties: dups=%d waste=%d efficiency=%d\n",
+ match_properties["dups"], match_properties["waste"], match_properties["efficiency"]);
+
+ for (auto it : match.min_limits) {
+ if (!match_properties.count(it.first))
+ log_error("Unknown property '%s' in match rule for bram type %s.\n",
+ it.first.c_str(), log_id(match.name));
+ if (match_properties[it.first] >= it.second)
+ continue;
+ log(" Rule for bram type %s rejected: requirement 'min %s %d' not met.\n",
+ log_id(match.name), it.first.c_str(), it.second);
+ return false;
+ }
+ for (auto it : match.max_limits) {
+ if (!match_properties.count(it.first))
+ log_error("Unknown property '%s' in match rule for bram type %s.\n",
+ it.first.c_str(), log_id(match.name));
+ if (match_properties[it.first] <= it.second)
+ continue;
+ log(" Rule for bram type %s rejected: requirement 'max %s %d' not met.\n",
+ log_id(match.name), it.first.c_str(), it.second);
+ return false;
+ }
+
+ if (mode == 1)
+ return true;
+ }
+
+ // prepare variant parameters
+
+ dict<IdString, Const> variant_params;
+ for (auto &other_bram : rules.brams.at(bram.name))
+ bram.find_variant_params(variant_params, other_bram);
+
+ // actually replace that memory cell
+
+ dict<SigSpec, pair<SigSpec, SigSpec>> dout_cache;
+
+ for (int grid_d = 0; grid_d*bram.dbits < mem_width; grid_d++)
+ {
+ SigSpec mktr_wraddr, mktr_wrdata, mktr_wrdata_q;
+ vector<SigSpec> mktr_wren;
+
+ if (enable_make_transp) {
+ mktr_wraddr = module->addWire(NEW_ID, bram.abits);
+ mktr_wrdata = module->addWire(NEW_ID, bram.dbits);
+ mktr_wrdata_q = module->addWire(NEW_ID, bram.dbits);
+ module->addDff(NEW_ID, make_transp_clk.first, mktr_wrdata, mktr_wrdata_q, make_transp_clk.second);
+ for (int grid_a = 0; grid_a*(1 << bram.abits) < mem_size; grid_a++)
+ mktr_wren.push_back(module->addWire(NEW_ID, make_transp_enbits));
+ }
+
+ for (int grid_a = 0; grid_a*(1 << bram.abits) < mem_size; grid_a++)
+ for (int dupidx = 0; dupidx < dup_count; dupidx++)
+ {
+ Cell *c = module->addCell(module->uniquify(stringf("%s.%d.%d.%d", cell->name.c_str(), grid_d, grid_a, dupidx)), bram.name);
+ log(" Creating %s cell at grid position <%d %d %d>: %s\n", log_id(bram.name), grid_d, grid_a, dupidx, log_id(c));
+
+ for (auto &vp : variant_params)
+ c->setParam(vp.first, vp.second);
+
+ if (cell_init) {
+ int init_offset = grid_a*(1 << bram.abits);
+ int init_shift = grid_d*bram.dbits;
+ int init_size = (1 << bram.abits);
+ Const initparam(State::Sx, init_size*bram.dbits);
+ for (int i = 0; i < init_size; i++) {
+ State padding = State::Sx;
+ for (int j = 0; j < bram.dbits; j++)
+ if (init_offset+i < GetSize(initdata) && init_shift+j < GetSize(initdata[init_offset+i]))
+ initparam[i*bram.dbits+j] = initdata[init_offset+i][init_shift+j];
+ else
+ initparam[i*bram.dbits+j] = padding;
+ }
+ c->setParam("\\INIT", initparam);
+ }
+
+ for (auto &pi : portinfos)
+ {
+ if (pi.dupidx != dupidx)
+ continue;
+
+ string prefix = stringf("%c%d", pi.group + 'A', pi.index + 1);
+ const char *pf = prefix.c_str();
+
+ if (pi.clocks && (!c->hasPort(stringf("\\CLK%d", (pi.clocks-1) % clocks_max + 1)) || pi.sig_clock.wire)) {
+ c->setPort(stringf("\\CLK%d", (pi.clocks-1) % clocks_max + 1), pi.sig_clock);
+ if (pi.clkpol > 1 && pi.sig_clock.wire)
+ c->setParam(stringf("\\CLKPOL%d", (pi.clkpol-1) % clkpol_max + 1), clock_polarities.at(pi.clkpol));
+ if (pi.transp > 1 && pi.sig_clock.wire)
+ c->setParam(stringf("\\TRANSP%d", (pi.transp-1) % transp_max + 1), read_transp.at(pi.transp));
+ }
+
+ SigSpec addr_ok;
+ if (GetSize(pi.sig_addr) > bram.abits) {
+ SigSpec extra_addr = pi.sig_addr.extract(bram.abits, GetSize(pi.sig_addr) - bram.abits);
+ SigSpec extra_addr_sel = SigSpec(grid_a, GetSize(extra_addr));
+ addr_ok = module->Eq(NEW_ID, extra_addr, extra_addr_sel);
+ }
+
+ if (pi.enable)
+ {
+ SigSpec sig_en = pi.sig_en;
+
+ if (pi.wrmode == 1) {
+ sig_en.extend_u0((grid_d+1) * pi.enable);
+ sig_en = sig_en.extract(grid_d * pi.enable, pi.enable);
+ }
+
+ if (!addr_ok.empty())
+ sig_en = module->Mux(NEW_ID, SigSpec(0, GetSize(sig_en)), sig_en, addr_ok);
+
+ c->setPort(stringf("\\%sEN", pf), sig_en);
+
+ if (pi.wrmode == 1 && enable_make_transp)
+ module->connect(mktr_wren[grid_a], sig_en);
+ }
+
+ SigSpec sig_addr = pi.sig_addr;
+ sig_addr.extend_u0(bram.abits);
+ c->setPort(stringf("\\%sADDR", pf), sig_addr);
+
+ if (pi.wrmode == 1 && enable_make_transp && grid_a == 0)
+ module->connect(mktr_wraddr, sig_addr);
+
+ SigSpec sig_data = pi.sig_data;
+ sig_data.extend_u0((grid_d+1) * bram.dbits);
+ sig_data = sig_data.extract(grid_d * bram.dbits, bram.dbits);
+
+ if (pi.wrmode == 1) {
+ c->setPort(stringf("\\%sDATA", pf), sig_data);
+ if (enable_make_transp && grid_a == 0)
+ module->connect(mktr_wrdata, sig_data);
+ } else {
+ SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits);
+ c->setPort(stringf("\\%sDATA", pf), bram_dout);
+
+ 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)
+ {
+ 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),
+ mktr_wren[grid_a], module->Eq(NEW_ID, mktr_wraddr, sig_addr));
+
+ SigSpec transp_en_q = module->addWire(NEW_ID, make_transp_enbits);
+ module->addDff(NEW_ID, make_transp_clk.first, transp_en_d, transp_en_q, make_transp_clk.second);
+
+ for (int i = 0; i < make_transp_enbits; i++) {
+ int en_width = bram.dbits / make_transp_enbits;
+ SigSpec orig_bram_dout = bram_dout.extract(i * en_width, en_width);
+ SigSpec bypass_dout = mktr_wrdata_q.extract(i * en_width, en_width);
+ bram_dout.replace(i * en_width, module->Mux(NEW_ID, orig_bram_dout, bypass_dout, transp_en_q[i]));
+ }
+ }
+
+ for (int i = bram.dbits-1; i >= 0; i--)
+ if (sig_data[i].wire == nullptr) {
+ sig_data.remove(i);
+ bram_dout.remove(i);
+ }
+
+ SigSpec addr_ok_q = addr_ok;
+ if ((pi.clocks || pi.make_outreg) && !addr_ok.empty()) {
+ addr_ok_q = module->addWire(NEW_ID);
+ module->addDff(NEW_ID, pi.sig_clock, addr_ok, addr_ok_q, pi.effective_clkpol);
+ }
+
+ dout_cache[sig_data].first.append(addr_ok_q);
+ dout_cache[sig_data].second.append(bram_dout);
+ }
+ }
+ }
+ }
+
+ for (auto &it : dout_cache)
+ {
+ if (it.second.first.empty())
+ {
+ log_assert(GetSize(it.first) == GetSize(it.second.second));
+ module->connect(it.first, it.second.second);
+ }
+ else
+ {
+ log_assert(GetSize(it.first)*GetSize(it.second.first) == GetSize(it.second.second));
+ module->addPmux(NEW_ID, SigSpec(State::Sx, GetSize(it.first)), it.second.second, it.second.first, it.first);
+ }
+ }
+
+ module->remove(cell);
+ return true;
+}
+
+void handle_cell(Cell *cell, const rules_t &rules)
+{
+ log("Processing %s.%s:\n", log_id(cell->module), log_id(cell));
+
+ bool cell_init = !SigSpec(cell->getParam("\\INIT")).is_fully_undef();
+
+ dict<string, int> match_properties;
+ match_properties["words"] = cell->getParam("\\SIZE").as_int();
+ match_properties["abits"] = cell->getParam("\\ABITS").as_int();
+ match_properties["dbits"] = cell->getParam("\\WIDTH").as_int();
+ match_properties["wports"] = cell->getParam("\\WR_PORTS").as_int();
+ match_properties["rports"] = cell->getParam("\\RD_PORTS").as_int();
+ match_properties["bits"] = match_properties["words"] * match_properties["dbits"];
+ match_properties["ports"] = match_properties["wports"] + match_properties["rports"];
+
+ log(" Properties:");
+ for (auto &it : match_properties)
+ log(" %s=%d", it.first.c_str(), it.second);
+ log("\n");
+
+ pool<pair<IdString, int>> failed_brams;
+ dict<pair<int, int>, tuple<int, int, int>> best_rule_cache;
+
+ for (int i = 0; i < GetSize(rules.matches); i++)
+ {
+ auto &match = rules.matches.at(i);
+
+ if (!rules.brams.count(rules.matches[i].name))
+ log_error("No bram description for resource %s found!\n", log_id(rules.matches[i].name));
+
+ for (int vi = 0; vi < GetSize(rules.brams.at(match.name)); vi++)
+ {
+ auto &bram = rules.brams.at(match.name).at(vi);
+ bool or_next_if_better = match.or_next_if_better || vi+1 < GetSize(rules.brams.at(match.name));
+
+ if (failed_brams.count(pair<IdString, int>(bram.name, bram.variant)))
+ continue;
+
+ int avail_rd_ports = 0;
+ int avail_wr_ports = 0;
+ for (int j = 0; j < bram.groups; j++) {
+ if (GetSize(bram.wrmode) < j || bram.wrmode.at(j) == 0)
+ avail_rd_ports += GetSize(bram.ports) < j ? bram.ports.at(j) : 0;
+ if (GetSize(bram.wrmode) < j || bram.wrmode.at(j) != 0)
+ avail_wr_ports += GetSize(bram.ports) < j ? bram.ports.at(j) : 0;
+ }
+
+ log(" Checking rule #%d for bram type %s (variant %d):\n", i+1, log_id(bram.name), bram.variant);
+ log(" Bram geometry: abits=%d dbits=%d wports=%d rports=%d\n", bram.abits, bram.dbits, avail_wr_ports, avail_rd_ports);
+
+ int dups = avail_rd_ports ? (match_properties["rports"] + avail_rd_ports - 1) / avail_rd_ports : 1;
+ match_properties["dups"] = dups;
+
+ log(" Estimated number of duplicates for more read ports: dups=%d\n", match_properties["dups"]);
+
+ int aover = match_properties["words"] % (1 << bram.abits);
+ int awaste = aover ? (1 << bram.abits) - aover : 0;
+ match_properties["awaste"] = awaste;
+
+ int dover = match_properties["dbits"] % bram.dbits;
+ int dwaste = dover ? bram.dbits - dover : 0;
+ match_properties["dwaste"] = dwaste;
+
+ int bwaste = awaste * bram.dbits + dwaste * (1 << bram.abits) - awaste * dwaste;
+ match_properties["bwaste"] = bwaste;
+
+ int waste = match_properties["dups"] * bwaste;
+ match_properties["waste"] = waste;
+
+ int cells = ((match_properties["dbits"] + bram.dbits - 1) / bram.dbits) * ((match_properties["words"] + (1 << bram.abits) - 1) / (1 << bram.abits));
+ int efficiency = (100 * match_properties["bits"]) / (dups * cells * bram.dbits * (1 << bram.abits));
+ match_properties["efficiency"] = efficiency;
+
+ log(" Metrics for %s: awaste=%d dwaste=%d bwaste=%d waste=%d efficiency=%d\n",
+ log_id(match.name), awaste, dwaste, bwaste, waste, efficiency);
+
+ if (cell_init && bram.init == 0) {
+ log(" Rule #%d for bram type %s (variant %d) rejected: cannot be initialized.\n",
+ i+1, log_id(bram.name), bram.variant);
+ goto next_match_rule;
+ }
+
+ for (auto it : match.min_limits) {
+ if (it.first == "waste" || it.first == "dups" || it.first == "acells" || it.first == "dcells" || it.first == "cells")
+ continue;
+ if (!match_properties.count(it.first))
+ log_error("Unknown property '%s' in match rule for bram type %s.\n",
+ it.first.c_str(), log_id(match.name));
+ if (match_properties[it.first] >= it.second)
+ continue;
+ log(" Rule #%d for bram type %s (variant %d) rejected: requirement 'min %s %d' not met.\n",
+ i+1, log_id(bram.name), bram.variant, it.first.c_str(), it.second);
+ goto next_match_rule;
+ }
+
+ for (auto it : match.max_limits) {
+ if (it.first == "acells" || it.first == "dcells" || it.first == "cells")
+ continue;
+ if (!match_properties.count(it.first))
+ log_error("Unknown property '%s' in match rule for bram type %s.\n",
+ it.first.c_str(), log_id(match.name));
+ if (match_properties[it.first] <= it.second)
+ continue;
+ log(" Rule #%d for bram type %s (variant %d) rejected: requirement 'max %s %d' not met.\n",
+ i+1, log_id(bram.name), bram.variant, it.first.c_str(), it.second);
+ goto next_match_rule;
+ }
+
+ log(" Rule #%d for bram type %s (variant %d) accepted.\n", i+1, log_id(bram.name), bram.variant);
+
+ if (or_next_if_better || !best_rule_cache.empty())
+ {
+ if (or_next_if_better && i+1 == GetSize(rules.matches) && vi+1 == GetSize(rules.brams.at(match.name)))
+ log_error("Found 'or_next_if_better' in last match rule.\n");
+
+ if (!replace_cell(cell, rules, bram, match, match_properties, 1)) {
+ log(" Mapping to bram type %s failed.\n", log_id(match.name));
+ failed_brams.insert(pair<IdString, int>(bram.name, bram.variant));
+ goto next_match_rule;
+ }
+
+ log(" Storing for later selection.\n");
+ best_rule_cache[pair<int, int>(i, vi)] = tuple<int, int, int>(match_properties["efficiency"], -match_properties["cells"], -match_properties["acells"]);
+
+ next_match_rule:
+ if (or_next_if_better || best_rule_cache.empty())
+ continue;
+
+ log(" Selecting best of %d rules:\n", GetSize(best_rule_cache));
+ pair<int, int> best_rule = best_rule_cache.begin()->first;
+
+ for (auto &it : best_rule_cache) {
+ if (it.second > best_rule_cache[best_rule])
+ best_rule = it.first;
+ log(" Efficiency for rule %d.%d: efficiency=%d, cells=%d, acells=%d\n", it.first.first+1, it.first.second+1,
+ std::get<0>(it.second), -std::get<1>(it.second), -std::get<2>(it.second));
+ }
+
+ log(" Selected rule %d.%d with efficiency %d.\n", best_rule.first+1, best_rule.second+1, std::get<0>(best_rule_cache[best_rule]));
+ best_rule_cache.clear();
+
+ auto &best_bram = rules.brams.at(rules.matches.at(best_rule.first).name).at(best_rule.second);
+ if (!replace_cell(cell, rules, best_bram, rules.matches.at(best_rule.first), match_properties, 2))
+ log_error("Mapping to bram type %s (variant %d) after pre-selection failed.\n", log_id(best_bram.name), best_bram.variant);
+ return;
+ }
+
+ if (!replace_cell(cell, rules, bram, match, match_properties, 0)) {
+ log(" Mapping to bram type %s failed.\n", log_id(match.name));
+ failed_brams.insert(pair<IdString, int>(bram.name, bram.variant));
+ goto next_match_rule;
+ }
+ return;
+ }
+ }
+
+ log(" No acceptable bram resources found.\n");
+}
+
+struct MemoryBramPass : public Pass {
+ MemoryBramPass() : Pass("memory_bram", "map memories to block rams") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" memory_bram -rules <rule_file> [selection]\n");
+ log("\n");
+ log("This pass converts the multi-port $mem memory cells into block ram instances.\n");
+ log("The given rules file describes the available resources and how they should be\n");
+ log("used.\n");
+ log("\n");
+ log("The rules file contains a set of block ram description and a sequence of match\n");
+ log("rules. A block ram description looks like this:\n");
+ log("\n");
+ log(" bram RAMB1024X32 # name of BRAM cell\n");
+ log(" init 1 # set to '1' if BRAM can be initialized\n");
+ log(" abits 10 # number of address bits\n");
+ log(" dbits 32 # number of data bits\n");
+ log(" groups 2 # number of port groups\n");
+ log(" ports 1 1 # number of ports in each group\n");
+ log(" wrmode 1 0 # set to '1' if this groups is write ports\n");
+ log(" enable 4 1 # number of enable bits\n");
+ log(" transp 0 2 # transparent (for read ports)\n");
+ log(" clocks 1 2 # clock configuration\n");
+ log(" clkpol 2 2 # clock polarity configuration\n");
+ log(" endbram\n");
+ log("\n");
+ log("For the option 'transp' the value 0 means non-transparent, 1 means transparent\n");
+ log("and a value greater than 1 means configurable. All groups with the same\n");
+ log("value greater than 1 share the same configuration bit.\n");
+ log("\n");
+ log("For the option 'clocks' the value 0 means non-clocked, and a value greater\n");
+ log("than 0 means clocked. All groups with the same value share the same clock\n");
+ log("signal.\n");
+ log("\n");
+ log("For the option 'clkpol' the value 0 means negative edge, 1 means positive edge\n");
+ log("and a value greater than 1 means configurable. All groups with the same value\n");
+ log("greater than 1 share the same configuration bit.\n");
+ log("\n");
+ log("Using the same bram name in different bram blocks will create different variants\n");
+ log("of the bram. Verilog configuration parameters for the bram are created as needed.\n");
+ log("\n");
+ log("It is also possible to create variants by repeating statements in the bram block\n");
+ log("and appending '@<label>' to the individual statements.\n");
+ log("\n");
+ log("A match rule looks like this:\n");
+ log("\n");
+ log(" match RAMB1024X32\n");
+ log(" max waste 16384 # only use this bram if <= 16k ram bits are unused\n");
+ log(" min efficiency 80 # only use this bram if efficiency is at least 80%%\n");
+ log(" endmatch\n");
+ log("\n");
+ log("It is possible to match against the following values with min/max rules:\n");
+ log("\n");
+ log(" words ........ number of words in memory in design\n");
+ log(" abits ........ number of address bits on memory in design\n");
+ log(" dbits ........ number of data bits on memory in design\n");
+ log(" wports ....... number of write ports on memory in design\n");
+ log(" rports ....... number of read ports on memory in design\n");
+ log(" ports ........ number of ports on memory in design\n");
+ log(" bits ......... number of bits in memory in design\n");
+ log(" dups .......... number of duplications for more read ports\n");
+ log("\n");
+ log(" awaste ....... number of unused address slots for this match\n");
+ log(" dwaste ....... number of unused data bits for this match\n");
+ log(" bwaste ....... number of unused bram bits for this match\n");
+ log(" waste ........ total number of unused bram bits (bwaste*dups)\n");
+ log(" efficiency ... total percentage of used and non-duplicated bits\n");
+ log("\n");
+ log(" acells ....... number of cells in 'address-direction'\n");
+ log(" dcells ....... number of cells in 'data-direction'\n");
+ log(" cells ........ total number of cells (acells*dcells*dups)\n");
+ log("\n");
+ log("The interface for the created bram instances is derived from the bram\n");
+ log("description. Use 'techmap' to convert the created bram instances into\n");
+ log("instances of the actual bram cells of your target architecture.\n");
+ log("\n");
+ log("A match containing the command 'or_next_if_better' is only used if it\n");
+ log("has a higher efficiency than the next match (and the one after that if\n");
+ log("the next also has 'or_next_if_better' set, and so forth).\n");
+ log("\n");
+ log("A match containing the command 'make_transp' will add external circuitry\n");
+ log("to simulate 'transparent read', if necessary.\n");
+ log("\n");
+ log("A match containing the command 'make_outreg' will add external flip-flops\n");
+ log("to implement synchronous read ports, if necessary.\n");
+ log("\n");
+ log("A match containing the command 'shuffle_enable A' will re-organize\n");
+ log("the data bits to accommodate the enable pattern of port A.\n");
+ log("\n");
+ }
+ virtual void execute(vector<string> args, Design *design)
+ {
+ rules_t rules;
+
+ log_header(design, "Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-rules" && argidx+1 < args.size()) {
+ rules.parse(args[++argidx]);
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto mod : design->selected_modules())
+ for (auto cell : mod->selected_cells())
+ if (cell->type == "$mem")
+ handle_cell(cell, rules);
+ }
+} MemoryBramPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc
new file mode 100644
index 00000000..ab66e3fb
--- /dev/null
+++ b/passes/memory/memory_collect.cc
@@ -0,0 +1,268 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+bool memcells_cmp(Cell *a, Cell *b)
+{
+ if (a->type == "$memrd" && b->type == "$memrd")
+ return a->name < b->name;
+ if (a->type == "$memrd" || b->type == "$memrd")
+ return (a->type == "$memrd") < (b->type == "$memrd");
+ return a->parameters.at("\\PRIORITY").as_int() < b->parameters.at("\\PRIORITY").as_int();
+}
+
+Cell *handle_memory(Module *module, RTLIL::Memory *memory)
+{
+ log("Collecting $memrd, $memwr and $meminit for memory `%s' in module `%s':\n",
+ memory->name.c_str(), module->name.c_str());
+
+ Const init_data(State::Sx, memory->size * memory->width);
+ SigMap sigmap(module);
+
+ int wr_ports = 0;
+ SigSpec sig_wr_clk;
+ SigSpec sig_wr_clk_enable;
+ SigSpec sig_wr_clk_polarity;
+ SigSpec sig_wr_addr;
+ SigSpec sig_wr_data;
+ SigSpec sig_wr_en;
+
+ int rd_ports = 0;
+ SigSpec sig_rd_clk;
+ SigSpec sig_rd_clk_enable;
+ SigSpec sig_rd_clk_polarity;
+ SigSpec sig_rd_transparent;
+ SigSpec sig_rd_addr;
+ SigSpec sig_rd_data;
+ SigSpec sig_rd_en;
+
+ int addr_bits = 0;
+ std::vector<Cell*> memcells;
+
+ for (auto &cell_it : module->cells_) {
+ Cell *cell = cell_it.second;
+ if (cell->type.in("$memrd", "$memwr", "$meminit") && memory->name == cell->parameters["\\MEMID"].decode_string()) {
+ SigSpec addr = sigmap(cell->getPort("\\ADDR"));
+ for (int i = 0; i < GetSize(addr); i++)
+ if (addr[i] != State::S0)
+ addr_bits = std::max(addr_bits, i+1);
+ memcells.push_back(cell);
+ }
+ }
+
+ if (memory->start_offset == 0 && addr_bits < 30 && (1 << addr_bits) < memory->size)
+ memory->size = 1 << addr_bits;
+
+ if (memory->start_offset >= 0)
+ addr_bits = std::min(addr_bits, ceil_log2(memory->size + memory->start_offset));
+
+ addr_bits = std::max(addr_bits, 1);
+
+ if (memcells.empty()) {
+ log(" no cells found. removing memory.\n");
+ return nullptr;
+ }
+
+ std::sort(memcells.begin(), memcells.end(), memcells_cmp);
+
+ for (auto cell : memcells)
+ {
+ log(" %s (%s)\n", log_id(cell), log_id(cell->type));
+
+ if (cell->type == "$meminit")
+ {
+ SigSpec addr = sigmap(cell->getPort("\\ADDR"));
+ SigSpec data = sigmap(cell->getPort("\\DATA"));
+
+ if (!addr.is_fully_const())
+ log_error("Non-constant address %s in memory initialization %s.\n", log_signal(addr), log_id(cell));
+ if (!data.is_fully_const())
+ log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data), log_id(cell));
+
+ int offset = (addr.as_int() - memory->start_offset) * memory->width;
+
+ if (offset < 0 || offset + GetSize(data) > GetSize(init_data))
+ log_warning("Address %s in memory initialization %s is out-of-bounds.\n", log_signal(addr), log_id(cell));
+
+ for (int i = 0; i < GetSize(data); i++)
+ if (0 <= i+offset && i+offset < GetSize(init_data))
+ init_data.bits[i+offset] = data[i].data;
+
+ continue;
+ }
+
+ if (cell->type == "$memwr")
+ {
+ SigSpec clk = sigmap(cell->getPort("\\CLK"));
+ SigSpec clk_enable = SigSpec(cell->parameters["\\CLK_ENABLE"]);
+ SigSpec clk_polarity = SigSpec(cell->parameters["\\CLK_POLARITY"]);
+ SigSpec addr = sigmap(cell->getPort("\\ADDR"));
+ SigSpec data = sigmap(cell->getPort("\\DATA"));
+ SigSpec en = sigmap(cell->getPort("\\EN"));
+
+ if (!en.is_fully_zero())
+ {
+ clk.extend_u0(1, false);
+ clk_enable.extend_u0(1, false);
+ clk_polarity.extend_u0(1, false);
+ addr.extend_u0(addr_bits, false);
+ data.extend_u0(memory->width, false);
+ en.extend_u0(memory->width, false);
+
+ sig_wr_clk.append(clk);
+ sig_wr_clk_enable.append(clk_enable);
+ sig_wr_clk_polarity.append(clk_polarity);
+ sig_wr_addr.append(addr);
+ sig_wr_data.append(data);
+ sig_wr_en.append(en);
+
+ wr_ports++;
+ }
+ continue;
+ }
+
+ if (cell->type == "$memrd")
+ {
+ SigSpec clk = sigmap(cell->getPort("\\CLK"));
+ SigSpec clk_enable = SigSpec(cell->parameters["\\CLK_ENABLE"]);
+ SigSpec clk_polarity = SigSpec(cell->parameters["\\CLK_POLARITY"]);
+ SigSpec transparent = SigSpec(cell->parameters["\\TRANSPARENT"]);
+ SigSpec addr = sigmap(cell->getPort("\\ADDR"));
+ SigSpec data = sigmap(cell->getPort("\\DATA"));
+ SigSpec en = sigmap(cell->getPort("\\EN"));
+
+ if (!en.is_fully_zero())
+ {
+ clk.extend_u0(1, false);
+ clk_enable.extend_u0(1, false);
+ clk_polarity.extend_u0(1, false);
+ transparent.extend_u0(1, false);
+ addr.extend_u0(addr_bits, false);
+ data.extend_u0(memory->width, false);
+
+ sig_rd_clk.append(clk);
+ sig_rd_clk_enable.append(clk_enable);
+ sig_rd_clk_polarity.append(clk_polarity);
+ sig_rd_transparent.append(transparent);
+ sig_rd_addr.append(addr);
+ sig_rd_data.append(data);
+ sig_rd_en.append(en);
+
+ rd_ports++;
+ }
+ continue;
+ }
+ }
+
+ std::stringstream sstr;
+ sstr << "$mem$" << memory->name.str() << "$" << (autoidx++);
+
+ Cell *mem = module->addCell(sstr.str(), "$mem");
+ mem->parameters["\\MEMID"] = Const(memory->name.str());
+ mem->parameters["\\WIDTH"] = Const(memory->width);
+ 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);
+ log_assert(sig_wr_clk_enable.size() == wr_ports && sig_wr_clk_enable.is_fully_const());
+ log_assert(sig_wr_clk_polarity.size() == wr_ports && sig_wr_clk_polarity.is_fully_const());
+ log_assert(sig_wr_addr.size() == wr_ports * addr_bits);
+ log_assert(sig_wr_data.size() == wr_ports * memory->width);
+ log_assert(sig_wr_en.size() == wr_ports * memory->width);
+
+ mem->parameters["\\WR_PORTS"] = Const(wr_ports);
+ mem->parameters["\\WR_CLK_ENABLE"] = wr_ports ? sig_wr_clk_enable.as_const() : Const(0, 1);
+ mem->parameters["\\WR_CLK_POLARITY"] = wr_ports ? sig_wr_clk_polarity.as_const() : Const(0, 1);
+
+ mem->setPort("\\WR_CLK", sig_wr_clk);
+ mem->setPort("\\WR_ADDR", sig_wr_addr);
+ mem->setPort("\\WR_DATA", sig_wr_data);
+ mem->setPort("\\WR_EN", sig_wr_en);
+
+ log_assert(sig_rd_clk.size() == rd_ports);
+ log_assert(sig_rd_clk_enable.size() == rd_ports && sig_rd_clk_enable.is_fully_const());
+ log_assert(sig_rd_clk_polarity.size() == rd_ports && sig_rd_clk_polarity.is_fully_const());
+ log_assert(sig_rd_addr.size() == rd_ports * addr_bits);
+ log_assert(sig_rd_data.size() == rd_ports * memory->width);
+
+ mem->parameters["\\RD_PORTS"] = Const(rd_ports);
+ mem->parameters["\\RD_CLK_ENABLE"] = rd_ports ? sig_rd_clk_enable.as_const() : Const(0, 1);
+ mem->parameters["\\RD_CLK_POLARITY"] = rd_ports ? sig_rd_clk_polarity.as_const() : Const(0, 1);
+ mem->parameters["\\RD_TRANSPARENT"] = rd_ports ? sig_rd_transparent.as_const() : Const(0, 1);
+
+ mem->setPort("\\RD_CLK", sig_rd_clk);
+ mem->setPort("\\RD_ADDR", sig_rd_addr);
+ mem->setPort("\\RD_DATA", sig_rd_data);
+ mem->setPort("\\RD_EN", sig_rd_en);
+
+ for (auto c : memcells)
+ module->remove(c);
+
+ return mem;
+}
+
+static void handle_module(Design *design, Module *module)
+{
+ std::vector<pair<Cell*, IdString>> finqueue;
+
+ for (auto &mem_it : module->memories)
+ if (design->selected(module, mem_it.second)) {
+ Cell *c = handle_memory(module, mem_it.second);
+ finqueue.push_back(pair<Cell*, IdString>(c, mem_it.first));
+ }
+ for (auto &it : finqueue) {
+ delete module->memories.at(it.second);
+ module->memories.erase(it.second);
+ if (it.first)
+ module->rename(it.first, it.second);
+ }
+}
+
+struct MemoryCollectPass : public Pass {
+ MemoryCollectPass() : Pass("memory_collect", "creating multi-port memory cells") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" memory_collect [selection]\n");
+ log("\n");
+ log("This pass collects memories and memory ports and creates generic multiport\n");
+ log("memory cells.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
+ log_header(design, "Executing MEMORY_COLLECT pass (generating $mem cells).\n");
+ extra_args(args, 1, design);
+ for (auto &mod_it : design->modules_)
+ if (design->selected(mod_it.second))
+ handle_module(design, mod_it.second);
+ }
+} MemoryCollectPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc
new file mode 100644
index 00000000..40691d16
--- /dev/null
+++ b/passes/memory/memory_dff.cc
@@ -0,0 +1,305 @@
+/*
+ * 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 MemoryDffWorker
+{
+ Module *module;
+ SigMap sigmap;
+
+ vector<Cell*> dff_cells;
+ dict<SigBit, SigBit> invbits;
+ dict<SigBit, int> sigbit_users_count;
+ dict<SigSpec, Cell*> mux_cells_a, mux_cells_b;
+ pool<Cell*> forward_merged_dffs, candidate_dffs;
+
+ MemoryDffWorker(Module *module) : module(module), sigmap(module) { }
+
+ bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false)
+ {
+ sigmap.apply(sig);
+
+ for (auto &bit : sig)
+ {
+ if (bit.wire == NULL)
+ continue;
+
+ for (auto cell : dff_cells)
+ {
+ if (after && forward_merged_dffs.count(cell))
+ continue;
+
+ SigSpec this_clk = cell->getPort("\\CLK");
+ bool this_clk_polarity = cell->parameters["\\CLK_POLARITY"].as_bool();
+
+ if (invbits.count(this_clk)) {
+ this_clk = invbits.at(this_clk);
+ this_clk_polarity = !this_clk_polarity;
+ }
+
+ if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
+ if (this_clk != clk)
+ continue;
+ if (this_clk_polarity != clk_polarity)
+ continue;
+ }
+
+ RTLIL::SigSpec q_norm = cell->getPort(after ? "\\D" : "\\Q");
+ sigmap.apply(q_norm);
+
+ RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(after ? "\\Q" : "\\D"));
+ if (d.size() != 1)
+ continue;
+
+ bit = d;
+ clk = this_clk;
+ clk_polarity = this_clk_polarity;
+ candidate_dffs.insert(cell);
+ goto replaced_this_bit;
+ }
+
+ return false;
+ replaced_this_bit:;
+ }
+
+ return true;
+ }
+
+ void handle_wr_cell(RTLIL::Cell *cell)
+ {
+ log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());
+
+ RTLIL::SigSpec clk = RTLIL::SigSpec(RTLIL::State::Sx);
+ bool clk_polarity = 0;
+ candidate_dffs.clear();
+
+ RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR");
+ if (!find_sig_before_dff(sig_addr, clk, clk_polarity)) {
+ log("no (compatible) $dff for address input found.\n");
+ return;
+ }
+
+ RTLIL::SigSpec sig_data = cell->getPort("\\DATA");
+ if (!find_sig_before_dff(sig_data, clk, clk_polarity)) {
+ log("no (compatible) $dff for data input found.\n");
+ return;
+ }
+
+ RTLIL::SigSpec sig_en = cell->getPort("\\EN");
+ if (!find_sig_before_dff(sig_en, clk, clk_polarity)) {
+ log("no (compatible) $dff for enable input found.\n");
+ return;
+ }
+
+ if (clk != RTLIL::SigSpec(RTLIL::State::Sx))
+ {
+ for (auto cell : candidate_dffs)
+ forward_merged_dffs.insert(cell);
+
+ cell->setPort("\\CLK", clk);
+ cell->setPort("\\ADDR", sig_addr);
+ cell->setPort("\\DATA", sig_data);
+ cell->setPort("\\EN", sig_en);
+ cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
+ cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
+
+ log("merged $dff to cell.\n");
+ return;
+ }
+
+ log("no (compatible) $dff found.\n");
+ }
+
+ void disconnect_dff(RTLIL::SigSpec sig)
+ {
+ sigmap.apply(sig);
+ sig.sort_and_unify();
+
+ std::stringstream sstr;
+ sstr << "$memory_dff_disconnected$" << (autoidx++);
+
+ RTLIL::SigSpec new_sig = module->addWire(sstr.str(), sig.size());
+
+ for (auto cell : module->cells())
+ if (cell->type == "$dff") {
+ RTLIL::SigSpec new_q = cell->getPort("\\Q");
+ new_q.replace(sig, new_sig);
+ cell->setPort("\\Q", new_q);
+ }
+ }
+
+ void handle_rd_cell(RTLIL::Cell *cell)
+ {
+ log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());
+
+ bool clk_polarity = 0;
+
+ RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx);
+ RTLIL::SigSpec sig_data = cell->getPort("\\DATA");
+
+ for (auto bit : sigmap(sig_data))
+ if (sigbit_users_count[bit] > 1)
+ goto skip_ff_after_read_merging;
+
+ if (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data))
+ {
+ bool enable_invert = mux_cells_a.count(sig_data) != 0;
+ Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data);
+ SigSpec check_q = sigmap(mux->getPort(enable_invert ? "\\B" : "\\A"));
+
+ sig_data = sigmap(mux->getPort("\\Y"));
+ for (auto bit : sig_data)
+ if (sigbit_users_count[bit] > 1)
+ goto skip_ff_after_read_merging;
+
+ if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) && sig_data == check_q)
+ {
+ disconnect_dff(sig_data);
+ cell->setPort("\\CLK", clk_data);
+ cell->setPort("\\EN", enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S"));
+ cell->setPort("\\DATA", sig_data);
+ cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
+ cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
+ cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
+ log("merged data $dff with rd enable to cell.\n");
+ return;
+ }
+ }
+ else
+ {
+ if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx))
+ {
+ disconnect_dff(sig_data);
+ cell->setPort("\\CLK", clk_data);
+ cell->setPort("\\EN", State::S1);
+ cell->setPort("\\DATA", sig_data);
+ cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
+ cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
+ cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
+ log("merged data $dff to cell.\n");
+ return;
+ }
+ }
+
+ skip_ff_after_read_merging:;
+ RTLIL::SigSpec clk_addr = RTLIL::SigSpec(RTLIL::State::Sx);
+ RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR");
+ if (find_sig_before_dff(sig_addr, clk_addr, clk_polarity) &&
+ clk_addr != RTLIL::SigSpec(RTLIL::State::Sx))
+ {
+ cell->setPort("\\CLK", clk_addr);
+ cell->setPort("\\EN", State::S1);
+ cell->setPort("\\ADDR", sig_addr);
+ cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
+ cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
+ cell->parameters["\\TRANSPARENT"] = RTLIL::Const(1);
+ log("merged address $dff to cell.\n");
+ return;
+ }
+
+ log("no (compatible) $dff found.\n");
+ }
+
+ void run(bool flag_wr_only)
+ {
+ for (auto wire : module->wires()) {
+ if (wire->port_output)
+ for (auto bit : sigmap(wire))
+ sigbit_users_count[bit]++;
+ }
+
+ for (auto cell : module->cells()) {
+ if (cell->type == "$dff")
+ dff_cells.push_back(cell);
+ if (cell->type == "$mux") {
+ mux_cells_a[sigmap(cell->getPort("\\A"))] = cell;
+ mux_cells_b[sigmap(cell->getPort("\\B"))] = cell;
+ }
+ if (cell->type == "$not" || cell->type == "$_NOT_" || (cell->type == "$logic_not" && GetSize(cell->getPort("\\A")) == 1)) {
+ SigSpec sig_a = cell->getPort("\\A");
+ SigSpec sig_y = cell->getPort("\\Y");
+ if (cell->type == "$not")
+ sig_a.extend_u0(GetSize(sig_y), cell->getParam("\\A_SIGNED").as_bool());
+ if (cell->type == "$logic_not")
+ sig_y.extend_u0(1);
+ for (int i = 0; i < GetSize(sig_y); i++)
+ invbits[sig_y[i]] = sig_a[i];
+ }
+ for (auto &conn : cell->connections())
+ if (!cell->known() || cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ sigbit_users_count[bit]++;
+ }
+
+ for (auto cell : module->selected_cells())
+ if (cell->type == "$memwr" && !cell->parameters["\\CLK_ENABLE"].as_bool())
+ handle_wr_cell(cell);
+
+ if (!flag_wr_only)
+ for (auto cell : module->selected_cells())
+ if (cell->type == "$memrd" && !cell->parameters["\\CLK_ENABLE"].as_bool())
+ handle_rd_cell(cell);
+ }
+};
+
+struct MemoryDffPass : public Pass {
+ MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memories") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" memory_dff [options] [selection]\n");
+ log("\n");
+ log("This pass detects DFFs at memory ports and merges them into the memory port.\n");
+ log("I.e. it consumes an asynchronous memory port and the flip-flops at its\n");
+ log("interface and yields a synchronous memory port.\n");
+ log("\n");
+ log(" -nordfff\n");
+ log(" do not merge registers on read ports\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool flag_wr_only = false;
+
+ log_header(design, "Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-nordff" || args[argidx] == "-wr_only") {
+ flag_wr_only = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto mod : design->selected_modules()) {
+ MemoryDffWorker worker(mod);
+ worker.run(flag_wr_only);
+ }
+ }
+} MemoryDffPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/memory/memory_map.cc b/passes/memory/memory_map.cc
new file mode 100644
index 00000000..bffeec85
--- /dev/null
+++ b/passes/memory/memory_map.cc
@@ -0,0 +1,373 @@
+/*
+ * 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/register.h"
+#include "kernel/log.h"
+#include <sstream>
+#include <set>
+#include <stdlib.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct MemoryMapWorker
+{
+ RTLIL::Design *design;
+ RTLIL::Module *module;
+
+ std::map<std::pair<RTLIL::SigSpec, RTLIL::SigSpec>, RTLIL::SigBit> decoder_cache;
+
+ std::string genid(RTLIL::IdString name, std::string token1 = "", int i = -1, std::string token2 = "", int j = -1, std::string token3 = "", int k = -1, std::string token4 = "")
+ {
+ std::stringstream sstr;
+ sstr << "$memory" << name.str() << token1;
+
+ if (i >= 0)
+ sstr << "[" << i << "]";
+
+ sstr << token2;
+
+ if (j >= 0)
+ sstr << "[" << j << "]";
+
+ sstr << token3;
+
+ if (k >= 0)
+ sstr << "[" << k << "]";
+
+ sstr << token4 << "$" << (autoidx++);
+ return sstr.str();
+ }
+
+ RTLIL::Wire *addr_decode(RTLIL::SigSpec addr_sig, RTLIL::SigSpec addr_val)
+ {
+ std::pair<RTLIL::SigSpec, RTLIL::SigSpec> key(addr_sig, addr_val);
+ log_assert(GetSize(addr_sig) == GetSize(addr_val));
+
+ if (decoder_cache.count(key) == 0) {
+ if (GetSize(addr_sig) < 2) {
+ decoder_cache[key] = module->Eq(NEW_ID, addr_sig, addr_val);
+ } else {
+ int split_at = GetSize(addr_sig) / 2;
+ RTLIL::SigBit left_eq = addr_decode(addr_sig.extract(0, split_at), addr_val.extract(0, split_at));
+ RTLIL::SigBit right_eq = addr_decode(addr_sig.extract(split_at, GetSize(addr_sig) - split_at), addr_val.extract(split_at, GetSize(addr_val) - split_at));
+ decoder_cache[key] = module->And(NEW_ID, left_eq, right_eq);
+ }
+ }
+
+ RTLIL::SigBit bit = decoder_cache.at(key);
+ log_assert(bit.wire != nullptr && GetSize(bit.wire) == 1);
+ return bit.wire;
+ }
+
+ void handle_cell(RTLIL::Cell *cell)
+ {
+ std::set<int> static_ports;
+ std::map<int, RTLIL::SigSpec> static_cells_map;
+
+ int wr_ports = cell->parameters["\\WR_PORTS"].as_int();
+ int rd_ports = cell->parameters["\\RD_PORTS"].as_int();
+
+ int mem_size = cell->parameters["\\SIZE"].as_int();
+ int mem_width = cell->parameters["\\WIDTH"].as_int();
+ int mem_offset = cell->parameters["\\OFFSET"].as_int();
+ int mem_abits = cell->parameters["\\ABITS"].as_int();
+
+ SigSpec init_data = cell->getParam("\\INIT");
+ init_data.extend_u0(mem_size*mem_width, true);
+
+ // delete unused memory cell
+ if (wr_ports == 0 && rd_ports == 0) {
+ module->remove(cell);
+ return;
+ }
+
+ // all write ports must share the same clock
+ RTLIL::SigSpec clocks = cell->getPort("\\WR_CLK");
+ RTLIL::Const clocks_pol = cell->parameters["\\WR_CLK_POLARITY"];
+ RTLIL::Const clocks_en = cell->parameters["\\WR_CLK_ENABLE"];
+ clocks_pol.bits.resize(wr_ports);
+ clocks_en.bits.resize(wr_ports);
+ RTLIL::SigSpec refclock;
+ RTLIL::State refclock_pol = RTLIL::State::Sx;
+ for (int i = 0; i < clocks.size(); i++) {
+ RTLIL::SigSpec wr_en = cell->getPort("\\WR_EN").extract(i * mem_width, mem_width);
+ if (wr_en.is_fully_const() && !wr_en.as_bool()) {
+ static_ports.insert(i);
+ continue;
+ }
+ if (clocks_en.bits[i] != RTLIL::State::S1) {
+ RTLIL::SigSpec wr_addr = cell->getPort("\\WR_ADDR").extract(i*mem_abits, mem_abits);
+ RTLIL::SigSpec wr_data = cell->getPort("\\WR_DATA").extract(i*mem_width, mem_width);
+ if (wr_addr.is_fully_const()) {
+ // FIXME: Actually we should check for wr_en.is_fully_const() also and
+ // create a $adff cell with this ports wr_en input as reset pin when wr_en
+ // is not a simple static 1.
+ static_cells_map[wr_addr.as_int() - mem_offset] = wr_data;
+ static_ports.insert(i);
+ continue;
+ }
+ log("Not mapping memory cell %s in module %s (write port %d has no clock).\n",
+ cell->name.c_str(), module->name.c_str(), i);
+ return;
+ }
+ if (refclock.size() == 0) {
+ refclock = clocks.extract(i, 1);
+ refclock_pol = clocks_pol.bits[i];
+ }
+ if (clocks.extract(i, 1) != refclock || clocks_pol.bits[i] != refclock_pol) {
+ log("Not mapping memory cell %s in module %s (write clock %d is incompatible with other clocks).\n",
+ cell->name.c_str(), module->name.c_str(), i);
+ return;
+ }
+ }
+
+ log("Mapping memory cell %s in module %s:\n", cell->name.c_str(), module->name.c_str());
+
+ std::vector<RTLIL::SigSpec> data_reg_in;
+ std::vector<RTLIL::SigSpec> data_reg_out;
+
+ int count_static = 0;
+
+ for (int i = 0; i < mem_size; i++)
+ {
+ if (static_cells_map.count(i) > 0)
+ {
+ data_reg_in.push_back(RTLIL::SigSpec(RTLIL::State::Sz, mem_width));
+ data_reg_out.push_back(static_cells_map[i]);
+ count_static++;
+ }
+ else
+ {
+ RTLIL::Cell *c = module->addCell(genid(cell->name, "", i), "$dff");
+ c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"];
+ if (clocks_pol.bits.size() > 0) {
+ c->parameters["\\CLK_POLARITY"] = RTLIL::Const(clocks_pol.bits[0]);
+ c->setPort("\\CLK", clocks.extract(0, 1));
+ } else {
+ c->parameters["\\CLK_POLARITY"] = RTLIL::Const(RTLIL::State::S1);
+ c->setPort("\\CLK", RTLIL::SigSpec(RTLIL::State::S0));
+ }
+
+ RTLIL::Wire *w_in = module->addWire(genid(cell->name, "", i, "$d"), mem_width);
+ data_reg_in.push_back(RTLIL::SigSpec(w_in));
+ c->setPort("\\D", data_reg_in.back());
+
+ std::string w_out_name = stringf("%s[%d]", cell->parameters["\\MEMID"].decode_string().c_str(), i);
+ if (module->wires_.count(w_out_name) > 0)
+ w_out_name = genid(cell->name, "", i, "$q");
+
+ RTLIL::Wire *w_out = module->addWire(w_out_name, mem_width);
+ SigSpec w_init = init_data.extract(i*mem_width, mem_width);
+
+ if (!w_init.is_fully_undef())
+ w_out->attributes["\\init"] = w_init.as_const();
+
+ data_reg_out.push_back(RTLIL::SigSpec(w_out));
+ c->setPort("\\Q", data_reg_out.back());
+ }
+ }
+
+ log(" created %d $dff cells and %d static cells of width %d.\n", mem_size-count_static, count_static, mem_width);
+
+ int count_dff = 0, count_mux = 0, count_wrmux = 0;
+
+ for (int i = 0; i < cell->parameters["\\RD_PORTS"].as_int(); i++)
+ {
+ RTLIL::SigSpec rd_addr = cell->getPort("\\RD_ADDR").extract(i*mem_abits, mem_abits);
+
+ if (mem_offset)
+ rd_addr = module->Sub(NEW_ID, rd_addr, SigSpec(mem_offset, GetSize(rd_addr)));
+
+ std::vector<RTLIL::SigSpec> rd_signals;
+ rd_signals.push_back(cell->getPort("\\RD_DATA").extract(i*mem_width, mem_width));
+
+ if (cell->parameters["\\RD_CLK_ENABLE"].bits[i] == RTLIL::State::S1)
+ {
+ RTLIL::Cell *dff_cell = nullptr;
+
+ if (cell->parameters["\\RD_TRANSPARENT"].bits[i] == RTLIL::State::S1)
+ {
+ dff_cell = module->addCell(genid(cell->name, "$rdreg", i), "$dff");
+ dff_cell->parameters["\\WIDTH"] = RTLIL::Const(mem_abits);
+ dff_cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]);
+ dff_cell->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1));
+ dff_cell->setPort("\\D", rd_addr);
+ count_dff++;
+
+ RTLIL::Wire *w = module->addWire(genid(cell->name, "$rdreg", i, "$q"), mem_abits);
+
+ dff_cell->setPort("\\Q", RTLIL::SigSpec(w));
+ rd_addr = RTLIL::SigSpec(w);
+ }
+ else
+ {
+ dff_cell = module->addCell(genid(cell->name, "$rdreg", i), "$dff");
+ dff_cell->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"];
+ dff_cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]);
+ dff_cell->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1));
+ dff_cell->setPort("\\Q", rd_signals.back());
+ count_dff++;
+
+ RTLIL::Wire *w = module->addWire(genid(cell->name, "$rdreg", i, "$d"), mem_width);
+
+ rd_signals.clear();
+ rd_signals.push_back(RTLIL::SigSpec(w));
+ dff_cell->setPort("\\D", rd_signals.back());
+ }
+
+ SigBit en_bit = cell->getPort("\\RD_EN").extract(i);
+ if (en_bit != State::S1) {
+ SigSpec new_d = module->Mux(genid(cell->name, "$rdenmux", i),
+ dff_cell->getPort("\\Q"), dff_cell->getPort("\\D"), en_bit);
+ dff_cell->setPort("\\D", new_d);
+ }
+ }
+
+ for (int j = 0; j < mem_abits; j++)
+ {
+ std::vector<RTLIL::SigSpec> next_rd_signals;
+
+ for (size_t k = 0; k < rd_signals.size(); k++)
+ {
+ RTLIL::Cell *c = module->addCell(genid(cell->name, "$rdmux", i, "", j, "", k), "$mux");
+ c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"];
+ c->setPort("\\Y", rd_signals[k]);
+ c->setPort("\\S", rd_addr.extract(mem_abits-j-1, 1));
+ count_mux++;
+
+ c->setPort("\\A", module->addWire(genid(cell->name, "$rdmux", i, "", j, "", k, "$a"), mem_width));
+ c->setPort("\\B", module->addWire(genid(cell->name, "$rdmux", i, "", j, "", k, "$b"), mem_width));
+
+ next_rd_signals.push_back(c->getPort("\\A"));
+ next_rd_signals.push_back(c->getPort("\\B"));
+ }
+
+ next_rd_signals.swap(rd_signals);
+ }
+
+ for (int j = 0; j < mem_size; j++)
+ module->connect(RTLIL::SigSig(rd_signals[j], data_reg_out[j]));
+ }
+
+ log(" read interface: %d $dff and %d $mux cells.\n", count_dff, count_mux);
+
+ for (int i = 0; i < mem_size; i++)
+ {
+ if (static_cells_map.count(i) > 0)
+ continue;
+
+ RTLIL::SigSpec sig = data_reg_out[i];
+
+ for (int j = 0; j < cell->parameters["\\WR_PORTS"].as_int(); j++)
+ {
+ RTLIL::SigSpec wr_addr = cell->getPort("\\WR_ADDR").extract(j*mem_abits, mem_abits);
+ RTLIL::SigSpec wr_data = cell->getPort("\\WR_DATA").extract(j*mem_width, mem_width);
+ RTLIL::SigSpec wr_en = cell->getPort("\\WR_EN").extract(j*mem_width, mem_width);
+
+ if (mem_offset)
+ wr_addr = module->Sub(NEW_ID, wr_addr, SigSpec(mem_offset, GetSize(wr_addr)));
+
+ RTLIL::Wire *w_seladdr = addr_decode(wr_addr, RTLIL::SigSpec(i, mem_abits));
+
+ int wr_offset = 0;
+ while (wr_offset < wr_en.size())
+ {
+ int wr_width = 1;
+ RTLIL::SigSpec wr_bit = wr_en.extract(wr_offset, 1);
+
+ while (wr_offset + wr_width < wr_en.size()) {
+ RTLIL::SigSpec next_wr_bit = wr_en.extract(wr_offset + wr_width, 1);
+ if (next_wr_bit != wr_bit)
+ break;
+ wr_width++;
+ }
+
+ RTLIL::Wire *w = w_seladdr;
+
+ if (wr_bit != RTLIL::SigSpec(1, 1))
+ {
+ RTLIL::Cell *c = module->addCell(genid(cell->name, "$wren", i, "", j, "", wr_offset), "$and");
+ c->parameters["\\A_SIGNED"] = RTLIL::Const(0);
+ c->parameters["\\B_SIGNED"] = RTLIL::Const(0);
+ c->parameters["\\A_WIDTH"] = RTLIL::Const(1);
+ c->parameters["\\B_WIDTH"] = RTLIL::Const(1);
+ c->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
+ c->setPort("\\A", w);
+ c->setPort("\\B", wr_bit);
+
+ w = module->addWire(genid(cell->name, "$wren", i, "", j, "", wr_offset, "$y"));
+ c->setPort("\\Y", RTLIL::SigSpec(w));
+ }
+
+ RTLIL::Cell *c = module->addCell(genid(cell->name, "$wrmux", i, "", j, "", wr_offset), "$mux");
+ c->parameters["\\WIDTH"] = wr_width;
+ c->setPort("\\A", sig.extract(wr_offset, wr_width));
+ c->setPort("\\B", wr_data.extract(wr_offset, wr_width));
+ c->setPort("\\S", RTLIL::SigSpec(w));
+
+ w = module->addWire(genid(cell->name, "$wrmux", i, "", j, "", wr_offset, "$y"), wr_width);
+ c->setPort("\\Y", w);
+
+ sig.replace(wr_offset, w);
+ wr_offset += wr_width;
+ count_wrmux++;
+ }
+ }
+
+ module->connect(RTLIL::SigSig(data_reg_in[i], sig));
+ }
+
+ log(" write interface: %d write mux blocks.\n", count_wrmux);
+
+ module->remove(cell);
+ }
+
+ MemoryMapWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module)
+ {
+ std::vector<RTLIL::Cell*> cells;
+ for (auto cell : module->selected_cells())
+ if (cell->type == "$mem" && design->selected(module, cell))
+ cells.push_back(cell);
+ for (auto cell : cells)
+ handle_cell(cell);
+ }
+};
+
+struct MemoryMapPass : public Pass {
+ MemoryMapPass() : Pass("memory_map", "translate multiport memories to basic cells") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" memory_map [selection]\n");
+ log("\n");
+ log("This pass converts multiport memory cells as generated by the memory_collect\n");
+ log("pass to word-wide DFFs and address decoders.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
+ log_header(design, "Executing MEMORY_MAP pass (converting $mem cells to logic and flip-flops).\n");
+ extra_args(args, 1, design);
+ for (auto mod : design->selected_modules())
+ MemoryMapWorker(design, mod);
+ }
+} MemoryMapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/memory/memory_memx.cc b/passes/memory/memory_memx.cc
new file mode 100644
index 00000000..2b02e249
--- /dev/null
+++ b/passes/memory/memory_memx.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/register.h"
+#include "kernel/log.h"
+#include <sstream>
+#include <set>
+#include <stdlib.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct MemoryMemxPass : public Pass {
+ MemoryMemxPass() : Pass("memory_memx", "emulate vlog sim behavior for mem ports") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" memory_memx [selection]\n");
+ log("\n");
+ log("This pass adds additional circuitry that emulates the Verilog simulation\n");
+ log("behavior for out-of-bounds memory reads and writes.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
+ log_header(design, "Executing MEMORY_MEMX pass (converting $mem cells to logic and flip-flops).\n");
+ extra_args(args, 1, design);
+
+ for (auto module : design->selected_modules())
+ {
+ vector<Cell*> mem_port_cells;
+
+ for (auto cell : module->selected_cells())
+ if (cell->type.in("$memrd", "$memwr"))
+ mem_port_cells.push_back(cell);
+
+ for (auto cell : mem_port_cells)
+ {
+ IdString memid = cell->getParam("\\MEMID").decode_string();
+ RTLIL::Memory *mem = module->memories.at(memid);
+
+ int lowest_addr = mem->start_offset;
+ int highest_addr = mem->start_offset + mem->size - 1;
+
+ SigSpec addr = cell->getPort("\\ADDR");
+ addr.extend_u0(32);
+
+ SigSpec addr_ok = module->Nex(NEW_ID, module->ReduceXor(NEW_ID, addr), module->ReduceXor(NEW_ID, {addr, State::S1}));
+ if (lowest_addr != 0)
+ addr_ok = module->LogicAnd(NEW_ID, addr_ok, module->Ge(NEW_ID, addr, lowest_addr));
+ addr_ok = module->LogicAnd(NEW_ID, addr_ok, module->Le(NEW_ID, addr, highest_addr));
+
+ if (cell->type == "$memrd")
+ {
+ if (cell->getParam("\\CLK_ENABLE").as_bool())
+ log_error("Cell %s.%s (%s) has an enabled clock. Clocked $memrd cells are not supported by memory_memx!\n",
+ log_id(module), log_id(cell), log_id(cell->type));
+
+ SigSpec rdata = cell->getPort("\\DATA");
+ Wire *raw_rdata = module->addWire(NEW_ID, GetSize(rdata));
+ module->addMux(NEW_ID, SigSpec(State::Sx, GetSize(rdata)), raw_rdata, addr_ok, rdata);
+ cell->setPort("\\DATA", raw_rdata);
+ }
+
+ if (cell->type == "$memwr")
+ {
+ SigSpec en = cell->getPort("\\EN");
+ en = module->And(NEW_ID, en, addr_ok.repeat(GetSize(en)));
+ cell->setPort("\\EN", en);
+ }
+ }
+ }
+ }
+} MemoryMemxPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/memory/memory_share.cc b/passes/memory/memory_share.cc
new file mode 100644
index 00000000..ca09ac52
--- /dev/null
+++ b/passes/memory/memory_share.cc
@@ -0,0 +1,763 @@
+/*
+ * 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/satgen.h"
+#include "kernel/sigtools.h"
+#include "kernel/modtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b)
+{
+ if (a->type == "$memrd" && b->type == "$memrd")
+ return a->name < b->name;
+ if (a->type == "$memrd" || b->type == "$memrd")
+ return (a->type == "$memrd") < (b->type == "$memrd");
+ return a->parameters.at("\\PRIORITY").as_int() < b->parameters.at("\\PRIORITY").as_int();
+}
+
+struct MemoryShareWorker
+{
+ RTLIL::Design *design;
+ RTLIL::Module *module;
+ SigMap sigmap, sigmap_xmux;
+ ModWalker modwalker;
+ CellTypes cone_ct;
+
+ std::map<RTLIL::SigBit, std::pair<RTLIL::Cell*, int>> sig_to_mux;
+ std::map<pair<std::set<std::map<SigBit, bool>>, SigBit>, SigBit> conditions_logic_cache;
+
+
+ // -----------------------------------------------------------------
+ // Converting feedbacks to async read ports to proper enable signals
+ // -----------------------------------------------------------------
+
+ bool find_data_feedback(const std::set<RTLIL::SigBit> &async_rd_bits, RTLIL::SigBit sig,
+ std::map<RTLIL::SigBit, bool> &state, std::set<std::map<RTLIL::SigBit, bool>> &conditions)
+ {
+ if (async_rd_bits.count(sig)) {
+ conditions.insert(state);
+ return true;
+ }
+
+ if (sig_to_mux.count(sig) == 0)
+ return false;
+
+ RTLIL::Cell *cell = sig_to_mux.at(sig).first;
+ int bit_idx = sig_to_mux.at(sig).second;
+
+ std::vector<RTLIL::SigBit> sig_a = sigmap(cell->getPort("\\A"));
+ std::vector<RTLIL::SigBit> sig_b = sigmap(cell->getPort("\\B"));
+ std::vector<RTLIL::SigBit> sig_s = sigmap(cell->getPort("\\S"));
+ std::vector<RTLIL::SigBit> sig_y = sigmap(cell->getPort("\\Y"));
+ log_assert(sig_y.at(bit_idx) == sig);
+
+ for (int i = 0; i < int(sig_s.size()); i++)
+ if (state.count(sig_s[i]) && state.at(sig_s[i]) == true) {
+ if (find_data_feedback(async_rd_bits, sig_b.at(bit_idx + i*sig_y.size()), state, conditions)) {
+ RTLIL::SigSpec new_b = cell->getPort("\\B");
+ new_b.replace(bit_idx + i*sig_y.size(), RTLIL::State::Sx);
+ cell->setPort("\\B", new_b);
+ }
+ return false;
+ }
+
+
+ for (int i = 0; i < int(sig_s.size()); i++)
+ {
+ if (state.count(sig_s[i]) && state.at(sig_s[i]) == false)
+ continue;
+
+ std::map<RTLIL::SigBit, bool> new_state = state;
+ new_state[sig_s[i]] = true;
+
+ if (find_data_feedback(async_rd_bits, sig_b.at(bit_idx + i*sig_y.size()), new_state, conditions)) {
+ RTLIL::SigSpec new_b = cell->getPort("\\B");
+ new_b.replace(bit_idx + i*sig_y.size(), RTLIL::State::Sx);
+ cell->setPort("\\B", new_b);
+ }
+ }
+
+ std::map<RTLIL::SigBit, bool> new_state = state;
+ for (int i = 0; i < int(sig_s.size()); i++)
+ new_state[sig_s[i]] = false;
+
+ if (find_data_feedback(async_rd_bits, sig_a.at(bit_idx), new_state, conditions)) {
+ RTLIL::SigSpec new_a = cell->getPort("\\A");
+ new_a.replace(bit_idx, RTLIL::State::Sx);
+ cell->setPort("\\A", new_a);
+ }
+
+ return false;
+ }
+
+ RTLIL::SigBit conditions_to_logic(std::set<std::map<RTLIL::SigBit, bool>> &conditions, SigBit olden, int &created_conditions)
+ {
+ auto key = make_pair(conditions, olden);
+
+ if (conditions_logic_cache.count(key))
+ return conditions_logic_cache.at(key);
+
+ RTLIL::SigSpec terms;
+ for (auto &cond : conditions) {
+ RTLIL::SigSpec sig1, sig2;
+ for (auto &it : cond) {
+ sig1.append_bit(it.first);
+ sig2.append_bit(it.second ? RTLIL::State::S1 : RTLIL::State::S0);
+ }
+ terms.append(module->Ne(NEW_ID, sig1, sig2));
+ created_conditions++;
+ }
+
+ if (olden.wire != nullptr || olden != State::S1)
+ terms.append(olden);
+
+ if (GetSize(terms) == 0)
+ terms = State::S1;
+
+ if (GetSize(terms) > 1)
+ terms = module->ReduceAnd(NEW_ID, terms);
+
+ return conditions_logic_cache[key] = terms;
+ }
+
+ void translate_rd_feedback_to_en(std::string memid, std::vector<RTLIL::Cell*> &rd_ports, std::vector<RTLIL::Cell*> &wr_ports)
+ {
+ std::map<RTLIL::SigSpec, std::vector<std::set<RTLIL::SigBit>>> async_rd_bits;
+ std::map<RTLIL::SigBit, std::set<RTLIL::SigBit>> muxtree_upstream_map;
+ std::set<RTLIL::SigBit> non_feedback_nets;
+
+ for (auto wire : module->wires())
+ if (wire->port_output) {
+ std::vector<RTLIL::SigBit> bits = sigmap(wire);
+ non_feedback_nets.insert(bits.begin(), bits.end());
+ }
+
+ for (auto cell : module->cells())
+ {
+ bool ignore_data_port = false;
+
+ if (cell->type == "$mux" || cell->type == "$pmux")
+ {
+ std::vector<RTLIL::SigBit> sig_a = sigmap(cell->getPort("\\A"));
+ std::vector<RTLIL::SigBit> sig_b = sigmap(cell->getPort("\\B"));
+ std::vector<RTLIL::SigBit> sig_s = sigmap(cell->getPort("\\S"));
+ std::vector<RTLIL::SigBit> sig_y = sigmap(cell->getPort("\\Y"));
+
+ non_feedback_nets.insert(sig_s.begin(), sig_s.end());
+
+ for (int i = 0; i < int(sig_y.size()); i++) {
+ muxtree_upstream_map[sig_y[i]].insert(sig_a[i]);
+ for (int j = 0; j < int(sig_s.size()); j++)
+ muxtree_upstream_map[sig_y[i]].insert(sig_b[i + j*sig_y.size()]);
+ }
+
+ continue;
+ }
+
+ if ((cell->type == "$memwr" || cell->type == "$memrd") &&
+ cell->parameters.at("\\MEMID").decode_string() == memid)
+ ignore_data_port = true;
+
+ for (auto conn : cell->connections())
+ {
+ if (ignore_data_port && conn.first == "\\DATA")
+ continue;
+ std::vector<RTLIL::SigBit> bits = sigmap(conn.second);
+ non_feedback_nets.insert(bits.begin(), bits.end());
+ }
+ }
+
+ std::set<RTLIL::SigBit> expand_non_feedback_nets = non_feedback_nets;
+ while (!expand_non_feedback_nets.empty())
+ {
+ std::set<RTLIL::SigBit> new_expand_non_feedback_nets;
+
+ for (auto &bit : expand_non_feedback_nets)
+ if (muxtree_upstream_map.count(bit))
+ for (auto &new_bit : muxtree_upstream_map.at(bit))
+ if (!non_feedback_nets.count(new_bit)) {
+ non_feedback_nets.insert(new_bit);
+ new_expand_non_feedback_nets.insert(new_bit);
+ }
+
+ expand_non_feedback_nets.swap(new_expand_non_feedback_nets);
+ }
+
+ for (auto cell : rd_ports)
+ {
+ if (cell->parameters.at("\\CLK_ENABLE").as_bool())
+ continue;
+
+ RTLIL::SigSpec sig_addr = sigmap(cell->getPort("\\ADDR"));
+ std::vector<RTLIL::SigBit> sig_data = sigmap(cell->getPort("\\DATA"));
+
+ for (int i = 0; i < int(sig_data.size()); i++)
+ if (non_feedback_nets.count(sig_data[i]))
+ goto not_pure_feedback_port;
+
+ async_rd_bits[sig_addr].resize(max(async_rd_bits.size(), sig_data.size()));
+ for (int i = 0; i < int(sig_data.size()); i++)
+ async_rd_bits[sig_addr][i].insert(sig_data[i]);
+
+ not_pure_feedback_port:;
+ }
+
+ if (async_rd_bits.empty())
+ return;
+
+ log("Populating enable bits on write ports of memory %s.%s with aync read feedback:\n", log_id(module), log_id(memid));
+
+ for (auto cell : wr_ports)
+ {
+ RTLIL::SigSpec sig_addr = sigmap_xmux(cell->getPort("\\ADDR"));
+ if (!async_rd_bits.count(sig_addr))
+ continue;
+
+ log(" Analyzing write port %s.\n", log_id(cell));
+
+ std::vector<RTLIL::SigBit> cell_data = cell->getPort("\\DATA");
+ std::vector<RTLIL::SigBit> cell_en = cell->getPort("\\EN");
+
+ int created_conditions = 0;
+ for (int i = 0; i < int(cell_data.size()); i++)
+ if (cell_en[i] != RTLIL::SigBit(RTLIL::State::S0))
+ {
+ std::map<RTLIL::SigBit, bool> state;
+ std::set<std::map<RTLIL::SigBit, bool>> conditions;
+
+ find_data_feedback(async_rd_bits.at(sig_addr).at(i), cell_data[i], state, conditions);
+ cell_en[i] = conditions_to_logic(conditions, cell_en[i], created_conditions);
+ }
+
+ if (created_conditions) {
+ log(" Added enable logic for %d different cases.\n", created_conditions);
+ cell->setPort("\\EN", cell_en);
+ }
+ }
+ }
+
+
+ // ------------------------------------------------------
+ // Consolidate write ports that write to the same address
+ // ------------------------------------------------------
+
+ RTLIL::SigSpec mask_en_naive(RTLIL::SigSpec do_mask, RTLIL::SigSpec bits, RTLIL::SigSpec mask_bits)
+ {
+ // this is the naive version of the function that does not care about grouping the EN bits.
+
+ RTLIL::SigSpec inv_mask_bits = module->Not(NEW_ID, mask_bits);
+ RTLIL::SigSpec inv_mask_bits_filtered = module->Mux(NEW_ID, RTLIL::SigSpec(RTLIL::State::S1, bits.size()), inv_mask_bits, do_mask);
+ RTLIL::SigSpec result = module->And(NEW_ID, inv_mask_bits_filtered, bits);
+ return result;
+ }
+
+ RTLIL::SigSpec mask_en_grouped(RTLIL::SigSpec do_mask, RTLIL::SigSpec bits, RTLIL::SigSpec mask_bits)
+ {
+ // this version of the function preserves the bit grouping in the EN bits.
+
+ std::vector<RTLIL::SigBit> v_bits = bits;
+ std::vector<RTLIL::SigBit> v_mask_bits = mask_bits;
+
+ std::map<std::pair<RTLIL::SigBit, RTLIL::SigBit>, std::pair<int, std::vector<int>>> groups;
+ RTLIL::SigSpec grouped_bits, grouped_mask_bits;
+
+ for (int i = 0; i < bits.size(); i++) {
+ std::pair<RTLIL::SigBit, RTLIL::SigBit> key(v_bits[i], v_mask_bits[i]);
+ if (groups.count(key) == 0) {
+ groups[key].first = grouped_bits.size();
+ grouped_bits.append_bit(v_bits[i]);
+ grouped_mask_bits.append_bit(v_mask_bits[i]);
+ }
+ groups[key].second.push_back(i);
+ }
+
+ std::vector<RTLIL::SigBit> grouped_result = mask_en_naive(do_mask, grouped_bits, grouped_mask_bits);
+ RTLIL::SigSpec result;
+
+ for (int i = 0; i < bits.size(); i++) {
+ std::pair<RTLIL::SigBit, RTLIL::SigBit> key(v_bits[i], v_mask_bits[i]);
+ result.append_bit(grouped_result.at(groups.at(key).first));
+ }
+
+ return result;
+ }
+
+ void merge_en_data(RTLIL::SigSpec &merged_en, RTLIL::SigSpec &merged_data, RTLIL::SigSpec next_en, RTLIL::SigSpec next_data)
+ {
+ std::vector<RTLIL::SigBit> v_old_en = merged_en;
+ std::vector<RTLIL::SigBit> v_next_en = next_en;
+
+ // The new merged_en signal is just the old merged_en signal and next_en OR'ed together.
+ // But of course we need to preserve the bit grouping..
+
+ std::map<std::pair<RTLIL::SigBit, RTLIL::SigBit>, int> groups;
+ std::vector<RTLIL::SigBit> grouped_old_en, grouped_next_en;
+ RTLIL::SigSpec new_merged_en;
+
+ for (int i = 0; i < int(v_old_en.size()); i++) {
+ std::pair<RTLIL::SigBit, RTLIL::SigBit> key(v_old_en[i], v_next_en[i]);
+ if (groups.count(key) == 0) {
+ groups[key] = grouped_old_en.size();
+ grouped_old_en.push_back(key.first);
+ grouped_next_en.push_back(key.second);
+ }
+ }
+
+ std::vector<RTLIL::SigBit> grouped_new_en = module->Or(NEW_ID, grouped_old_en, grouped_next_en);
+
+ for (int i = 0; i < int(v_old_en.size()); i++) {
+ std::pair<RTLIL::SigBit, RTLIL::SigBit> key(v_old_en[i], v_next_en[i]);
+ new_merged_en.append_bit(grouped_new_en.at(groups.at(key)));
+ }
+
+ // Create the new merged_data signal.
+
+ RTLIL::SigSpec new_merged_data(RTLIL::State::Sx, merged_data.size());
+
+ RTLIL::SigSpec old_data_set = module->And(NEW_ID, merged_en, merged_data);
+ RTLIL::SigSpec old_data_unset = module->And(NEW_ID, merged_en, module->Not(NEW_ID, merged_data));
+
+ RTLIL::SigSpec new_data_set = module->And(NEW_ID, next_en, next_data);
+ RTLIL::SigSpec new_data_unset = module->And(NEW_ID, next_en, module->Not(NEW_ID, next_data));
+
+ new_merged_data = module->Or(NEW_ID, new_merged_data, old_data_set);
+ new_merged_data = module->And(NEW_ID, new_merged_data, module->Not(NEW_ID, old_data_unset));
+
+ new_merged_data = module->Or(NEW_ID, new_merged_data, new_data_set);
+ new_merged_data = module->And(NEW_ID, new_merged_data, module->Not(NEW_ID, new_data_unset));
+
+ // Update merged_* signals
+
+ merged_en = new_merged_en;
+ merged_data = new_merged_data;
+ }
+
+ void consolidate_wr_by_addr(std::string memid, std::vector<RTLIL::Cell*> &wr_ports)
+ {
+ if (wr_ports.size() <= 1)
+ return;
+
+ log("Consolidating write ports of memory %s.%s by address:\n", log_id(module), log_id(memid));
+
+ std::map<RTLIL::SigSpec, int> last_port_by_addr;
+ std::vector<std::vector<bool>> active_bits_on_port;
+
+ bool cache_clk_enable = false;
+ bool cache_clk_polarity = false;
+ RTLIL::SigSpec cache_clk;
+
+ for (int i = 0; i < int(wr_ports.size()); i++)
+ {
+ RTLIL::Cell *cell = wr_ports.at(i);
+ RTLIL::SigSpec addr = sigmap_xmux(cell->getPort("\\ADDR"));
+
+ if (cell->parameters.at("\\CLK_ENABLE").as_bool() != cache_clk_enable ||
+ (cache_clk_enable && (sigmap(cell->getPort("\\CLK")) != cache_clk ||
+ cell->parameters.at("\\CLK_POLARITY").as_bool() != cache_clk_polarity)))
+ {
+ cache_clk_enable = cell->parameters.at("\\CLK_ENABLE").as_bool();
+ cache_clk_polarity = cell->parameters.at("\\CLK_POLARITY").as_bool();
+ cache_clk = sigmap(cell->getPort("\\CLK"));
+ last_port_by_addr.clear();
+
+ if (cache_clk_enable)
+ log(" New clock domain: %s %s\n", cache_clk_polarity ? "posedge" : "negedge", log_signal(cache_clk));
+ else
+ log(" New clock domain: unclocked\n");
+ }
+
+ log(" Port %d (%s) has addr %s.\n", i, log_id(cell), log_signal(addr));
+
+ log(" Active bits: ");
+ std::vector<RTLIL::SigBit> en_bits = sigmap(cell->getPort("\\EN"));
+ active_bits_on_port.push_back(std::vector<bool>(en_bits.size()));
+ for (int k = int(en_bits.size())-1; k >= 0; k--) {
+ active_bits_on_port[i][k] = en_bits[k].wire != NULL || en_bits[k].data != RTLIL::State::S0;
+ log("%c", active_bits_on_port[i][k] ? '1' : '0');
+ }
+ log("\n");
+
+ if (last_port_by_addr.count(addr))
+ {
+ int last_i = last_port_by_addr.at(addr);
+ log(" Merging port %d into this one.\n", last_i);
+
+ bool found_overlapping_bits = false;
+ for (int k = 0; k < int(en_bits.size()); k++) {
+ if (active_bits_on_port[i][k] && active_bits_on_port[last_i][k])
+ found_overlapping_bits = true;
+ active_bits_on_port[i][k] = active_bits_on_port[i][k] || active_bits_on_port[last_i][k];
+ }
+
+ // Force this ports addr input to addr directly (skip don't care muxes)
+
+ cell->setPort("\\ADDR", addr);
+
+ // If any of the ports between `last_i' and `i' write to the same address, this
+ // will have priority over whatever `last_i` wrote. So we need to revisit those
+ // ports and mask the EN bits accordingly.
+
+ RTLIL::SigSpec merged_en = sigmap(wr_ports[last_i]->getPort("\\EN"));
+
+ for (int j = last_i+1; j < i; j++)
+ {
+ if (wr_ports[j] == NULL)
+ continue;
+
+ for (int k = 0; k < int(en_bits.size()); k++)
+ if (active_bits_on_port[i][k] && active_bits_on_port[j][k])
+ goto found_overlapping_bits_i_j;
+
+ if (0) {
+ found_overlapping_bits_i_j:
+ log(" Creating collosion-detect logic for port %d.\n", j);
+ RTLIL::SigSpec is_same_addr = module->addWire(NEW_ID);
+ module->addEq(NEW_ID, addr, wr_ports[j]->getPort("\\ADDR"), is_same_addr);
+ merged_en = mask_en_grouped(is_same_addr, merged_en, sigmap(wr_ports[j]->getPort("\\EN")));
+ }
+ }
+
+ // Then we need to merge the (masked) EN and the DATA signals.
+
+ RTLIL::SigSpec merged_data = wr_ports[last_i]->getPort("\\DATA");
+ if (found_overlapping_bits) {
+ log(" Creating logic for merging DATA and EN ports.\n");
+ merge_en_data(merged_en, merged_data, sigmap(cell->getPort("\\EN")), sigmap(cell->getPort("\\DATA")));
+ } else {
+ RTLIL::SigSpec cell_en = sigmap(cell->getPort("\\EN"));
+ RTLIL::SigSpec cell_data = sigmap(cell->getPort("\\DATA"));
+ for (int k = 0; k < int(en_bits.size()); k++)
+ if (!active_bits_on_port[last_i][k]) {
+ merged_en.replace(k, cell_en.extract(k, 1));
+ merged_data.replace(k, cell_data.extract(k, 1));
+ }
+ }
+
+ // Connect the new EN and DATA signals and remove the old write port.
+
+ cell->setPort("\\EN", merged_en);
+ cell->setPort("\\DATA", merged_data);
+
+ module->remove(wr_ports[last_i]);
+ wr_ports[last_i] = NULL;
+
+ log(" Active bits: ");
+ std::vector<RTLIL::SigBit> en_bits = sigmap(cell->getPort("\\EN"));
+ active_bits_on_port.push_back(std::vector<bool>(en_bits.size()));
+ for (int k = int(en_bits.size())-1; k >= 0; k--)
+ log("%c", active_bits_on_port[i][k] ? '1' : '0');
+ log("\n");
+ }
+
+ last_port_by_addr[addr] = i;
+ }
+
+ // Clean up `wr_ports': remove all NULL entries
+
+ std::vector<RTLIL::Cell*> wr_ports_with_nulls;
+ wr_ports_with_nulls.swap(wr_ports);
+
+ for (auto cell : wr_ports_with_nulls)
+ if (cell != NULL)
+ wr_ports.push_back(cell);
+ }
+
+
+ // --------------------------------------------------------
+ // Consolidate write ports using sat-based resource sharing
+ // --------------------------------------------------------
+
+ void consolidate_wr_using_sat(std::string memid, std::vector<RTLIL::Cell*> &wr_ports)
+ {
+ if (wr_ports.size() <= 1)
+ return;
+
+ ezSatPtr ez;
+ SatGen satgen(ez.get(), &modwalker.sigmap);
+
+ // find list of considered ports and port pairs
+
+ std::set<int> considered_ports;
+ std::set<int> considered_port_pairs;
+
+ for (int i = 0; i < int(wr_ports.size()); i++) {
+ std::vector<RTLIL::SigBit> bits = modwalker.sigmap(wr_ports[i]->getPort("\\EN"));
+ for (auto bit : bits)
+ if (bit == RTLIL::State::S1)
+ goto port_is_always_active;
+ if (modwalker.has_drivers(bits))
+ considered_ports.insert(i);
+ port_is_always_active:;
+ }
+
+ log("Consolidating write ports of memory %s.%s using sat-based resource sharing:\n", log_id(module), log_id(memid));
+
+ bool cache_clk_enable = false;
+ bool cache_clk_polarity = false;
+ RTLIL::SigSpec cache_clk;
+
+ for (int i = 0; i < int(wr_ports.size()); i++)
+ {
+ RTLIL::Cell *cell = wr_ports.at(i);
+
+ if (cell->parameters.at("\\CLK_ENABLE").as_bool() != cache_clk_enable ||
+ (cache_clk_enable && (sigmap(cell->getPort("\\CLK")) != cache_clk ||
+ cell->parameters.at("\\CLK_POLARITY").as_bool() != cache_clk_polarity)))
+ {
+ cache_clk_enable = cell->parameters.at("\\CLK_ENABLE").as_bool();
+ cache_clk_polarity = cell->parameters.at("\\CLK_POLARITY").as_bool();
+ cache_clk = sigmap(cell->getPort("\\CLK"));
+ }
+ else if (i > 0 && considered_ports.count(i-1) && considered_ports.count(i))
+ considered_port_pairs.insert(i);
+
+ if (cache_clk_enable)
+ log(" Port %d (%s) on %s %s: %s\n", i, log_id(cell),
+ cache_clk_polarity ? "posedge" : "negedge", log_signal(cache_clk),
+ considered_ports.count(i) ? "considered" : "not considered");
+ else
+ log(" Port %d (%s) unclocked: %s\n", i, log_id(cell),
+ considered_ports.count(i) ? "considered" : "not considered");
+ }
+
+ if (considered_port_pairs.size() < 1) {
+ log(" No two subsequent ports in same clock domain considered -> nothing to consolidate.\n");
+ return;
+ }
+
+ // create SAT representation of common input cone of all considered EN signals
+
+ pool<Wire*> one_hot_wires;
+ std::set<RTLIL::Cell*> sat_cells;
+ std::set<RTLIL::SigBit> bits_queue;
+ std::map<int, int> port_to_sat_variable;
+
+ for (int i = 0; i < int(wr_ports.size()); i++)
+ if (considered_port_pairs.count(i) || considered_port_pairs.count(i+1))
+ {
+ RTLIL::SigSpec sig = modwalker.sigmap(wr_ports[i]->getPort("\\EN"));
+ port_to_sat_variable[i] = ez->expression(ez->OpOr, satgen.importSigSpec(sig));
+
+ std::vector<RTLIL::SigBit> bits = sig;
+ bits_queue.insert(bits.begin(), bits.end());
+ }
+
+ while (!bits_queue.empty())
+ {
+ for (auto bit : bits_queue)
+ if (bit.wire && bit.wire->get_bool_attribute("\\onehot"))
+ one_hot_wires.insert(bit.wire);
+
+ pool<ModWalker::PortBit> portbits;
+ modwalker.get_drivers(portbits, bits_queue);
+ bits_queue.clear();
+
+ for (auto &pbit : portbits)
+ if (sat_cells.count(pbit.cell) == 0 && cone_ct.cell_known(pbit.cell->type)) {
+ pool<RTLIL::SigBit> &cell_inputs = modwalker.cell_inputs[pbit.cell];
+ bits_queue.insert(cell_inputs.begin(), cell_inputs.end());
+ sat_cells.insert(pbit.cell);
+ }
+ }
+
+ for (auto wire : one_hot_wires) {
+ log(" Adding one-hot constraint for wire %s.\n", log_id(wire));
+ vector<int> ez_wire_bits = satgen.importSigSpec(wire);
+ for (int i : ez_wire_bits)
+ for (int j : ez_wire_bits)
+ if (i != j) ez->assume(ez->NOT(i), j);
+ }
+
+ log(" Common input cone for all EN signals: %d cells.\n", int(sat_cells.size()));
+
+ for (auto cell : sat_cells)
+ satgen.importCell(cell);
+
+ log(" Size of unconstrained SAT problem: %d variables, %d clauses\n", ez->numCnfVariables(), ez->numCnfClauses());
+
+ // merge subsequent ports if possible
+
+ for (int i = 0; i < int(wr_ports.size()); i++)
+ {
+ if (!considered_port_pairs.count(i))
+ continue;
+
+ if (ez->solve(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i))) {
+ log(" According to SAT solver sharing of port %d with port %d is not possible.\n", i-1, i);
+ continue;
+ }
+
+ log(" Merging port %d into port %d.\n", i-1, i);
+ port_to_sat_variable.at(i) = ez->OR(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i));
+
+ RTLIL::SigSpec last_addr = wr_ports[i-1]->getPort("\\ADDR");
+ RTLIL::SigSpec last_data = wr_ports[i-1]->getPort("\\DATA");
+ std::vector<RTLIL::SigBit> last_en = modwalker.sigmap(wr_ports[i-1]->getPort("\\EN"));
+
+ RTLIL::SigSpec this_addr = wr_ports[i]->getPort("\\ADDR");
+ RTLIL::SigSpec this_data = wr_ports[i]->getPort("\\DATA");
+ std::vector<RTLIL::SigBit> this_en = modwalker.sigmap(wr_ports[i]->getPort("\\EN"));
+
+ RTLIL::SigBit this_en_active = module->ReduceOr(NEW_ID, this_en);
+
+ if (GetSize(last_addr) < GetSize(this_addr))
+ last_addr.extend_u0(GetSize(this_addr));
+ else
+ this_addr.extend_u0(GetSize(last_addr));
+
+ wr_ports[i]->setParam("\\ABITS", GetSize(this_addr));
+ wr_ports[i]->setPort("\\ADDR", module->Mux(NEW_ID, last_addr, this_addr, this_en_active));
+ wr_ports[i]->setPort("\\DATA", module->Mux(NEW_ID, last_data, this_data, this_en_active));
+
+ std::map<std::pair<RTLIL::SigBit, RTLIL::SigBit>, int> groups_en;
+ RTLIL::SigSpec grouped_last_en, grouped_this_en, en;
+ RTLIL::Wire *grouped_en = module->addWire(NEW_ID, 0);
+
+ for (int j = 0; j < int(this_en.size()); j++) {
+ std::pair<RTLIL::SigBit, RTLIL::SigBit> key(last_en[j], this_en[j]);
+ if (!groups_en.count(key)) {
+ grouped_last_en.append_bit(last_en[j]);
+ grouped_this_en.append_bit(this_en[j]);
+ groups_en[key] = grouped_en->width;
+ grouped_en->width++;
+ }
+ en.append(RTLIL::SigSpec(grouped_en, groups_en[key]));
+ }
+
+ module->addMux(NEW_ID, grouped_last_en, grouped_this_en, this_en_active, grouped_en);
+ wr_ports[i]->setPort("\\EN", en);
+
+ module->remove(wr_ports[i-1]);
+ wr_ports[i-1] = NULL;
+ }
+
+ // Clean up `wr_ports': remove all NULL entries
+
+ std::vector<RTLIL::Cell*> wr_ports_with_nulls;
+ wr_ports_with_nulls.swap(wr_ports);
+
+ for (auto cell : wr_ports_with_nulls)
+ if (cell != NULL)
+ wr_ports.push_back(cell);
+ }
+
+
+ // -------------
+ // Setup and run
+ // -------------
+
+ MemoryShareWorker(RTLIL::Design *design, RTLIL::Module *module) :
+ design(design), module(module), sigmap(module)
+ {
+ std::map<std::string, std::pair<std::vector<RTLIL::Cell*>, std::vector<RTLIL::Cell*>>> memindex;
+
+ sigmap_xmux = sigmap;
+ for (auto cell : module->cells())
+ {
+ if (cell->type == "$memrd")
+ memindex[cell->parameters.at("\\MEMID").decode_string()].first.push_back(cell);
+
+ if (cell->type == "$memwr")
+ memindex[cell->parameters.at("\\MEMID").decode_string()].second.push_back(cell);
+
+ if (cell->type == "$mux")
+ {
+ RTLIL::SigSpec sig_a = sigmap_xmux(cell->getPort("\\A"));
+ RTLIL::SigSpec sig_b = sigmap_xmux(cell->getPort("\\B"));
+
+ if (sig_a.is_fully_undef())
+ sigmap_xmux.add(cell->getPort("\\Y"), sig_b);
+ else if (sig_b.is_fully_undef())
+ sigmap_xmux.add(cell->getPort("\\Y"), sig_a);
+ }
+
+ if (cell->type == "$mux" || cell->type == "$pmux")
+ {
+ std::vector<RTLIL::SigBit> sig_y = sigmap(cell->getPort("\\Y"));
+ for (int i = 0; i < int(sig_y.size()); i++)
+ sig_to_mux[sig_y[i]] = std::pair<RTLIL::Cell*, int>(cell, i);
+ }
+ }
+
+ for (auto &it : memindex) {
+ std::sort(it.second.first.begin(), it.second.first.end(), memcells_cmp);
+ std::sort(it.second.second.begin(), it.second.second.end(), memcells_cmp);
+ translate_rd_feedback_to_en(it.first, it.second.first, it.second.second);
+ consolidate_wr_by_addr(it.first, it.second.second);
+ }
+
+ cone_ct.setup_internals();
+ cone_ct.cell_types.erase("$mul");
+ cone_ct.cell_types.erase("$mod");
+ cone_ct.cell_types.erase("$div");
+ cone_ct.cell_types.erase("$pow");
+ cone_ct.cell_types.erase("$shl");
+ cone_ct.cell_types.erase("$shr");
+ cone_ct.cell_types.erase("$sshl");
+ cone_ct.cell_types.erase("$sshr");
+ cone_ct.cell_types.erase("$shift");
+ cone_ct.cell_types.erase("$shiftx");
+
+ modwalker.setup(design, module, &cone_ct);
+
+ for (auto &it : memindex)
+ consolidate_wr_using_sat(it.first, it.second.second);
+ }
+};
+
+struct MemorySharePass : public Pass {
+ MemorySharePass() : Pass("memory_share", "consolidate memory ports") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" memory_share [selection]\n");
+ log("\n");
+ log("This pass merges share-able memory ports into single memory ports.\n");
+ log("\n");
+ log("The following methods are used to consolidate the number of memory ports:\n");
+ log("\n");
+ log(" - When write ports are connected to async read ports accessing the same\n");
+ log(" address, then this feedback path is converted to a write port with\n");
+ log(" byte/part enable signals.\n");
+ log("\n");
+ log(" - When multiple write ports access the same address then this is converted\n");
+ log(" to a single write port with a more complex data and/or enable logic path.\n");
+ log("\n");
+ log(" - When multiple write ports are never accessed at the same time (a SAT\n");
+ log(" solver is used to determine this), then the ports are merged into a single\n");
+ log(" write port.\n");
+ log("\n");
+ log("Note that in addition to the algorithms implemented in this pass, the $memrd\n");
+ log("and $memwr cells are also subject to generic resource sharing passes (and other\n");
+ log("optimizations) such as \"share\" and \"opt_merge\".\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
+ log_header(design, "Executing MEMORY_SHARE pass (consolidating $memrd/$memwr cells).\n");
+ extra_args(args, 1, design);
+ for (auto module : design->selected_modules())
+ MemoryShareWorker(design, module);
+ }
+} MemorySharePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/memory/memory_unpack.cc b/passes/memory/memory_unpack.cc
new file mode 100644
index 00000000..a0fc31b5
--- /dev/null
+++ b/passes/memory/memory_unpack.cc
@@ -0,0 +1,149 @@
+/*
+ * 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/register.h"
+#include "kernel/log.h"
+#include <sstream>
+#include <algorithm>
+#include <stdlib.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void handle_memory(RTLIL::Module *module, RTLIL::Cell *memory)
+{
+ log("Creating $memrd and $memwr for memory `%s' in module `%s':\n",
+ memory->name.c_str(), module->name.c_str());
+
+ RTLIL::IdString mem_name = RTLIL::escape_id(memory->parameters.at("\\MEMID").decode_string());
+
+ while (module->memories.count(mem_name) != 0)
+ mem_name = mem_name.str() + stringf("_%d", autoidx++);
+
+ RTLIL::Memory *mem = new RTLIL::Memory;
+ mem->name = mem_name;
+ mem->width = memory->parameters.at("\\WIDTH").as_int();
+ mem->start_offset = memory->parameters.at("\\OFFSET").as_int();
+ mem->size = memory->parameters.at("\\SIZE").as_int();
+ module->memories[mem_name] = mem;
+
+ int abits = memory->parameters.at("\\ABITS").as_int();
+ int num_rd_ports = memory->parameters.at("\\RD_PORTS").as_int();
+ int num_wr_ports = memory->parameters.at("\\WR_PORTS").as_int();
+
+ for (int i = 0; i < num_rd_ports; i++)
+ {
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$memrd");
+ cell->parameters["\\MEMID"] = mem_name.str();
+ cell->parameters["\\ABITS"] = memory->parameters.at("\\ABITS");
+ cell->parameters["\\WIDTH"] = memory->parameters.at("\\WIDTH");
+ cell->parameters["\\CLK_ENABLE"] = RTLIL::SigSpec(memory->parameters.at("\\RD_CLK_ENABLE")).extract(i, 1).as_const();
+ cell->parameters["\\CLK_POLARITY"] = RTLIL::SigSpec(memory->parameters.at("\\RD_CLK_POLARITY")).extract(i, 1).as_const();
+ cell->parameters["\\TRANSPARENT"] = RTLIL::SigSpec(memory->parameters.at("\\RD_TRANSPARENT")).extract(i, 1).as_const();
+ cell->setPort("\\CLK", memory->getPort("\\RD_CLK").extract(i, 1));
+ cell->setPort("\\EN", memory->getPort("\\RD_EN").extract(i, 1));
+ cell->setPort("\\ADDR", memory->getPort("\\RD_ADDR").extract(i*abits, abits));
+ cell->setPort("\\DATA", memory->getPort("\\RD_DATA").extract(i*mem->width, mem->width));
+ }
+
+ for (int i = 0; i < num_wr_ports; i++)
+ {
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$memwr");
+ cell->parameters["\\MEMID"] = mem_name.str();
+ cell->parameters["\\ABITS"] = memory->parameters.at("\\ABITS");
+ cell->parameters["\\WIDTH"] = memory->parameters.at("\\WIDTH");
+ cell->parameters["\\CLK_ENABLE"] = RTLIL::SigSpec(memory->parameters.at("\\WR_CLK_ENABLE")).extract(i, 1).as_const();
+ cell->parameters["\\CLK_POLARITY"] = RTLIL::SigSpec(memory->parameters.at("\\WR_CLK_POLARITY")).extract(i, 1).as_const();
+ cell->parameters["\\PRIORITY"] = i;
+ cell->setPort("\\CLK", memory->getPort("\\WR_CLK").extract(i, 1));
+ cell->setPort("\\EN", memory->getPort("\\WR_EN").extract(i*mem->width, mem->width));
+ cell->setPort("\\ADDR", memory->getPort("\\WR_ADDR").extract(i*abits, abits));
+ cell->setPort("\\DATA", memory->getPort("\\WR_DATA").extract(i*mem->width, mem->width));
+ }
+
+ Const initval = memory->parameters.at("\\INIT");
+ RTLIL::Cell *last_init_cell = nullptr;
+ SigSpec last_init_data;
+ int last_init_addr=0;
+
+ for (int i = 0; i < GetSize(initval) && i/mem->width < (1 << abits); i += mem->width) {
+ Const val = initval.extract(i, mem->width, State::Sx);
+ for (auto bit : val.bits)
+ if (bit != State::Sx)
+ goto found_non_undef_initval;
+ continue;
+ found_non_undef_initval:
+ if (last_init_cell && last_init_addr+1 == i/mem->width) {
+ last_init_cell->parameters["\\WORDS"] = last_init_cell->parameters["\\WORDS"].as_int() + 1;
+ last_init_data.append(val);
+ last_init_addr++;
+ } else {
+ if (last_init_cell)
+ last_init_cell->setPort("\\DATA", last_init_data);
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$meminit");
+ cell->parameters["\\MEMID"] = mem_name.str();
+ cell->parameters["\\ABITS"] = memory->parameters.at("\\ABITS");
+ cell->parameters["\\WIDTH"] = memory->parameters.at("\\WIDTH");
+ cell->parameters["\\WORDS"] = 1;
+ cell->parameters["\\PRIORITY"] = i/mem->width;
+ cell->setPort("\\ADDR", SigSpec(i/mem->width, abits));
+ last_init_cell = cell;
+ last_init_addr = i/mem->width;
+ last_init_data = val;
+ }
+ }
+
+ if (last_init_cell)
+ last_init_cell->setPort("\\DATA", last_init_data);
+
+ module->remove(memory);
+}
+
+void handle_module(RTLIL::Design *design, RTLIL::Module *module)
+{
+ std::vector<RTLIL::IdString> memcells;
+ for (auto &cell_it : module->cells_)
+ if (cell_it.second->type == "$mem" && design->selected(module, cell_it.second))
+ memcells.push_back(cell_it.first);
+ for (auto &it : memcells)
+ handle_memory(module, module->cells_.at(it));
+}
+
+struct MemoryUnpackPass : public Pass {
+ MemoryUnpackPass() : Pass("memory_unpack", "unpack multi-port memory cells") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" memory_unpack [selection]\n");
+ log("\n");
+ log("This pass converts the multi-port $mem memory cells into individual $memrd and\n");
+ log("$memwr cells. It is the counterpart to the memory_collect pass.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
+ log_header(design, "Executing MEMORY_UNPACK pass (generating $memrd/$memwr cells form $mem cells).\n");
+ extra_args(args, 1, design);
+ for (auto &mod_it : design->modules_)
+ if (design->selected(mod_it.second))
+ handle_module(design, mod_it.second);
+ }
+} MemoryUnpackPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc
new file mode 100644
index 00000000..a8b1537b
--- /dev/null
+++ b/passes/opt/Makefile.inc
@@ -0,0 +1,14 @@
+
+OBJS += passes/opt/opt.o
+OBJS += passes/opt/opt_merge.o
+OBJS += passes/opt/opt_muxtree.o
+OBJS += passes/opt/opt_reduce.o
+OBJS += passes/opt/opt_rmdff.o
+OBJS += passes/opt/opt_clean.o
+OBJS += passes/opt/opt_expr.o
+
+ifneq ($(SMALL),1)
+OBJS += passes/opt/share.o
+OBJS += passes/opt/wreduce.o
+endif
+
diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc
new file mode 100644
index 00000000..021c1a03
--- /dev/null
+++ b/passes/opt/opt.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/register.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct OptPass : public Pass {
+ OptPass() : Pass("opt", "perform simple optimizations") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" opt [options] [selection]\n");
+ log("\n");
+ log("This pass calls all the other opt_* passes in a useful order. This performs\n");
+ log("a series of trivial optimizations and cleanups. This pass executes the other\n");
+ log("passes in the following order:\n");
+ log("\n");
+ log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
+ log(" opt_merge [-share_all] -nomux\n");
+ log("\n");
+ log(" do\n");
+ log(" opt_muxtree\n");
+ log(" opt_reduce [-fine] [-full]\n");
+ log(" opt_merge [-share_all]\n");
+ log(" opt_rmdff [-keepdc]\n");
+ log(" opt_clean [-purge]\n");
+ log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
+ log(" while <changed design>\n");
+ log("\n");
+ log("When called with -fast the following script is used instead:\n");
+ log("\n");
+ log(" do\n");
+ log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
+ log(" opt_merge [-share_all]\n");
+ log(" opt_rmdff [-keepdc]\n");
+ log(" opt_clean [-purge]\n");
+ log(" while <changed design in opt_rmdff>\n");
+ log("\n");
+ log("Note: Options in square brackets (such as [-keepdc]) are passed through to\n");
+ log("the opt_* commands when given to 'opt'.\n");
+ log("\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::string opt_clean_args;
+ std::string opt_expr_args;
+ std::string opt_reduce_args;
+ std::string opt_merge_args;
+ std::string opt_rmdff_args;
+ bool fast_mode = false;
+
+ log_header(design, "Executing OPT pass (performing simple optimizations).\n");
+ log_push();
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-purge") {
+ opt_clean_args += " -purge";
+ continue;
+ }
+ if (args[argidx] == "-mux_undef") {
+ opt_expr_args += " -mux_undef";
+ continue;
+ }
+ if (args[argidx] == "-mux_bool") {
+ opt_expr_args += " -mux_bool";
+ continue;
+ }
+ if (args[argidx] == "-undriven") {
+ opt_expr_args += " -undriven";
+ continue;
+ }
+ if (args[argidx] == "-clkinv") {
+ opt_expr_args += " -clkinv";
+ continue;
+ }
+ if (args[argidx] == "-fine") {
+ opt_expr_args += " -fine";
+ opt_reduce_args += " -fine";
+ continue;
+ }
+ if (args[argidx] == "-full") {
+ opt_expr_args += " -full";
+ opt_reduce_args += " -full";
+ continue;
+ }
+ if (args[argidx] == "-keepdc") {
+ opt_expr_args += " -keepdc";
+ opt_rmdff_args += " -keepdc";
+ continue;
+ }
+ if (args[argidx] == "-share_all") {
+ opt_merge_args += " -share_all";
+ continue;
+ }
+ if (args[argidx] == "-fast") {
+ fast_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (fast_mode)
+ {
+ while (1) {
+ Pass::call(design, "opt_expr" + opt_expr_args);
+ Pass::call(design, "opt_merge" + opt_merge_args);
+ design->scratchpad_unset("opt.did_something");
+ Pass::call(design, "opt_rmdff" + opt_rmdff_args);
+ if (design->scratchpad_get_bool("opt.did_something") == false)
+ break;
+ Pass::call(design, "opt_clean" + opt_clean_args);
+ log_header(design, "Rerunning OPT passes. (Removed registers in this run.)\n");
+ }
+ Pass::call(design, "opt_clean" + opt_clean_args);
+ }
+ else
+ {
+ Pass::call(design, "opt_expr" + opt_expr_args);
+ Pass::call(design, "opt_merge -nomux" + opt_merge_args);
+ while (1) {
+ design->scratchpad_unset("opt.did_something");
+ Pass::call(design, "opt_muxtree");
+ Pass::call(design, "opt_reduce" + opt_reduce_args);
+ Pass::call(design, "opt_merge" + opt_merge_args);
+ Pass::call(design, "opt_rmdff" + opt_rmdff_args);
+ Pass::call(design, "opt_clean" + opt_clean_args);
+ Pass::call(design, "opt_expr" + opt_expr_args);
+ if (design->scratchpad_get_bool("opt.did_something") == false)
+ break;
+ log_header(design, "Rerunning OPT passes. (Maybe there is more to do..)\n");
+ }
+ }
+
+ design->optimize();
+ design->sort();
+ design->check();
+
+ log_header(design, fast_mode ? "Finished fast OPT passes.\n" : "Finished OPT passes. (There is nothing left to do.)\n");
+ log_pop();
+ }
+} OptPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc
new file mode 100644
index 00000000..6600ffa2
--- /dev/null
+++ b/passes/opt/opt_clean.cc
@@ -0,0 +1,484 @@
+/*
+ * 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/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include "kernel/celltypes.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <set>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+using RTLIL::id2cstr;
+
+struct keep_cache_t
+{
+ Design *design;
+ dict<Module*, bool> cache;
+
+ void reset(Design *design = nullptr)
+ {
+ this->design = design;
+ cache.clear();
+ }
+
+ bool query(Module *module)
+ {
+ log_assert(design != nullptr);
+
+ if (module == nullptr)
+ return false;
+
+ if (cache.count(module))
+ return cache.at(module);
+
+ cache[module] = true;
+ if (!module->get_bool_attribute("\\keep")) {
+ bool found_keep = false;
+ for (auto cell : module->cells())
+ if (query(cell)) found_keep = true;
+ cache[module] = found_keep;
+ }
+
+ return cache[module];
+ }
+
+ bool query(Cell *cell)
+ {
+ if (cell->type.in("$memwr", "$meminit", "$assert", "$assume"))
+ return true;
+
+ if (cell->has_keep_attr())
+ return true;
+
+ if (cell->module && cell->module->design)
+ return query(cell->module->design->module(cell->type));
+
+ return false;
+ }
+};
+
+keep_cache_t keep_cache;
+CellTypes ct_reg, ct_all;
+int count_rm_cells, count_rm_wires;
+
+void rmunused_module_cells(Module *module, bool verbose)
+{
+ SigMap sigmap(module);
+ pool<Cell*> queue, unused;
+ dict<SigBit, pool<Cell*>> wire2driver;
+
+ for (auto &it : module->cells_) {
+ Cell *cell = it.second;
+ for (auto &it2 : cell->connections()) {
+ if (!ct_all.cell_known(cell->type) || ct_all.cell_output(cell->type, it2.first))
+ for (auto bit : sigmap(it2.second))
+ if (bit.wire != nullptr)
+ wire2driver[bit].insert(cell);
+ }
+ if (keep_cache.query(cell))
+ queue.insert(cell);
+ else
+ unused.insert(cell);
+ }
+
+ for (auto &it : module->wires_) {
+ Wire *wire = it.second;
+ if (wire->port_output || wire->get_bool_attribute("\\keep")) {
+ for (auto bit : sigmap(wire))
+ for (auto c : wire2driver[bit])
+ queue.insert(c), unused.erase(c);
+ }
+ }
+
+ while (!queue.empty())
+ {
+ pool<SigBit> bits;
+ for (auto cell : queue)
+ for (auto &it : cell->connections())
+ if (!ct_all.cell_known(cell->type) || ct_all.cell_input(cell->type, it.first))
+ for (auto bit : sigmap(it.second))
+ bits.insert(bit);
+
+ queue.clear();
+ for (auto bit : bits)
+ for (auto c : wire2driver[bit])
+ if (unused.count(c))
+ queue.insert(c), unused.erase(c);
+ }
+
+ unused.sort(RTLIL::sort_by_name_id<RTLIL::Cell>());
+
+ for (auto cell : unused) {
+ if (verbose)
+ log(" removing unused `%s' cell `%s'.\n", cell->type.c_str(), cell->name.c_str());
+ module->design->scratchpad_set_bool("opt.did_something", true);
+ module->remove(cell);
+ count_rm_cells++;
+ }
+}
+
+int count_nontrivial_wire_attrs(RTLIL::Wire *w)
+{
+ int count = w->attributes.size();
+ count -= w->attributes.count("\\src");
+ count -= w->attributes.count("\\unused_bits");
+ return count;
+}
+
+bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool &regs, SigPool &conns, pool<RTLIL::Wire*> &direct_wires)
+{
+ RTLIL::Wire *w1 = s1.wire;
+ RTLIL::Wire *w2 = s2.wire;
+
+ if (w1 == NULL || w2 == NULL)
+ return w2 == NULL;
+
+ if (w1->port_input != w2->port_input)
+ return w2->port_input;
+
+ if ((w1->port_input && w1->port_output) != (w2->port_input && w2->port_output))
+ return !(w2->port_input && w2->port_output);
+
+ if (w1->name[0] == '\\' && w2->name[0] == '\\') {
+ if (regs.check_any(s1) != regs.check_any(s2))
+ return regs.check_any(s2);
+ if (direct_wires.count(w1) != direct_wires.count(w2))
+ return direct_wires.count(w2) != 0;
+ if (conns.check_any(s1) != conns.check_any(s2))
+ return conns.check_any(s2);
+ }
+
+ if (w1->port_output != w2->port_output)
+ return w2->port_output;
+
+ if (w1->name[0] != w2->name[0])
+ return w2->name[0] == '\\';
+
+ int attrs1 = count_nontrivial_wire_attrs(w1);
+ int attrs2 = count_nontrivial_wire_attrs(w2);
+
+ if (attrs1 != attrs2)
+ return attrs2 > attrs1;
+
+ return strcmp(w2->name.c_str(), w1->name.c_str()) < 0;
+}
+
+bool check_public_name(RTLIL::IdString id)
+{
+ const std::string &id_str = id.str();
+ if (id_str[0] == '$')
+ return false;
+ if (id_str.substr(0, 2) == "\\_" && (id_str[id_str.size()-1] == '_' || id_str.find("_[") != std::string::npos))
+ return false;
+ if (id_str.find(".$") != std::string::npos)
+ return false;
+ return true;
+}
+
+void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbose)
+{
+ SigPool register_signals;
+ SigPool connected_signals;
+
+ if (!purge_mode)
+ for (auto &it : module->cells_) {
+ RTLIL::Cell *cell = it.second;
+ if (ct_reg.cell_known(cell->type))
+ for (auto &it2 : cell->connections())
+ if (ct_reg.cell_output(cell->type, it2.first))
+ register_signals.add(it2.second);
+ for (auto &it2 : cell->connections())
+ connected_signals.add(it2.second);
+ }
+
+ SigMap assign_map(module);
+ pool<RTLIL::SigSpec> direct_sigs;
+ pool<RTLIL::Wire*> direct_wires;
+ for (auto &it : module->cells_) {
+ RTLIL::Cell *cell = it.second;
+ if (ct_all.cell_known(cell->type))
+ for (auto &it2 : cell->connections())
+ if (ct_all.cell_output(cell->type, it2.first))
+ direct_sigs.insert(assign_map(it2.second));
+ }
+ for (auto &it : module->wires_) {
+ if (direct_sigs.count(assign_map(it.second)) || it.second->port_input)
+ direct_wires.insert(it.second);
+ }
+
+ for (auto &it : module->wires_) {
+ RTLIL::Wire *wire = it.second;
+ for (int i = 0; i < wire->width; i++) {
+ RTLIL::SigBit s1 = RTLIL::SigBit(wire, i), s2 = assign_map(s1);
+ if (!compare_signals(s1, s2, register_signals, connected_signals, direct_wires))
+ assign_map.add(s1);
+ }
+ }
+
+ module->connections_.clear();
+
+ SigPool used_signals;
+ SigPool used_signals_nodrivers;
+ for (auto &it : module->cells_) {
+ RTLIL::Cell *cell = it.second;
+ for (auto &it2 : cell->connections_) {
+ assign_map.apply(it2.second);
+ used_signals.add(it2.second);
+ if (!ct_all.cell_output(cell->type, it2.first))
+ used_signals_nodrivers.add(it2.second);
+ }
+ }
+ for (auto &it : module->wires_) {
+ RTLIL::Wire *wire = it.second;
+ if (wire->port_id > 0) {
+ RTLIL::SigSpec sig = RTLIL::SigSpec(wire);
+ assign_map.apply(sig);
+ used_signals.add(sig);
+ if (!wire->port_input)
+ used_signals_nodrivers.add(sig);
+ }
+ if (wire->get_bool_attribute("\\keep")) {
+ RTLIL::SigSpec sig = RTLIL::SigSpec(wire);
+ assign_map.apply(sig);
+ used_signals.add(sig);
+ }
+ }
+
+ std::vector<RTLIL::Wire*> maybe_del_wires;
+ for (auto wire : module->wires())
+ {
+ if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0 || wire->get_bool_attribute("\\keep") || wire->attributes.count("\\init")) {
+ RTLIL::SigSpec s1 = RTLIL::SigSpec(wire), s2 = s1;
+ assign_map.apply(s2);
+ if (!used_signals.check_any(s2) && wire->port_id == 0 && !wire->get_bool_attribute("\\keep")) {
+ maybe_del_wires.push_back(wire);
+ } else {
+ log_assert(GetSize(s1) == GetSize(s2));
+ RTLIL::SigSig new_conn;
+ for (int i = 0; i < GetSize(s1); i++)
+ if (s1[i] != s2[i]) {
+ new_conn.first.append_bit(s1[i]);
+ new_conn.second.append_bit(s2[i]);
+ }
+ if (new_conn.first.size() > 0) {
+ used_signals.add(new_conn.first);
+ used_signals.add(new_conn.second);
+ module->connect(new_conn);
+ }
+ }
+ } else {
+ if (!used_signals.check_any(RTLIL::SigSpec(wire)))
+ maybe_del_wires.push_back(wire);
+ }
+
+ RTLIL::SigSpec sig = assign_map(RTLIL::SigSpec(wire));
+ if (!used_signals_nodrivers.check_any(sig)) {
+ std::string unused_bits;
+ for (int i = 0; i < GetSize(sig); i++) {
+ if (sig[i].wire == NULL)
+ continue;
+ if (!used_signals_nodrivers.check(sig[i])) {
+ if (!unused_bits.empty())
+ unused_bits += " ";
+ unused_bits += stringf("%d", i);
+ }
+ }
+ if (unused_bits.empty() || wire->port_id != 0)
+ wire->attributes.erase("\\unused_bits");
+ else
+ wire->attributes["\\unused_bits"] = RTLIL::Const(unused_bits);
+ } else {
+ wire->attributes.erase("\\unused_bits");
+ }
+ }
+
+
+ pool<RTLIL::Wire*> del_wires;
+
+ int del_wires_count = 0;
+ for (auto wire : maybe_del_wires)
+ if (!used_signals.check_any(RTLIL::SigSpec(wire))) {
+ if (check_public_name(wire->name) && verbose) {
+ log(" removing unused non-port wire %s.\n", wire->name.c_str());
+ del_wires_count++;
+ }
+ del_wires.insert(wire);
+ }
+
+ module->remove(del_wires);
+ count_rm_wires += del_wires.size();;
+
+ if (del_wires_count > 0)
+ log(" removed %d unused temporary wires.\n", del_wires_count);
+}
+
+void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose)
+{
+ if (verbose)
+ log("Finding unused cells or wires in module %s..\n", module->name.c_str());
+
+ std::vector<RTLIL::Cell*> delcells;
+ for (auto cell : module->cells())
+ if (cell->type.in("$pos", "$_BUF_")) {
+ bool is_signed = cell->type == "$pos" && cell->getParam("\\A_SIGNED").as_bool();
+ RTLIL::SigSpec a = cell->getPort("\\A");
+ RTLIL::SigSpec y = cell->getPort("\\Y");
+ a.extend_u0(GetSize(y), is_signed);
+ module->connect(y, a);
+ delcells.push_back(cell);
+ }
+ for (auto cell : delcells) {
+ if (verbose)
+ log(" removing buffer cell `%s': %s = %s\n", cell->name.c_str(),
+ log_signal(cell->getPort("\\Y")), log_signal(cell->getPort("\\A")));
+ module->remove(cell);
+ }
+ if (!delcells.empty())
+ module->design->scratchpad_set_bool("opt.did_something", true);
+
+ rmunused_module_cells(module, verbose);
+ rmunused_module_signals(module, purge_mode, verbose);
+}
+
+struct OptCleanPass : public Pass {
+ OptCleanPass() : Pass("opt_clean", "remove unused cells and wires") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" opt_clean [options] [selection]\n");
+ log("\n");
+ log("This pass identifies wires and cells that are unused and removes them. Other\n");
+ log("passes often remove cells but leave the wires in the design or reconnect the\n");
+ log("wires but leave the old cells in the design. This pass can be used to clean up\n");
+ log("after the passes that do the actual work.\n");
+ log("\n");
+ log("This pass only operates on completely selected modules without processes.\n");
+ log("\n");
+ log(" -purge\n");
+ log(" also remove internal nets if they have a public name\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool purge_mode = false;
+
+ log_header(design, "Executing OPT_CLEAN pass (remove unused cells and wires).\n");
+ log_push();
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-purge") {
+ purge_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ keep_cache.reset(design);
+
+ ct_reg.setup_internals_mem();
+ ct_reg.setup_stdcells_mem();
+
+ ct_all.setup(design);
+
+ for (auto module : design->selected_whole_modules_warn()) {
+ if (module->has_processes_warn())
+ continue;
+ rmunused_module(module, purge_mode, true);
+ }
+
+ design->optimize();
+ design->sort();
+ design->check();
+
+ keep_cache.reset();
+ ct_reg.clear();
+ ct_all.clear();
+ log_pop();
+ }
+} OptCleanPass;
+
+struct CleanPass : public Pass {
+ CleanPass() : Pass("clean", "remove unused cells and wires") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" clean [options] [selection]\n");
+ log("\n");
+ log("This is identical to 'opt_clean', but less verbose.\n");
+ log("\n");
+ log("When commands are separated using the ';;' token, this command will be executed\n");
+ log("between the commands.\n");
+ log("\n");
+ log("When commands are separated using the ';;;' token, this command will be executed\n");
+ log("in -purge mode between the commands.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool purge_mode = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-purge") {
+ purge_mode = true;
+ continue;
+ }
+ break;
+ }
+ if (argidx < args.size())
+ extra_args(args, argidx, design);
+
+ keep_cache.reset(design);
+
+ ct_reg.setup_internals_mem();
+ ct_reg.setup_stdcells_mem();
+
+ ct_all.setup(design);
+
+ count_rm_cells = 0;
+ count_rm_wires = 0;
+
+ for (auto module : design->selected_whole_modules()) {
+ if (module->has_processes())
+ continue;
+ rmunused_module(module, purge_mode, false);
+ }
+
+ if (count_rm_cells > 0 || count_rm_wires > 0)
+ log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires);
+
+ design->optimize();
+ design->sort();
+ design->check();
+
+ keep_cache.reset();
+ ct_reg.clear();
+ ct_all.clear();
+ }
+} CleanPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc
new file mode 100644
index 00000000..b62eae28
--- /dev/null
+++ b/passes/opt/opt_expr.cc
@@ -0,0 +1,1283 @@
+/*
+ * 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/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
+#include "kernel/utils.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <algorithm>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+bool did_something;
+
+void replace_undriven(RTLIL::Design *design, RTLIL::Module *module)
+{
+ CellTypes ct(design);
+ SigMap sigmap(module);
+ SigPool driven_signals;
+ SigPool used_signals;
+ SigPool all_signals;
+
+ for (auto cell : module->cells())
+ for (auto &conn : cell->connections()) {
+ if (!ct.cell_known(cell->type) || ct.cell_output(cell->type, conn.first))
+ driven_signals.add(sigmap(conn.second));
+ if (!ct.cell_known(cell->type) || ct.cell_input(cell->type, conn.first))
+ used_signals.add(sigmap(conn.second));
+ }
+
+ for (auto wire : module->wires()) {
+ if (wire->port_input)
+ driven_signals.add(sigmap(wire));
+ if (wire->port_output)
+ used_signals.add(sigmap(wire));
+ all_signals.add(sigmap(wire));
+ }
+
+ all_signals.del(driven_signals);
+ RTLIL::SigSpec undriven_signals = all_signals.export_all();
+
+ for (auto &c : undriven_signals.chunks())
+ {
+ RTLIL::SigSpec sig = c;
+
+ if (c.wire->name[0] == '$')
+ sig = used_signals.extract(sig);
+ if (sig.size() == 0)
+ continue;
+
+ log("Setting undriven signal in %s to undef: %s\n", RTLIL::id2cstr(module->name), log_signal(c));
+ module->connect(RTLIL::SigSig(c, RTLIL::SigSpec(RTLIL::State::Sx, c.width)));
+ did_something = true;
+ }
+}
+
+void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, std::string info, std::string out_port, RTLIL::SigSpec out_val)
+{
+ RTLIL::SigSpec Y = cell->getPort(out_port);
+ out_val.extend_u0(Y.size(), false);
+
+ log("Replacing %s cell `%s' (%s) in module `%s' with constant driver `%s = %s'.\n",
+ cell->type.c_str(), cell->name.c_str(), info.c_str(),
+ module->name.c_str(), log_signal(Y), log_signal(out_val));
+ // log_cell(cell);
+ assign_map.add(Y, out_val);
+ module->connect(Y, out_val);
+ module->remove(cell);
+ did_something = true;
+}
+
+bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutative, SigMap &sigmap)
+{
+ std::string b_name = cell->hasPort("\\B") ? "\\B" : "\\A";
+
+ bool a_signed = cell->parameters.at("\\A_SIGNED").as_bool();
+ bool b_signed = cell->parameters.at(b_name + "_SIGNED").as_bool();
+
+ RTLIL::SigSpec sig_a = sigmap(cell->getPort("\\A"));
+ RTLIL::SigSpec sig_b = sigmap(cell->getPort(b_name));
+ RTLIL::SigSpec sig_y = sigmap(cell->getPort("\\Y"));
+
+ sig_a.extend_u0(sig_y.size(), a_signed);
+ sig_b.extend_u0(sig_y.size(), b_signed);
+
+ std::vector<RTLIL::SigBit> bits_a = sig_a, bits_b = sig_b, bits_y = sig_y;
+
+ enum { GRP_DYN, GRP_CONST_A, GRP_CONST_B, GRP_CONST_AB, GRP_N };
+ std::map<std::pair<RTLIL::SigBit, RTLIL::SigBit>, std::set<RTLIL::SigBit>> grouped_bits[GRP_N];
+
+ for (int i = 0; i < GetSize(bits_y); i++)
+ {
+ int group_idx = GRP_DYN;
+ RTLIL::SigBit bit_a = bits_a[i], bit_b = bits_b[i];
+
+ if (cell->type == "$or" && (bit_a == RTLIL::State::S1 || bit_b == RTLIL::State::S1))
+ bit_a = bit_b = RTLIL::State::S1;
+
+ if (cell->type == "$and" && (bit_a == RTLIL::State::S0 || bit_b == RTLIL::State::S0))
+ bit_a = bit_b = RTLIL::State::S0;
+
+ if (bit_a.wire == NULL && bit_b.wire == NULL)
+ group_idx = GRP_CONST_AB;
+ else if (bit_a.wire == NULL)
+ group_idx = GRP_CONST_A;
+ else if (bit_b.wire == NULL && commutative)
+ group_idx = GRP_CONST_A, std::swap(bit_a, bit_b);
+ else if (bit_b.wire == NULL)
+ group_idx = GRP_CONST_B;
+
+ grouped_bits[group_idx][std::pair<RTLIL::SigBit, RTLIL::SigBit>(bit_a, bit_b)].insert(bits_y[i]);
+ }
+
+ for (int i = 0; i < GRP_N; i++)
+ if (GetSize(grouped_bits[i]) == GetSize(bits_y))
+ return false;
+
+ log("Replacing %s cell `%s' in module `%s' with cells using grouped bits:\n",
+ log_id(cell->type), log_id(cell), log_id(module));
+
+ for (int i = 0; i < GRP_N; i++)
+ {
+ if (grouped_bits[i].empty())
+ continue;
+
+ RTLIL::Wire *new_y = module->addWire(NEW_ID, GetSize(grouped_bits[i]));
+ RTLIL::SigSpec new_a, new_b;
+ RTLIL::SigSig new_conn;
+
+ for (auto &it : grouped_bits[i]) {
+ for (auto &bit : it.second) {
+ new_conn.first.append_bit(bit);
+ new_conn.second.append_bit(RTLIL::SigBit(new_y, new_a.size()));
+ }
+ new_a.append_bit(it.first.first);
+ new_b.append_bit(it.first.second);
+ }
+
+ RTLIL::Cell *c = module->addCell(NEW_ID, cell->type);
+
+ c->setPort("\\A", new_a);
+ c->parameters["\\A_WIDTH"] = new_a.size();
+ c->parameters["\\A_SIGNED"] = false;
+
+ if (b_name == "\\B") {
+ c->setPort("\\B", new_b);
+ c->parameters["\\B_WIDTH"] = new_b.size();
+ c->parameters["\\B_SIGNED"] = false;
+ }
+
+ c->setPort("\\Y", new_y);
+ c->parameters["\\Y_WIDTH"] = new_y->width;
+ c->check();
+
+ module->connect(new_conn);
+
+ log(" New cell `%s': A=%s", log_id(c), log_signal(new_a));
+ if (b_name == "\\B")
+ log(", B=%s", log_signal(new_b));
+ log("\n");
+ }
+
+ cover_list("opt.opt_expr.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell->type.str());
+
+ module->remove(cell);
+ did_something = true;
+ return true;
+}
+
+void handle_polarity_inv(Cell *cell, IdString port, IdString param, const SigMap &assign_map, const dict<RTLIL::SigSpec, RTLIL::SigSpec> &invert_map)
+{
+ SigSpec sig = assign_map(cell->getPort(port));
+ if (invert_map.count(sig)) {
+ log("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
+ log_id(port), log_id(cell->type), log_id(cell), log_id(cell->module),
+ log_signal(sig), log_signal(invert_map.at(sig)));
+ cell->setPort(port, (invert_map.at(sig)));
+ cell->setParam(param, !cell->getParam(param).as_bool());
+ }
+}
+
+void handle_clkpol_celltype_swap(Cell *cell, string type1, string type2, IdString port, const SigMap &assign_map, const dict<RTLIL::SigSpec, RTLIL::SigSpec> &invert_map)
+{
+ log_assert(GetSize(type1) == GetSize(type2));
+ string cell_type = cell->type.str();
+
+ if (GetSize(type1) != GetSize(cell_type))
+ return;
+
+ for (int i = 0; i < GetSize(type1); i++) {
+ log_assert((type1[i] == '?') == (type2[i] == '?'));
+ if (type1[i] == '?') {
+ if (cell_type[i] != '0' && cell_type[i] != '1' && cell_type[i] != 'N' && cell_type[i] != 'P')
+ return;
+ type1[i] = cell_type[i];
+ type2[i] = cell_type[i];
+ }
+ }
+
+ if (cell->type.in(type1, type2)) {
+ SigSpec sig = assign_map(cell->getPort(port));
+ if (invert_map.count(sig)) {
+ log("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
+ log_id(port), log_id(cell->type), log_id(cell), log_id(cell->module),
+ log_signal(sig), log_signal(invert_map.at(sig)));
+ cell->setPort(port, (invert_map.at(sig)));
+ cell->type = cell->type == type1 ? type2 : type1;
+ }
+ }
+}
+
+bool is_one_or_minus_one(const Const &value, bool is_signed, bool &is_negative)
+{
+ bool all_bits_one = true;
+ bool last_bit_one = true;
+
+ if (GetSize(value.bits) < 1)
+ return false;
+
+ if (GetSize(value.bits) == 1) {
+ if (value.bits[0] != State::S1)
+ return false;
+ if (is_signed)
+ is_negative = true;
+ return true;
+ }
+
+ for (int i = 0; i < GetSize(value.bits); i++) {
+ if (value.bits[i] != State::S1)
+ all_bits_one = false;
+ if (value.bits[i] != (i ? State::S0 : State::S1))
+ last_bit_one = false;
+ }
+
+ if (all_bits_one && is_signed) {
+ is_negative = true;
+ return true;
+ }
+
+ return last_bit_one;
+}
+
+void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool clkinv)
+{
+ if (!design->selected(module))
+ return;
+
+ CellTypes ct_combinational;
+ ct_combinational.setup_internals();
+ ct_combinational.setup_stdcells();
+
+ SigMap assign_map(module);
+ dict<RTLIL::SigSpec, RTLIL::SigSpec> invert_map;
+
+ TopoSort<RTLIL::Cell*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Cell>> cells;
+ dict<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_inbit;
+ dict<RTLIL::SigBit, std::set<RTLIL::Cell*>> outbit_to_cell;
+
+ for (auto cell : module->cells())
+ if (design->selected(module, cell) && cell->type[0] == '$') {
+ if ((cell->type == "$_NOT_" || cell->type == "$not" || cell->type == "$logic_not") &&
+ cell->getPort("\\A").size() == 1 && cell->getPort("\\Y").size() == 1)
+ invert_map[assign_map(cell->getPort("\\Y"))] = assign_map(cell->getPort("\\A"));
+ if ((cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == SigSpec(State::S1) && cell->getPort("\\B") == SigSpec(State::S0))
+ invert_map[assign_map(cell->getPort("\\Y"))] = assign_map(cell->getPort("\\S"));
+ if (ct_combinational.cell_known(cell->type))
+ for (auto &conn : cell->connections()) {
+ RTLIL::SigSpec sig = assign_map(conn.second);
+ sig.remove_const();
+ if (ct_combinational.cell_input(cell->type, conn.first))
+ cell_to_inbit[cell].insert(sig.begin(), sig.end());
+ if (ct_combinational.cell_output(cell->type, conn.first))
+ for (auto &bit : sig)
+ outbit_to_cell[bit].insert(cell);
+ }
+ cells.node(cell);
+ }
+
+ for (auto &it_right : cell_to_inbit)
+ for (auto &it_sigbit : it_right.second)
+ for (auto &it_left : outbit_to_cell[it_sigbit])
+ cells.edge(it_left, it_right.first);
+
+ cells.sort();
+
+ for (auto cell : cells.sorted)
+ {
+#define ACTION_DO(_p_, _s_) do { cover("opt.opt_expr.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0)
+#define ACTION_DO_Y(_v_) ACTION_DO("\\Y", RTLIL::SigSpec(RTLIL::State::S ## _v_))
+
+ if (clkinv)
+ {
+ if (cell->type.in("$dff", "$dffe", "$dffsr", "$adff", "$fsm", "$memrd", "$memwr"))
+ handle_polarity_inv(cell, "\\CLK", "\\CLK_POLARITY", assign_map, invert_map);
+
+ if (cell->type.in("$sr", "$dffsr", "$dlatchsr")) {
+ handle_polarity_inv(cell, "\\SET", "\\SET_POLARITY", assign_map, invert_map);
+ handle_polarity_inv(cell, "\\CLR", "\\CLR_POLARITY", assign_map, invert_map);
+ }
+
+ if (cell->type.in("$dffe", "$dlatch", "$dlatchsr"))
+ handle_polarity_inv(cell, "\\EN", "\\EN_POLARITY", assign_map, invert_map);
+
+ handle_clkpol_celltype_swap(cell, "$_SR_N?_", "$_SR_P?_", "\\S", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_SR_?N_", "$_SR_?P_", "\\R", assign_map, invert_map);
+
+ handle_clkpol_celltype_swap(cell, "$_DFF_N_", "$_DFF_P_", "\\C", assign_map, invert_map);
+
+ handle_clkpol_celltype_swap(cell, "$_DFFE_N?_", "$_DFFE_P?_", "\\C", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFFE_?N_", "$_DFFE_?P_", "\\E", assign_map, invert_map);
+
+ handle_clkpol_celltype_swap(cell, "$_DFF_N??_", "$_DFF_P??_", "\\C", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFF_?N?_", "$_DFF_?P?_", "\\R", assign_map, invert_map);
+
+ handle_clkpol_celltype_swap(cell, "$_DFFSR_N??_", "$_DFFSR_P??_", "\\C", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFFSR_?N?_", "$_DFFSR_?P?_", "\\S", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFFSR_??N_", "$_DFFSR_??P_", "\\R", assign_map, invert_map);
+
+ handle_clkpol_celltype_swap(cell, "$_DLATCH_N_", "$_DLATCH_P_", "\\E", assign_map, invert_map);
+
+ handle_clkpol_celltype_swap(cell, "$_DLATCHSR_N??_", "$_DLATCHSR_P??_", "\\E", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DLATCHSR_?N?_", "$_DLATCHSR_?P?_", "\\S", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DLATCHSR_??N_", "$_DLATCHSR_??P_", "\\R", assign_map, invert_map);
+ }
+
+ bool detect_const_and = false;
+ bool detect_const_or = false;
+
+ if (cell->type.in("$reduce_and", "$_AND_"))
+ detect_const_and = true;
+
+ if (cell->type.in("$and", "$logic_and") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1)
+ detect_const_and = true;
+
+ if (cell->type.in("$reduce_or", "$reduce_bool", "$_OR_"))
+ detect_const_or = true;
+
+ if (cell->type.in("$or", "$logic_or") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1)
+ detect_const_or = true;
+
+ if (detect_const_and || detect_const_or)
+ {
+ pool<SigBit> input_bits = assign_map(cell->getPort("\\A")).to_sigbit_pool();
+ bool found_zero = false, found_one = false, found_inv = false;
+
+ if (cell->hasPort("\\B")) {
+ vector<SigBit> more_bits = assign_map(cell->getPort("\\B")).to_sigbit_vector();
+ input_bits.insert(more_bits.begin(), more_bits.end());
+ }
+
+ for (auto bit : input_bits) {
+ if (bit == State::S0)
+ found_zero = true;
+ if (bit == State::S1)
+ found_one = true;
+ if (invert_map.count(bit) && input_bits.count(invert_map.at(bit)))
+ found_inv = true;
+ }
+
+ if (detect_const_and && (found_zero || found_inv)) {
+ cover("opt.opt_expr.const_and");
+ replace_cell(assign_map, module, cell, "const_and", "\\Y", RTLIL::State::S0);
+ goto next_cell;
+ }
+
+ if (detect_const_or && (found_one || found_inv)) {
+ cover("opt.opt_expr.const_or");
+ replace_cell(assign_map, module, cell, "const_or", "\\Y", RTLIL::State::S1);
+ goto next_cell;
+ }
+ }
+
+ if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool", "$reduce_xor", "$reduce_xnor", "$neg") &&
+ GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\Y")) == 1)
+ {
+ if (cell->type == "$reduce_xnor") {
+ cover("opt.opt_expr.reduce_xnor_not");
+ log("Replacing %s cell `%s' in module `%s' with $not cell.\n",
+ log_id(cell->type), log_id(cell->name), log_id(module));
+ cell->type = "$not";
+ } else {
+ cover("opt.opt_expr.unary_buffer");
+ replace_cell(assign_map, module, cell, "unary_buffer", "\\Y", cell->getPort("\\A"));
+ }
+ goto next_cell;
+ }
+
+ if (do_fine)
+ {
+ if (cell->type == "$not" || cell->type == "$pos" ||
+ cell->type == "$and" || cell->type == "$or" || cell->type == "$xor" || cell->type == "$xnor")
+ if (group_cell_inputs(module, cell, true, assign_map))
+ goto next_cell;
+
+ if (cell->type == "$reduce_and")
+ {
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
+
+ RTLIL::State new_a = RTLIL::State::S1;
+ for (auto &bit : sig_a.to_sigbit_vector())
+ if (bit == RTLIL::State::Sx) {
+ if (new_a == RTLIL::State::S1)
+ new_a = RTLIL::State::Sx;
+ } else if (bit == RTLIL::State::S0) {
+ new_a = RTLIL::State::S0;
+ break;
+ } else if (bit.wire != NULL) {
+ new_a = RTLIL::State::Sm;
+ }
+
+ if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) {
+ cover("opt.opt_expr.fine.$reduce_and");
+ log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
+ cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a));
+ cell->setPort("\\A", sig_a = new_a);
+ cell->parameters.at("\\A_WIDTH") = 1;
+ did_something = true;
+ }
+ }
+
+ if (cell->type == "$logic_not" || cell->type == "$logic_and" || cell->type == "$logic_or" || cell->type == "$reduce_or" || cell->type == "$reduce_bool")
+ {
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
+
+ RTLIL::State new_a = RTLIL::State::S0;
+ for (auto &bit : sig_a.to_sigbit_vector())
+ if (bit == RTLIL::State::Sx) {
+ if (new_a == RTLIL::State::S0)
+ new_a = RTLIL::State::Sx;
+ } else if (bit == RTLIL::State::S1) {
+ new_a = RTLIL::State::S1;
+ break;
+ } else if (bit.wire != NULL) {
+ new_a = RTLIL::State::Sm;
+ }
+
+ if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) {
+ cover_list("opt.opt_expr.fine.A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_bool", cell->type.str());
+ log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
+ cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a));
+ cell->setPort("\\A", sig_a = new_a);
+ cell->parameters.at("\\A_WIDTH") = 1;
+ did_something = true;
+ }
+ }
+
+ if (cell->type == "$logic_and" || cell->type == "$logic_or")
+ {
+ RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B"));
+
+ RTLIL::State new_b = RTLIL::State::S0;
+ for (auto &bit : sig_b.to_sigbit_vector())
+ if (bit == RTLIL::State::Sx) {
+ if (new_b == RTLIL::State::S0)
+ new_b = RTLIL::State::Sx;
+ } else if (bit == RTLIL::State::S1) {
+ new_b = RTLIL::State::S1;
+ break;
+ } else if (bit.wire != NULL) {
+ new_b = RTLIL::State::Sm;
+ }
+
+ if (new_b != RTLIL::State::Sm && RTLIL::SigSpec(new_b) != sig_b) {
+ cover_list("opt.opt_expr.fine.B", "$logic_and", "$logic_or", cell->type.str());
+ log("Replacing port B of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
+ cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_b));
+ cell->setPort("\\B", sig_b = new_b);
+ cell->parameters.at("\\B_WIDTH") = 1;
+ did_something = true;
+ }
+ }
+ }
+
+ if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor" || cell->type == "$shift" || cell->type == "$shiftx" ||
+ cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr" ||
+ cell->type == "$lt" || cell->type == "$le" || cell->type == "$ge" || cell->type == "$gt" ||
+ cell->type == "$neg" || cell->type == "$add" || cell->type == "$sub" ||
+ cell->type == "$mul" || cell->type == "$div" || cell->type == "$mod" || cell->type == "$pow")
+ {
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
+ RTLIL::SigSpec sig_b = cell->hasPort("\\B") ? assign_map(cell->getPort("\\B")) : RTLIL::SigSpec();
+
+ if (cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr" || cell->type == "$shift" || cell->type == "$shiftx")
+ sig_a = RTLIL::SigSpec();
+
+ for (auto &bit : sig_a.to_sigbit_vector())
+ if (bit == RTLIL::State::Sx)
+ goto found_the_x_bit;
+
+ for (auto &bit : sig_b.to_sigbit_vector())
+ if (bit == RTLIL::State::Sx)
+ goto found_the_x_bit;
+
+ if (0) {
+ found_the_x_bit:
+ cover_list("opt.opt_expr.xbit", "$reduce_xor", "$reduce_xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
+ "$lt", "$le", "$ge", "$gt", "$neg", "$add", "$sub", "$mul", "$div", "$mod", "$pow", cell->type.str());
+ if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor" ||
+ cell->type == "$lt" || cell->type == "$le" || cell->type == "$ge" || cell->type == "$gt")
+ replace_cell(assign_map, module, cell, "x-bit in input", "\\Y", RTLIL::State::Sx);
+ else
+ replace_cell(assign_map, module, cell, "x-bit in input", "\\Y", RTLIL::SigSpec(RTLIL::State::Sx, cell->getPort("\\Y").size()));
+ goto next_cell;
+ }
+ }
+
+ if ((cell->type == "$_NOT_" || cell->type == "$not" || cell->type == "$logic_not") && cell->getPort("\\Y").size() == 1 &&
+ invert_map.count(assign_map(cell->getPort("\\A"))) != 0) {
+ cover_list("opt.opt_expr.invert.double", "$_NOT_", "$not", "$logic_not", cell->type.str());
+ replace_cell(assign_map, module, cell, "double_invert", "\\Y", invert_map.at(assign_map(cell->getPort("\\A"))));
+ goto next_cell;
+ }
+
+ if ((cell->type == "$_MUX_" || cell->type == "$mux") && invert_map.count(assign_map(cell->getPort("\\S"))) != 0) {
+ cover_list("opt.opt_expr.invert.muxsel", "$_MUX_", "$mux", cell->type.str());
+ log("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", log_id(cell->type), log_id(cell), log_id(module));
+ RTLIL::SigSpec tmp = cell->getPort("\\A");
+ cell->setPort("\\A", cell->getPort("\\B"));
+ cell->setPort("\\B", tmp);
+ cell->setPort("\\S", invert_map.at(assign_map(cell->getPort("\\S"))));
+ did_something = true;
+ goto next_cell;
+ }
+
+ if (cell->type == "$_NOT_") {
+ RTLIL::SigSpec input = cell->getPort("\\A");
+ assign_map.apply(input);
+ if (input.match("1")) ACTION_DO_Y(0);
+ if (input.match("0")) ACTION_DO_Y(1);
+ if (input.match("*")) ACTION_DO_Y(x);
+ }
+
+ if (cell->type == "$_AND_") {
+ RTLIL::SigSpec input;
+ input.append(cell->getPort("\\B"));
+ input.append(cell->getPort("\\A"));
+ assign_map.apply(input);
+ if (input.match(" 0")) ACTION_DO_Y(0);
+ if (input.match("0 ")) ACTION_DO_Y(0);
+ if (input.match("11")) ACTION_DO_Y(1);
+ if (input.match("**")) ACTION_DO_Y(x);
+ if (input.match("1*")) ACTION_DO_Y(x);
+ if (input.match("*1")) ACTION_DO_Y(x);
+ if (consume_x) {
+ if (input.match(" *")) ACTION_DO_Y(0);
+ if (input.match("* ")) ACTION_DO_Y(0);
+ }
+ if (input.match(" 1")) ACTION_DO("\\Y", input.extract(1, 1));
+ if (input.match("1 ")) ACTION_DO("\\Y", input.extract(0, 1));
+ }
+
+ if (cell->type == "$_OR_") {
+ RTLIL::SigSpec input;
+ input.append(cell->getPort("\\B"));
+ input.append(cell->getPort("\\A"));
+ assign_map.apply(input);
+ if (input.match(" 1")) ACTION_DO_Y(1);
+ if (input.match("1 ")) ACTION_DO_Y(1);
+ if (input.match("00")) ACTION_DO_Y(0);
+ if (input.match("**")) ACTION_DO_Y(x);
+ if (input.match("0*")) ACTION_DO_Y(x);
+ if (input.match("*0")) ACTION_DO_Y(x);
+ if (consume_x) {
+ if (input.match(" *")) ACTION_DO_Y(1);
+ if (input.match("* ")) ACTION_DO_Y(1);
+ }
+ if (input.match(" 0")) ACTION_DO("\\Y", input.extract(1, 1));
+ if (input.match("0 ")) ACTION_DO("\\Y", input.extract(0, 1));
+ }
+
+ if (cell->type == "$_XOR_") {
+ RTLIL::SigSpec input;
+ input.append(cell->getPort("\\B"));
+ input.append(cell->getPort("\\A"));
+ assign_map.apply(input);
+ if (input.match("00")) ACTION_DO_Y(0);
+ if (input.match("01")) ACTION_DO_Y(1);
+ if (input.match("10")) ACTION_DO_Y(1);
+ if (input.match("11")) ACTION_DO_Y(0);
+ if (input.match(" *")) ACTION_DO_Y(x);
+ if (input.match("* ")) ACTION_DO_Y(x);
+ if (input.match(" 0")) ACTION_DO("\\Y", input.extract(1, 1));
+ if (input.match("0 ")) ACTION_DO("\\Y", input.extract(0, 1));
+ }
+
+ if (cell->type == "$_MUX_") {
+ RTLIL::SigSpec input;
+ input.append(cell->getPort("\\S"));
+ input.append(cell->getPort("\\B"));
+ input.append(cell->getPort("\\A"));
+ assign_map.apply(input);
+ if (input.extract(2, 1) == input.extract(1, 1))
+ ACTION_DO("\\Y", input.extract(2, 1));
+ if (input.match(" 0")) ACTION_DO("\\Y", input.extract(2, 1));
+ if (input.match(" 1")) ACTION_DO("\\Y", input.extract(1, 1));
+ if (input.match("01 ")) ACTION_DO("\\Y", input.extract(0, 1));
+ if (input.match("10 ")) {
+ cover("opt.opt_expr.mux_to_inv");
+ cell->type = "$_NOT_";
+ cell->setPort("\\A", input.extract(0, 1));
+ cell->unsetPort("\\B");
+ cell->unsetPort("\\S");
+ goto next_cell;
+ }
+ if (input.match("11 ")) ACTION_DO_Y(1);
+ if (input.match("00 ")) ACTION_DO_Y(0);
+ if (input.match("** ")) ACTION_DO_Y(x);
+ if (input.match("01*")) ACTION_DO_Y(x);
+ if (input.match("10*")) ACTION_DO_Y(x);
+ if (mux_undef) {
+ if (input.match("* ")) ACTION_DO("\\Y", input.extract(1, 1));
+ if (input.match(" * ")) ACTION_DO("\\Y", input.extract(2, 1));
+ if (input.match(" *")) ACTION_DO("\\Y", input.extract(2, 1));
+ }
+ }
+
+ if (cell->type == "$eq" || cell->type == "$ne" || cell->type == "$eqx" || cell->type == "$nex")
+ {
+ RTLIL::SigSpec a = cell->getPort("\\A");
+ RTLIL::SigSpec b = cell->getPort("\\B");
+
+ if (cell->parameters["\\A_WIDTH"].as_int() != cell->parameters["\\B_WIDTH"].as_int()) {
+ int width = max(cell->parameters["\\A_WIDTH"].as_int(), cell->parameters["\\B_WIDTH"].as_int());
+ a.extend_u0(width, cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool());
+ b.extend_u0(width, cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool());
+ }
+
+ RTLIL::SigSpec new_a, new_b;
+
+ log_assert(GetSize(a) == GetSize(b));
+ for (int i = 0; i < GetSize(a); i++) {
+ if (a[i].wire == NULL && b[i].wire == NULL && a[i] != b[i] && a[i].data <= RTLIL::State::S1 && b[i].data <= RTLIL::State::S1) {
+ cover_list("opt.opt_expr.eqneq.isneq", "$eq", "$ne", "$eqx", "$nex", cell->type.str());
+ RTLIL::SigSpec new_y = RTLIL::SigSpec((cell->type == "$eq" || cell->type == "$eqx") ? RTLIL::State::S0 : RTLIL::State::S1);
+ new_y.extend_u0(cell->parameters["\\Y_WIDTH"].as_int(), false);
+ replace_cell(assign_map, module, cell, "isneq", "\\Y", new_y);
+ goto next_cell;
+ }
+ if (a[i] == b[i])
+ continue;
+ new_a.append(a[i]);
+ new_b.append(b[i]);
+ }
+
+ if (new_a.size() == 0) {
+ cover_list("opt.opt_expr.eqneq.empty", "$eq", "$ne", "$eqx", "$nex", cell->type.str());
+ RTLIL::SigSpec new_y = RTLIL::SigSpec((cell->type == "$eq" || cell->type == "$eqx") ? RTLIL::State::S1 : RTLIL::State::S0);
+ new_y.extend_u0(cell->parameters["\\Y_WIDTH"].as_int(), false);
+ replace_cell(assign_map, module, cell, "empty", "\\Y", new_y);
+ goto next_cell;
+ }
+
+ if (new_a.size() < a.size() || new_b.size() < b.size()) {
+ cover_list("opt.opt_expr.eqneq.resize", "$eq", "$ne", "$eqx", "$nex", cell->type.str());
+ cell->setPort("\\A", new_a);
+ cell->setPort("\\B", new_b);
+ cell->parameters["\\A_WIDTH"] = new_a.size();
+ cell->parameters["\\B_WIDTH"] = new_b.size();
+ }
+ }
+
+ if ((cell->type == "$eq" || cell->type == "$ne") && cell->parameters["\\Y_WIDTH"].as_int() == 1 &&
+ cell->parameters["\\A_WIDTH"].as_int() == 1 && cell->parameters["\\B_WIDTH"].as_int() == 1)
+ {
+ RTLIL::SigSpec a = assign_map(cell->getPort("\\A"));
+ RTLIL::SigSpec b = assign_map(cell->getPort("\\B"));
+
+ if (a.is_fully_const() && !b.is_fully_const()) {
+ cover_list("opt.opt_expr.eqneq.swapconst", "$eq", "$ne", cell->type.str());
+ cell->setPort("\\A", b);
+ cell->setPort("\\B", a);
+ std::swap(a, b);
+ }
+
+ if (b.is_fully_const()) {
+ if (b.as_bool() == (cell->type == "$eq")) {
+ RTLIL::SigSpec input = b;
+ ACTION_DO("\\Y", cell->getPort("\\A"));
+ } else {
+ cover_list("opt.opt_expr.eqneq.isnot", "$eq", "$ne", cell->type.str());
+ log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
+ cell->type = "$not";
+ cell->parameters.erase("\\B_WIDTH");
+ cell->parameters.erase("\\B_SIGNED");
+ cell->unsetPort("\\B");
+ did_something = true;
+ }
+ goto next_cell;
+ }
+ }
+
+ if ((cell->type == "$eq" || cell->type == "$ne") &&
+ (assign_map(cell->getPort("\\A")).is_fully_zero() || assign_map(cell->getPort("\\B")).is_fully_zero()))
+ {
+ cover_list("opt.opt_expr.eqneq.cmpzero", "$eq", "$ne", cell->type.str());
+ log("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell),
+ log_id(module), "$eq" ? "$logic_not" : "$reduce_bool");
+ cell->type = cell->type == "$eq" ? "$logic_not" : "$reduce_bool";
+ if (assign_map(cell->getPort("\\A")).is_fully_zero()) {
+ cell->setPort("\\A", cell->getPort("\\B"));
+ cell->setParam("\\A_SIGNED", cell->getParam("\\B_SIGNED"));
+ cell->setParam("\\A_WIDTH", cell->getParam("\\B_WIDTH"));
+ }
+ cell->unsetPort("\\B");
+ cell->unsetParam("\\B_SIGNED");
+ cell->unsetParam("\\B_WIDTH");
+ did_something = true;
+ goto next_cell;
+ }
+
+ if (cell->type.in("$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx") && assign_map(cell->getPort("\\B")).is_fully_const())
+ {
+ bool sign_ext = cell->type == "$sshr" && cell->getParam("\\A_SIGNED").as_bool();
+ int shift_bits = assign_map(cell->getPort("\\B")).as_int(cell->type.in("$shift", "$shiftx") && cell->getParam("\\B_SIGNED").as_bool());
+
+ if (cell->type.in("$shl", "$sshl"))
+ shift_bits *= -1;
+
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
+ RTLIL::SigSpec sig_y(cell->type == "$shiftx" ? RTLIL::State::Sx : RTLIL::State::S0, cell->getParam("\\Y_WIDTH").as_int());
+
+ if (GetSize(sig_a) < GetSize(sig_y))
+ sig_a.extend_u0(GetSize(sig_y), cell->getParam("\\A_SIGNED").as_bool());
+
+ for (int i = 0; i < GetSize(sig_y); i++) {
+ int idx = i + shift_bits;
+ if (0 <= idx && idx < GetSize(sig_a))
+ sig_y[i] = sig_a[idx];
+ else if (GetSize(sig_a) <= idx && sign_ext)
+ sig_y[i] = sig_a[GetSize(sig_a)-1];
+ }
+
+ cover_list("opt.opt_expr.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell->type.str());
+
+ log("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n",
+ log_id(cell->type), log_id(cell), log_signal(assign_map(cell->getPort("\\B"))), shift_bits, log_id(module), log_signal(sig_y));
+
+ module->connect(cell->getPort("\\Y"), sig_y);
+ module->remove(cell);
+
+ did_something = true;
+ goto next_cell;
+ }
+
+ if (!keepdc)
+ {
+ bool identity_wrt_a = false;
+ bool identity_wrt_b = false;
+ bool arith_inverse = false;
+
+ if (cell->type == "$add" || cell->type == "$sub" || cell->type == "$or" || cell->type == "$xor")
+ {
+ RTLIL::SigSpec a = assign_map(cell->getPort("\\A"));
+ RTLIL::SigSpec b = assign_map(cell->getPort("\\B"));
+
+ if (cell->type != "$sub" && a.is_fully_const() && a.as_bool() == false)
+ identity_wrt_b = true;
+
+ if (b.is_fully_const() && b.as_bool() == false)
+ identity_wrt_a = true;
+ }
+
+ if (cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr" || cell->type == "$shift" || cell->type == "$shiftx")
+ {
+ RTLIL::SigSpec b = assign_map(cell->getPort("\\B"));
+
+ if (b.is_fully_const() && b.as_bool() == false)
+ identity_wrt_a = true;
+ }
+
+ if (cell->type == "$mul")
+ {
+ RTLIL::SigSpec a = assign_map(cell->getPort("\\A"));
+ RTLIL::SigSpec b = assign_map(cell->getPort("\\B"));
+
+ if (a.is_fully_const() && is_one_or_minus_one(a.as_const(), cell->getParam("\\A_SIGNED").as_bool(), arith_inverse))
+ identity_wrt_b = true;
+ else
+ if (b.is_fully_const() && is_one_or_minus_one(b.as_const(), cell->getParam("\\B_SIGNED").as_bool(), arith_inverse))
+ identity_wrt_a = true;
+ }
+
+ if (cell->type == "$div")
+ {
+ RTLIL::SigSpec b = assign_map(cell->getPort("\\B"));
+
+ if (b.is_fully_const() && b.size() <= 32 && b.as_int() == 1)
+ identity_wrt_a = true;
+ }
+
+ if (identity_wrt_a || identity_wrt_b)
+ {
+ if (identity_wrt_a)
+ cover_list("opt.opt_expr.identwrt.a", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str());
+ if (identity_wrt_b)
+ cover_list("opt.opt_expr.identwrt.b", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str());
+
+ log("Replacing %s cell `%s' in module `%s' with identity for port %c.\n",
+ cell->type.c_str(), cell->name.c_str(), module->name.c_str(), identity_wrt_a ? 'A' : 'B');
+
+ if (!identity_wrt_a) {
+ cell->setPort("\\A", cell->getPort("\\B"));
+ cell->parameters.at("\\A_WIDTH") = cell->parameters.at("\\B_WIDTH");
+ cell->parameters.at("\\A_SIGNED") = cell->parameters.at("\\B_SIGNED");
+ }
+
+ cell->type = arith_inverse ? "$neg" : "$pos";
+ cell->unsetPort("\\B");
+ cell->parameters.erase("\\B_WIDTH");
+ cell->parameters.erase("\\B_SIGNED");
+ cell->check();
+
+ did_something = true;
+ goto next_cell;
+ }
+ }
+
+ if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") &&
+ cell->getPort("\\A") == RTLIL::SigSpec(0, 1) && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) {
+ cover_list("opt.opt_expr.mux_bool", "$mux", "$_MUX_", cell->type.str());
+ replace_cell(assign_map, module, cell, "mux_bool", "\\Y", cell->getPort("\\S"));
+ goto next_cell;
+ }
+
+ if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") &&
+ cell->getPort("\\A") == RTLIL::SigSpec(1, 1) && cell->getPort("\\B") == RTLIL::SigSpec(0, 1)) {
+ cover_list("opt.opt_expr.mux_invert", "$mux", "$_MUX_", cell->type.str());
+ log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
+ cell->setPort("\\A", cell->getPort("\\S"));
+ cell->unsetPort("\\B");
+ cell->unsetPort("\\S");
+ if (cell->type == "$mux") {
+ Const width = cell->parameters["\\WIDTH"];
+ cell->parameters["\\A_WIDTH"] = width;
+ cell->parameters["\\Y_WIDTH"] = width;
+ cell->parameters["\\A_SIGNED"] = 0;
+ cell->parameters.erase("\\WIDTH");
+ cell->type = "$not";
+ } else
+ cell->type = "$_NOT_";
+ did_something = true;
+ goto next_cell;
+ }
+
+ if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(0, 1)) {
+ cover_list("opt.opt_expr.mux_and", "$mux", "$_MUX_", cell->type.str());
+ log("Replacing %s cell `%s' in module `%s' with and-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
+ cell->setPort("\\A", cell->getPort("\\S"));
+ cell->unsetPort("\\S");
+ if (cell->type == "$mux") {
+ Const width = cell->parameters["\\WIDTH"];
+ cell->parameters["\\A_WIDTH"] = width;
+ cell->parameters["\\B_WIDTH"] = width;
+ cell->parameters["\\Y_WIDTH"] = width;
+ cell->parameters["\\A_SIGNED"] = 0;
+ cell->parameters["\\B_SIGNED"] = 0;
+ cell->parameters.erase("\\WIDTH");
+ cell->type = "$and";
+ } else
+ cell->type = "$_AND_";
+ did_something = true;
+ goto next_cell;
+ }
+
+ if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) {
+ cover_list("opt.opt_expr.mux_or", "$mux", "$_MUX_", cell->type.str());
+ log("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
+ cell->setPort("\\B", cell->getPort("\\S"));
+ cell->unsetPort("\\S");
+ if (cell->type == "$mux") {
+ Const width = cell->parameters["\\WIDTH"];
+ cell->parameters["\\A_WIDTH"] = width;
+ cell->parameters["\\B_WIDTH"] = width;
+ cell->parameters["\\Y_WIDTH"] = width;
+ cell->parameters["\\A_SIGNED"] = 0;
+ cell->parameters["\\B_SIGNED"] = 0;
+ cell->parameters.erase("\\WIDTH");
+ cell->type = "$or";
+ } else
+ cell->type = "$_OR_";
+ did_something = true;
+ goto next_cell;
+ }
+
+ if (mux_undef && (cell->type == "$mux" || cell->type == "$pmux")) {
+ RTLIL::SigSpec new_a, new_b, new_s;
+ int width = cell->getPort("\\A").size();
+ if ((cell->getPort("\\A").is_fully_undef() && cell->getPort("\\B").is_fully_undef()) ||
+ cell->getPort("\\S").is_fully_undef()) {
+ cover_list("opt.opt_expr.mux_undef", "$mux", "$pmux", cell->type.str());
+ replace_cell(assign_map, module, cell, "mux_undef", "\\Y", cell->getPort("\\A"));
+ goto next_cell;
+ }
+ for (int i = 0; i < cell->getPort("\\S").size(); i++) {
+ RTLIL::SigSpec old_b = cell->getPort("\\B").extract(i*width, width);
+ RTLIL::SigSpec old_s = cell->getPort("\\S").extract(i, 1);
+ if (old_b.is_fully_undef() || old_s.is_fully_undef())
+ continue;
+ new_b.append(old_b);
+ new_s.append(old_s);
+ }
+ new_a = cell->getPort("\\A");
+ if (new_a.is_fully_undef() && new_s.size() > 0) {
+ new_a = new_b.extract((new_s.size()-1)*width, width);
+ new_b = new_b.extract(0, (new_s.size()-1)*width);
+ new_s = new_s.extract(0, new_s.size()-1);
+ }
+ if (new_s.size() == 0) {
+ cover_list("opt.opt_expr.mux_empty", "$mux", "$pmux", cell->type.str());
+ replace_cell(assign_map, module, cell, "mux_empty", "\\Y", new_a);
+ goto next_cell;
+ }
+ if (new_a == RTLIL::SigSpec(RTLIL::State::S0) && new_b == RTLIL::SigSpec(RTLIL::State::S1)) {
+ cover_list("opt.opt_expr.mux_sel01", "$mux", "$pmux", cell->type.str());
+ replace_cell(assign_map, module, cell, "mux_sel01", "\\Y", new_s);
+ goto next_cell;
+ }
+ if (cell->getPort("\\S").size() != new_s.size()) {
+ cover_list("opt.opt_expr.mux_reduce", "$mux", "$pmux", cell->type.str());
+ log("Optimized away %d select inputs of %s cell `%s' in module `%s'.\n",
+ GetSize(cell->getPort("\\S")) - GetSize(new_s), log_id(cell->type), log_id(cell), log_id(module));
+ cell->setPort("\\A", new_a);
+ cell->setPort("\\B", new_b);
+ cell->setPort("\\S", new_s);
+ if (new_s.size() > 1) {
+ cell->type = "$pmux";
+ cell->parameters["\\S_WIDTH"] = new_s.size();
+ } else {
+ cell->type = "$mux";
+ cell->parameters.erase("\\S_WIDTH");
+ }
+ did_something = true;
+ }
+ }
+
+#define FOLD_1ARG_CELL(_t) \
+ if (cell->type == "$" #_t) { \
+ RTLIL::SigSpec a = cell->getPort("\\A"); \
+ assign_map.apply(a); \
+ if (a.is_fully_const()) { \
+ RTLIL::Const dummy_arg(RTLIL::State::S0, 1); \
+ RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), dummy_arg, \
+ cell->parameters["\\A_SIGNED"].as_bool(), false, \
+ cell->parameters["\\Y_WIDTH"].as_int())); \
+ cover("opt.opt_expr.const.$" #_t); \
+ replace_cell(assign_map, module, cell, stringf("%s", log_signal(a)), "\\Y", y); \
+ goto next_cell; \
+ } \
+ }
+#define FOLD_2ARG_CELL(_t) \
+ if (cell->type == "$" #_t) { \
+ RTLIL::SigSpec a = cell->getPort("\\A"); \
+ RTLIL::SigSpec b = cell->getPort("\\B"); \
+ assign_map.apply(a), assign_map.apply(b); \
+ if (a.is_fully_const() && b.is_fully_const()) { \
+ RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), b.as_const(), \
+ cell->parameters["\\A_SIGNED"].as_bool(), \
+ cell->parameters["\\B_SIGNED"].as_bool(), \
+ cell->parameters["\\Y_WIDTH"].as_int())); \
+ cover("opt.opt_expr.const.$" #_t); \
+ replace_cell(assign_map, module, cell, stringf("%s, %s", log_signal(a), log_signal(b)), "\\Y", y); \
+ goto next_cell; \
+ } \
+ }
+
+ FOLD_1ARG_CELL(not)
+ FOLD_2ARG_CELL(and)
+ FOLD_2ARG_CELL(or)
+ FOLD_2ARG_CELL(xor)
+ FOLD_2ARG_CELL(xnor)
+
+ FOLD_1ARG_CELL(reduce_and)
+ FOLD_1ARG_CELL(reduce_or)
+ FOLD_1ARG_CELL(reduce_xor)
+ FOLD_1ARG_CELL(reduce_xnor)
+ FOLD_1ARG_CELL(reduce_bool)
+
+ FOLD_1ARG_CELL(logic_not)
+ FOLD_2ARG_CELL(logic_and)
+ FOLD_2ARG_CELL(logic_or)
+
+ FOLD_2ARG_CELL(shl)
+ FOLD_2ARG_CELL(shr)
+ FOLD_2ARG_CELL(sshl)
+ FOLD_2ARG_CELL(sshr)
+ FOLD_2ARG_CELL(shift)
+ FOLD_2ARG_CELL(shiftx)
+
+ FOLD_2ARG_CELL(lt)
+ FOLD_2ARG_CELL(le)
+ FOLD_2ARG_CELL(eq)
+ FOLD_2ARG_CELL(ne)
+ FOLD_2ARG_CELL(gt)
+ FOLD_2ARG_CELL(ge)
+
+ FOLD_2ARG_CELL(add)
+ FOLD_2ARG_CELL(sub)
+ FOLD_2ARG_CELL(mul)
+ FOLD_2ARG_CELL(div)
+ FOLD_2ARG_CELL(mod)
+ FOLD_2ARG_CELL(pow)
+
+ FOLD_1ARG_CELL(pos)
+ FOLD_1ARG_CELL(neg)
+
+ // be very conservative with optimizing $mux cells as we do not want to break mux trees
+ if (cell->type == "$mux") {
+ RTLIL::SigSpec input = assign_map(cell->getPort("\\S"));
+ RTLIL::SigSpec inA = assign_map(cell->getPort("\\A"));
+ RTLIL::SigSpec inB = assign_map(cell->getPort("\\B"));
+ if (input.is_fully_const())
+ ACTION_DO("\\Y", input.as_bool() ? cell->getPort("\\B") : cell->getPort("\\A"));
+ else if (inA == inB)
+ ACTION_DO("\\Y", cell->getPort("\\A"));
+ }
+
+ if (!keepdc && cell->type == "$mul")
+ {
+ bool a_signed = cell->parameters["\\A_SIGNED"].as_bool();
+ bool b_signed = cell->parameters["\\B_SIGNED"].as_bool();
+ bool swapped_ab = false;
+
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
+ RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B"));
+ RTLIL::SigSpec sig_y = assign_map(cell->getPort("\\Y"));
+
+ if (sig_b.is_fully_const() && sig_b.size() <= 32)
+ std::swap(sig_a, sig_b), std::swap(a_signed, b_signed), swapped_ab = true;
+
+ if (sig_a.is_fully_def() && sig_a.size() <= 32)
+ {
+ int a_val = sig_a.as_int();
+
+ if (a_val == 0)
+ {
+ cover("opt.opt_expr.mul_shift.zero");
+
+ log("Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n",
+ cell->name.c_str(), module->name.c_str());
+
+ module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size())));
+ module->remove(cell);
+
+ did_something = true;
+ goto next_cell;
+ }
+
+ for (int i = 1; i < (a_signed ? sig_a.size()-1 : sig_a.size()); i++)
+ if (a_val == (1 << i))
+ {
+ if (swapped_ab)
+ cover("opt.opt_expr.mul_shift.swapped");
+ else
+ cover("opt.opt_expr.mul_shift.unswapped");
+
+ log("Replacing multiply-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
+ a_val, cell->name.c_str(), module->name.c_str(), i);
+
+ if (!swapped_ab) {
+ cell->setPort("\\A", cell->getPort("\\B"));
+ cell->parameters.at("\\A_WIDTH") = cell->parameters.at("\\B_WIDTH");
+ cell->parameters.at("\\A_SIGNED") = cell->parameters.at("\\B_SIGNED");
+ }
+
+ std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6);
+
+ while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0)
+ new_b.pop_back();
+
+ cell->type = "$shl";
+ cell->parameters["\\B_WIDTH"] = GetSize(new_b);
+ cell->parameters["\\B_SIGNED"] = false;
+ cell->setPort("\\B", new_b);
+ cell->check();
+
+ did_something = true;
+ goto next_cell;
+ }
+ }
+ }
+
+ if (!keepdc && cell->type.in("$div", "$mod"))
+ {
+ bool b_signed = cell->parameters["\\B_SIGNED"].as_bool();
+ SigSpec sig_b = assign_map(cell->getPort("\\B"));
+ SigSpec sig_y = assign_map(cell->getPort("\\Y"));
+
+ if (sig_b.is_fully_def() && sig_b.size() <= 32)
+ {
+ int b_val = sig_b.as_int();
+
+ if (b_val == 0)
+ {
+ cover("opt.opt_expr.divmod_zero");
+
+ log("Replacing divide-by-zero cell `%s' in module `%s' with undef-driver.\n",
+ cell->name.c_str(), module->name.c_str());
+
+ module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(State::Sx, sig_y.size())));
+ module->remove(cell);
+
+ did_something = true;
+ goto next_cell;
+ }
+
+ for (int i = 1; i < (b_signed ? sig_b.size()-1 : sig_b.size()); i++)
+ if (b_val == (1 << i))
+ {
+ if (cell->type == "$div")
+ {
+ cover("opt.opt_expr.div_shift");
+
+ log("Replacing divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
+ b_val, cell->name.c_str(), module->name.c_str(), i);
+
+ std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6);
+
+ while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0)
+ new_b.pop_back();
+
+ cell->type = "$shr";
+ cell->parameters["\\B_WIDTH"] = GetSize(new_b);
+ cell->parameters["\\B_SIGNED"] = false;
+ cell->setPort("\\B", new_b);
+ cell->check();
+ }
+ else
+ {
+ cover("opt.opt_expr.mod_mask");
+
+ log("Replacing modulo-by-%d cell `%s' in module `%s' with bitmask.\n",
+ b_val, cell->name.c_str(), module->name.c_str());
+
+ std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(State::S1, i);
+
+ if (b_signed)
+ new_b.push_back(State::S0);
+
+ cell->type = "$and";
+ cell->parameters["\\B_WIDTH"] = GetSize(new_b);
+ cell->setPort("\\B", new_b);
+ cell->check();
+ }
+
+ did_something = true;
+ goto next_cell;
+ }
+ }
+ }
+
+ next_cell:;
+#undef ACTION_DO
+#undef ACTION_DO_Y
+#undef FOLD_1ARG_CELL
+#undef FOLD_2ARG_CELL
+ }
+}
+
+struct OptExprPass : public Pass {
+ OptExprPass() : Pass("opt_expr", "perform const folding and simple expression rewriting") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ 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("\n");
+ log(" -mux_undef\n");
+ log(" remove 'undef' inputs from $mux, $pmux and $_MUX_ cells\n");
+ log("\n");
+ log(" -mux_bool\n");
+ log(" replace $mux cells with inverters or buffers when possible\n");
+ log("\n");
+ log(" -undriven\n");
+ log(" replace undriven nets with undef (x) constants\n");
+ log("\n");
+ log(" -clkinv\n");
+ log(" optimize clock inverters by changing FF types\n");
+ log("\n");
+ log(" -fine\n");
+ log(" perform fine-grain optimizations\n");
+ log("\n");
+ log(" -full\n");
+ log(" alias for -mux_undef -mux_bool -undriven -fine\n");
+ log("\n");
+ log(" -keepdc\n");
+ log(" some optimizations change the behavior of the circuit with respect to\n");
+ log(" don't-care bits. for example in 'a+0' a single x-bit in 'a' will cause\n");
+ log(" all result bits to be set to x. this behavior changes when 'a+0' is\n");
+ log(" replaced by 'a'. the -keepdc option disables all such optimizations.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool mux_undef = false;
+ bool mux_bool = false;
+ bool undriven = false;
+ bool clkinv = false;
+ bool do_fine = false;
+ bool keepdc = false;
+
+ log_header(design, "Executing OPT_EXPR pass (perform const folding).\n");
+ log_push();
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-mux_undef") {
+ mux_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-mux_bool") {
+ mux_bool = true;
+ continue;
+ }
+ if (args[argidx] == "-undriven") {
+ undriven = true;
+ continue;
+ }
+ if (args[argidx] == "-clkinv") {
+ clkinv = true;
+ continue;
+ }
+ if (args[argidx] == "-fine") {
+ do_fine = true;
+ continue;
+ }
+ if (args[argidx] == "-full") {
+ mux_undef = true;
+ mux_bool = true;
+ undriven = true;
+ do_fine = true;
+ continue;
+ }
+ if (args[argidx] == "-keepdc") {
+ keepdc = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ if (undriven)
+ replace_undriven(design, module);
+
+ do {
+ do {
+ did_something = false;
+ replace_const_cells(design, module, false, mux_undef, mux_bool, do_fine, keepdc, clkinv);
+ if (did_something)
+ design->scratchpad_set_bool("opt.did_something", true);
+ } while (did_something);
+ replace_const_cells(design, module, true, mux_undef, mux_bool, do_fine, keepdc, clkinv);
+ } while (did_something);
+ }
+
+ log_pop();
+ }
+} OptExprPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc
new file mode 100644
index 00000000..97989d27
--- /dev/null
+++ b/passes/opt/opt_merge.cc
@@ -0,0 +1,383 @@
+/*
+ * 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/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include "kernel/celltypes.h"
+#include "libs/sha1/sha1.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <set>
+
+#define USE_CELL_HASH_CACHE
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct OptMergeWorker
+{
+ RTLIL::Design *design;
+ RTLIL::Module *module;
+ SigMap assign_map;
+ SigMap dff_init_map;
+ bool mode_share_all;
+
+ CellTypes ct;
+ int total_count;
+#ifdef USE_CELL_HASH_CACHE
+ dict<const RTLIL::Cell*, std::string> cell_hash_cache;
+#endif
+
+ static void sort_pmux_conn(dict<RTLIL::IdString, RTLIL::SigSpec> &conn)
+ {
+ SigSpec sig_s = conn.at("\\S");
+ SigSpec sig_b = conn.at("\\B");
+
+ int s_width = GetSize(sig_s);
+ int width = GetSize(sig_b) / s_width;
+
+ vector<pair<SigBit, SigSpec>> sb_pairs;
+ for (int i = 0; i < s_width; i++)
+ sb_pairs.push_back(pair<SigBit, SigSpec>(sig_s[i], sig_b.extract(i*width, width)));
+
+ std::sort(sb_pairs.begin(), sb_pairs.end());
+
+ conn["\\S"] = SigSpec();
+ conn["\\B"] = SigSpec();
+
+ for (auto &it : sb_pairs) {
+ conn["\\S"].append(it.first);
+ conn["\\B"].append(it.second);
+ }
+ }
+
+#ifdef USE_CELL_HASH_CACHE
+ std::string int_to_hash_string(unsigned int v)
+ {
+ if (v == 0)
+ return "0";
+ std::string str = "";
+ while (v > 0) {
+ str += 'a' + (v & 15);
+ v = v >> 4;
+ }
+ return str;
+ }
+
+ std::string hash_cell_parameters_and_connections(const RTLIL::Cell *cell)
+ {
+ if (cell_hash_cache.count(cell) > 0)
+ return cell_hash_cache[cell];
+
+ std::string hash_string = cell->type.str() + "\n";
+
+ for (auto &it : cell->parameters)
+ hash_string += "P " + it.first.str() + "=" + it.second.as_string() + "\n";
+
+ const dict<RTLIL::IdString, RTLIL::SigSpec> *conn = &cell->connections();
+ dict<RTLIL::IdString, RTLIL::SigSpec> alt_conn;
+
+ if (cell->type == "$and" || cell->type == "$or" || cell->type == "$xor" || cell->type == "$xnor" || cell->type == "$add" || cell->type == "$mul" ||
+ cell->type == "$logic_and" || cell->type == "$logic_or" || cell->type == "$_AND_" || cell->type == "$_OR_" || cell->type == "$_XOR_") {
+ alt_conn = *conn;
+ if (assign_map(alt_conn.at("\\A")) < assign_map(alt_conn.at("\\B"))) {
+ alt_conn["\\A"] = conn->at("\\B");
+ alt_conn["\\B"] = conn->at("\\A");
+ }
+ conn = &alt_conn;
+ } else
+ if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor") {
+ alt_conn = *conn;
+ assign_map.apply(alt_conn.at("\\A"));
+ alt_conn.at("\\A").sort();
+ conn = &alt_conn;
+ } else
+ if (cell->type == "$reduce_and" || cell->type == "$reduce_or" || cell->type == "$reduce_bool") {
+ alt_conn = *conn;
+ assign_map.apply(alt_conn.at("\\A"));
+ alt_conn.at("\\A").sort_and_unify();
+ conn = &alt_conn;
+ } else
+ if (cell->type == "$pmux") {
+ alt_conn = *conn;
+ assign_map.apply(alt_conn.at("\\A"));
+ assign_map.apply(alt_conn.at("\\B"));
+ assign_map.apply(alt_conn.at("\\S"));
+ sort_pmux_conn(alt_conn);
+ conn = &alt_conn;
+ }
+
+ vector<string> hash_conn_strings;
+
+ for (auto &it : *conn) {
+ if (cell->output(it.first))
+ continue;
+ RTLIL::SigSpec sig = it.second;
+ assign_map.apply(sig);
+ string s = "C " + it.first.str() + "=";
+ for (auto &chunk : sig.chunks()) {
+ if (chunk.wire)
+ s += "{" + chunk.wire->name.str() + " " +
+ int_to_hash_string(chunk.offset) + " " +
+ int_to_hash_string(chunk.width) + "}";
+ else
+ s += RTLIL::Const(chunk.data).as_string();
+ }
+ hash_conn_strings.push_back(s + "\n");
+ }
+
+ std::sort(hash_conn_strings.begin(), hash_conn_strings.end());
+
+ for (auto it : hash_conn_strings)
+ hash_string += it;
+
+ cell_hash_cache[cell] = sha1(hash_string);
+ return cell_hash_cache[cell];
+ }
+#endif
+
+ bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2, bool &lt)
+ {
+#ifdef USE_CELL_HASH_CACHE
+ std::string hash1 = hash_cell_parameters_and_connections(cell1);
+ std::string hash2 = hash_cell_parameters_and_connections(cell2);
+
+ if (hash1 != hash2) {
+ lt = hash1 < hash2;
+ return true;
+ }
+#endif
+
+ if (cell1->parameters != cell2->parameters) {
+ std::map<RTLIL::IdString, RTLIL::Const> p1(cell1->parameters.begin(), cell1->parameters.end());
+ std::map<RTLIL::IdString, RTLIL::Const> p2(cell2->parameters.begin(), cell2->parameters.end());
+ lt = p1 < p2;
+ return true;
+ }
+
+ dict<RTLIL::IdString, RTLIL::SigSpec> conn1 = cell1->connections();
+ dict<RTLIL::IdString, RTLIL::SigSpec> conn2 = cell2->connections();
+
+ for (auto &it : conn1) {
+ if (cell1->output(it.first))
+ it.second = RTLIL::SigSpec();
+ else
+ assign_map.apply(it.second);
+ }
+
+ for (auto &it : conn2) {
+ if (cell2->output(it.first))
+ it.second = RTLIL::SigSpec();
+ else
+ assign_map.apply(it.second);
+ }
+
+ if (cell1->type == "$and" || cell1->type == "$or" || cell1->type == "$xor" || cell1->type == "$xnor" || cell1->type == "$add" || cell1->type == "$mul" ||
+ cell1->type == "$logic_and" || cell1->type == "$logic_or" || cell1->type == "$_AND_" || cell1->type == "$_OR_" || cell1->type == "$_XOR_") {
+ if (conn1.at("\\A") < conn1.at("\\B")) {
+ RTLIL::SigSpec tmp = conn1["\\A"];
+ conn1["\\A"] = conn1["\\B"];
+ conn1["\\B"] = tmp;
+ }
+ if (conn2.at("\\A") < conn2.at("\\B")) {
+ RTLIL::SigSpec tmp = conn2["\\A"];
+ conn2["\\A"] = conn2["\\B"];
+ conn2["\\B"] = tmp;
+ }
+ } else
+ if (cell1->type == "$reduce_xor" || cell1->type == "$reduce_xnor") {
+ conn1["\\A"].sort();
+ conn2["\\A"].sort();
+ } else
+ if (cell1->type == "$reduce_and" || cell1->type == "$reduce_or" || cell1->type == "$reduce_bool") {
+ conn1["\\A"].sort_and_unify();
+ conn2["\\A"].sort_and_unify();
+ } else
+ if (cell1->type == "$pmux") {
+ sort_pmux_conn(conn1);
+ sort_pmux_conn(conn2);
+ }
+
+ if (conn1 != conn2) {
+ std::map<RTLIL::IdString, RTLIL::SigSpec> c1(conn1.begin(), conn1.end());
+ std::map<RTLIL::IdString, RTLIL::SigSpec> c2(conn2.begin(), conn2.end());
+ lt = c1 < c2;
+ return true;
+ }
+
+ if (cell1->type.substr(0, 1) == "$" && conn1.count("\\Q") != 0) {
+ std::vector<RTLIL::SigBit> q1 = dff_init_map(cell1->getPort("\\Q")).to_sigbit_vector();
+ std::vector<RTLIL::SigBit> q2 = dff_init_map(cell2->getPort("\\Q")).to_sigbit_vector();
+ for (size_t i = 0; i < q1.size(); i++)
+ if ((q1.at(i).wire == NULL || q2.at(i).wire == NULL) && q1.at(i) != q2.at(i)) {
+ lt = q1.at(i) < q2.at(i);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool compare_cells(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2)
+ {
+ if (cell1->type != cell2->type)
+ return cell1->type < cell2->type;
+
+ if ((!mode_share_all && !ct.cell_known(cell1->type)) || !cell1->known())
+ return cell1 < cell2;
+
+ if (cell1->has_keep_attr() || cell2->has_keep_attr())
+ return cell1 < cell2;
+
+ bool lt;
+ if (compare_cell_parameters_and_connections(cell1, cell2, lt))
+ return lt;
+
+ return false;
+ }
+
+ struct CompareCells {
+ OptMergeWorker *that;
+ CompareCells(OptMergeWorker *that) : that(that) {}
+ bool operator()(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) const {
+ return that->compare_cells(cell1, cell2);
+ }
+ };
+
+ OptMergeWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux, bool mode_share_all) :
+ design(design), module(module), assign_map(module), mode_share_all(mode_share_all)
+ {
+ total_count = 0;
+ ct.setup_internals();
+ ct.setup_internals_mem();
+ ct.setup_stdcells();
+ ct.setup_stdcells_mem();
+
+ if (mode_nomux) {
+ ct.cell_types.erase("$mux");
+ ct.cell_types.erase("$pmux");
+ }
+
+ log("Finding identical cells in module `%s'.\n", module->name.c_str());
+ assign_map.set(module);
+
+ dff_init_map.set(module);
+ for (auto &it : module->wires_)
+ if (it.second->attributes.count("\\init") != 0)
+ dff_init_map.add(it.second, it.second->attributes.at("\\init"));
+
+ bool did_something = true;
+ while (did_something)
+ {
+#ifdef USE_CELL_HASH_CACHE
+ cell_hash_cache.clear();
+#endif
+ std::vector<RTLIL::Cell*> cells;
+ cells.reserve(module->cells_.size());
+ for (auto &it : module->cells_) {
+ if (!design->selected(module, it.second))
+ continue;
+ if (ct.cell_known(it.second->type) || (mode_share_all && it.second->known()))
+ cells.push_back(it.second);
+ }
+
+ did_something = false;
+ std::map<RTLIL::Cell*, RTLIL::Cell*, CompareCells> sharemap(CompareCells(this));
+ for (auto cell : cells)
+ {
+ if (sharemap.count(cell) > 0) {
+ did_something = true;
+ log(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str());
+ for (auto &it : cell->connections()) {
+ if (cell->output(it.first)) {
+ RTLIL::SigSpec other_sig = sharemap[cell]->getPort(it.first);
+ log(" Redirecting output %s: %s = %s\n", it.first.c_str(),
+ log_signal(it.second), log_signal(other_sig));
+ module->connect(RTLIL::SigSig(it.second, other_sig));
+ assign_map.add(it.second, other_sig);
+ }
+ }
+ log(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str());
+#ifdef USE_CELL_HASH_CACHE
+ cell_hash_cache.erase(cell);
+#endif
+ module->remove(cell);
+ total_count++;
+ } else {
+ sharemap[cell] = cell;
+ }
+ }
+ }
+ }
+};
+
+struct OptMergePass : public Pass {
+ OptMergePass() : Pass("opt_merge", "consolidate identical cells") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" opt_merge [options] [selection]\n");
+ log("\n");
+ log("This pass identifies cells with identical type and input signals. Such cells\n");
+ log("are then merged to one cell.\n");
+ log("\n");
+ log(" -nomux\n");
+ log(" Do not merge MUX cells.\n");
+ log("\n");
+ log(" -share_all\n");
+ log(" Operate on all cell types, not just built-in types.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing OPT_MERGE pass (detect identical cells).\n");
+
+ bool mode_nomux = false;
+ bool mode_share_all = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-nomux") {
+ mode_nomux = true;
+ continue;
+ }
+ if (arg == "-share_all") {
+ mode_share_all = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ int total_count = 0;
+ for (auto module : design->selected_modules()) {
+ OptMergeWorker worker(design, module, mode_nomux, mode_share_all);
+ total_count += worker.total_count;
+ }
+
+ if (total_count)
+ design->scratchpad_set_bool("opt.did_something", true);
+ log("Removed a total of %d cells.\n", total_count);
+ }
+} OptMergePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc
new file mode 100644
index 00000000..f5ddc2af
--- /dev/null
+++ b/passes/opt/opt_muxtree.cc
@@ -0,0 +1,483 @@
+/*
+ * 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/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include "kernel/celltypes.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <set>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+using RTLIL::id2cstr;
+
+struct OptMuxtreeWorker
+{
+ RTLIL::Design *design;
+ RTLIL::Module *module;
+ SigMap assign_map;
+ int removed_count;
+
+ struct bitinfo_t {
+ bool seen_non_mux;
+ pool<int> mux_users;
+ pool<int> mux_drivers;
+ };
+
+ idict<SigBit> bit2num;
+ vector<bitinfo_t> bit2info;
+
+ struct portinfo_t {
+ int ctrl_sig;
+ pool<int> input_sigs;
+ pool<int> input_muxes;
+ bool const_activated;
+ bool const_deactivated;
+ bool enabled;
+ };
+
+ struct muxinfo_t {
+ RTLIL::Cell *cell;
+ vector<portinfo_t> ports;
+ };
+
+ vector<muxinfo_t> mux2info;
+ vector<bool> root_muxes;
+ vector<bool> root_enable_muxes;
+ pool<int> root_mux_rerun;
+
+ OptMuxtreeWorker(RTLIL::Design *design, RTLIL::Module *module) :
+ design(design), module(module), assign_map(module), removed_count(0)
+ {
+ log("Running muxtree optimizer on module %s..\n", module->name.c_str());
+
+ log(" Creating internal representation of mux trees.\n");
+
+ // Populate bit2info[]:
+ // .seen_non_mux
+ // .mux_users
+ // .mux_drivers
+ // Populate mux2info[].ports[]:
+ // .ctrl_sig
+ // .input_sigs
+ // .const_activated
+ // .const_deactivated
+ for (auto cell : module->cells())
+ {
+ if (cell->type == "$mux" || cell->type == "$pmux")
+ {
+ RTLIL::SigSpec sig_a = cell->getPort("\\A");
+ RTLIL::SigSpec sig_b = cell->getPort("\\B");
+ RTLIL::SigSpec sig_s = cell->getPort("\\S");
+ RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+
+ muxinfo_t muxinfo;
+ muxinfo.cell = cell;
+
+ for (int i = 0; i < GetSize(sig_s); i++) {
+ RTLIL::SigSpec sig = sig_b.extract(i*GetSize(sig_a), GetSize(sig_a));
+ RTLIL::SigSpec ctrl_sig = assign_map(sig_s.extract(i, 1));
+ portinfo_t portinfo;
+ portinfo.ctrl_sig = sig2bits(ctrl_sig, false).front();
+ for (int idx : sig2bits(sig)) {
+ bit2info[idx].mux_users.insert(GetSize(mux2info));
+ portinfo.input_sigs.insert(idx);
+ }
+ portinfo.const_activated = ctrl_sig.is_fully_const() && ctrl_sig.as_bool();
+ portinfo.const_deactivated = ctrl_sig.is_fully_const() && !ctrl_sig.as_bool();
+ portinfo.enabled = false;
+ muxinfo.ports.push_back(portinfo);
+ }
+
+ portinfo_t portinfo;
+ for (int idx : sig2bits(sig_a)) {
+ bit2info[idx].mux_users.insert(GetSize(mux2info));
+ portinfo.input_sigs.insert(idx);
+ }
+ portinfo.ctrl_sig = -1;
+ portinfo.const_activated = false;
+ portinfo.const_deactivated = false;
+ portinfo.enabled = false;
+ muxinfo.ports.push_back(portinfo);
+
+ for (int idx : sig2bits(sig_y))
+ bit2info[idx].mux_drivers.insert(GetSize(mux2info));
+
+ for (int idx : sig2bits(sig_s))
+ bit2info[idx].seen_non_mux = true;
+
+ mux2info.push_back(muxinfo);
+ }
+ else
+ {
+ for (auto &it : cell->connections()) {
+ for (int idx : sig2bits(it.second))
+ bit2info[idx].seen_non_mux = true;
+ }
+ }
+ }
+ for (auto wire : module->wires()) {
+ if (wire->port_output || wire->get_bool_attribute("\\keep"))
+ for (int idx : sig2bits(RTLIL::SigSpec(wire)))
+ bit2info[idx].seen_non_mux = true;
+ }
+
+ if (mux2info.empty()) {
+ log(" No muxes found in this module.\n");
+ return;
+ }
+
+ // Populate mux2info[].ports[]:
+ // .input_muxes
+ for (int i = 0; i < GetSize(bit2info); i++)
+ for (int j : bit2info[i].mux_users)
+ for (auto &p : mux2info[j].ports) {
+ if (p.input_sigs.count(i))
+ for (int k : bit2info[i].mux_drivers)
+ p.input_muxes.insert(k);
+ }
+
+ log(" Evaluating internal representation of mux trees.\n");
+
+ dict<int, pool<int>> mux_to_users;
+ root_muxes.resize(GetSize(mux2info));
+ root_enable_muxes.resize(GetSize(mux2info));
+
+ for (auto &bi : bit2info) {
+ for (int i : bi.mux_drivers)
+ for (int j : bi.mux_users)
+ mux_to_users[i].insert(j);
+ if (!bi.seen_non_mux)
+ continue;
+ for (int mux_idx : bi.mux_drivers) {
+ root_muxes.at(mux_idx) = true;
+ root_enable_muxes.at(mux_idx) = true;
+ }
+ }
+
+ for (auto &it : mux_to_users)
+ if (GetSize(it.second) > 1)
+ root_muxes.at(it.first) = true;
+
+ for (int mux_idx = 0; mux_idx < GetSize(root_muxes); mux_idx++)
+ if (root_muxes.at(mux_idx)) {
+ log(" Root of a mux tree: %s%s\n", log_id(mux2info[mux_idx].cell), root_enable_muxes.at(mux_idx) ? " (pure)" : "");
+ root_mux_rerun.erase(mux_idx);
+ eval_root_mux(mux_idx);
+ }
+
+ while (!root_mux_rerun.empty()) {
+ int mux_idx = *root_mux_rerun.begin();
+ log(" Root of a mux tree: %s (rerun as non-pure)\n", log_id(mux2info[mux_idx].cell));
+ log_assert(root_enable_muxes.at(mux_idx));
+ root_mux_rerun.erase(mux_idx);
+ eval_root_mux(mux_idx);
+ }
+
+ log(" Analyzing evaluation results.\n");
+
+ for (auto &mi : mux2info)
+ {
+ vector<int> live_ports;
+ for (int port_idx = 0; port_idx < GetSize(mi.ports); port_idx++) {
+ portinfo_t &pi = mi.ports[port_idx];
+ if (pi.enabled) {
+ live_ports.push_back(port_idx);
+ } else {
+ log(" dead port %d/%d on %s %s.\n", port_idx+1, GetSize(mi.ports),
+ mi.cell->type.c_str(), mi.cell->name.c_str());
+ removed_count++;
+ }
+ }
+
+ if (GetSize(live_ports) == GetSize(mi.ports))
+ continue;
+
+ if (live_ports.empty()) {
+ module->remove(mi.cell);
+ continue;
+ }
+
+ RTLIL::SigSpec sig_a = mi.cell->getPort("\\A");
+ RTLIL::SigSpec sig_b = mi.cell->getPort("\\B");
+ RTLIL::SigSpec sig_s = mi.cell->getPort("\\S");
+ RTLIL::SigSpec sig_y = mi.cell->getPort("\\Y");
+
+ RTLIL::SigSpec sig_ports = sig_b;
+ sig_ports.append(sig_a);
+
+ if (GetSize(live_ports) == 1)
+ {
+ RTLIL::SigSpec sig_in = sig_ports.extract(live_ports[0]*GetSize(sig_a), GetSize(sig_a));
+ module->connect(RTLIL::SigSig(sig_y, sig_in));
+ module->remove(mi.cell);
+ }
+ else
+ {
+ RTLIL::SigSpec new_sig_a, new_sig_b, new_sig_s;
+
+ for (int i = 0; i < GetSize(live_ports); i++) {
+ RTLIL::SigSpec sig_in = sig_ports.extract(live_ports[i]*GetSize(sig_a), GetSize(sig_a));
+ if (i == GetSize(live_ports)-1) {
+ new_sig_a = sig_in;
+ } else {
+ new_sig_b.append(sig_in);
+ new_sig_s.append(sig_s.extract(live_ports[i], 1));
+ }
+ }
+
+ mi.cell->setPort("\\A", new_sig_a);
+ mi.cell->setPort("\\B", new_sig_b);
+ mi.cell->setPort("\\S", new_sig_s);
+ if (GetSize(new_sig_s) == 1) {
+ mi.cell->type = "$mux";
+ mi.cell->parameters.erase("\\S_WIDTH");
+ } else {
+ mi.cell->parameters["\\S_WIDTH"] = RTLIL::Const(GetSize(new_sig_s));
+ }
+ }
+ }
+ }
+
+ vector<int> sig2bits(RTLIL::SigSpec sig, bool skip_non_wires = true)
+ {
+ vector<int> results;
+ assign_map.apply(sig);
+ for (auto &bit : sig)
+ if (bit.wire != NULL) {
+ if (bit2num.count(bit) == 0) {
+ bitinfo_t info;
+ info.seen_non_mux = false;
+ bit2num.expect(bit, GetSize(bit2info));
+ bit2info.push_back(info);
+ }
+ results.push_back(bit2num.at(bit));
+ } else if (!skip_non_wires)
+ results.push_back(-1);
+ return results;
+ }
+
+ struct knowledge_t
+ {
+ // database of known inactive signals
+ // the payload is a reference counter used to manage the
+ // list. when it is non-zero the signal in known to be inactive
+ vector<int> known_inactive;
+
+ // database of known active signals
+ vector<int> known_active;
+
+ // this is just used to keep track of visited muxes in order to prohibit
+ // endless recursion in mux loops
+ vector<bool> visited_muxes;
+ };
+
+ void eval_mux_port(knowledge_t &knowledge, int mux_idx, int port_idx, bool do_replace_known, bool do_enable_ports, int abort_count)
+ {
+ muxinfo_t &muxinfo = mux2info[mux_idx];
+
+ if (do_enable_ports)
+ muxinfo.ports[port_idx].enabled = true;
+
+ for (int i = 0; i < GetSize(muxinfo.ports); i++) {
+ if (i == port_idx)
+ continue;
+ if (muxinfo.ports[i].ctrl_sig >= 0)
+ knowledge.known_inactive.at(muxinfo.ports[i].ctrl_sig)++;
+ }
+
+ if (port_idx < GetSize(muxinfo.ports)-1 && !muxinfo.ports[port_idx].const_activated)
+ knowledge.known_active.at(muxinfo.ports[port_idx].ctrl_sig)++;
+
+ vector<int> parent_muxes;
+ for (int m : muxinfo.ports[port_idx].input_muxes) {
+ if (knowledge.visited_muxes[m])
+ continue;
+ knowledge.visited_muxes[m] = true;
+ parent_muxes.push_back(m);
+ }
+ for (int m : parent_muxes)
+ if (root_enable_muxes.at(m))
+ continue;
+ else if (root_muxes.at(m)) {
+ if (abort_count == 0) {
+ root_mux_rerun.insert(m);
+ root_enable_muxes.at(m) = true;
+ log(" Removing pure flag from root mux %s.\n", log_id(mux2info[m].cell));
+ } else
+ eval_mux(knowledge, m, false, do_enable_ports, abort_count - 1);
+ } else
+ eval_mux(knowledge, m, do_replace_known, do_enable_ports, abort_count);
+ for (int m : parent_muxes)
+ knowledge.visited_muxes[m] = false;
+
+ if (port_idx < GetSize(muxinfo.ports)-1 && !muxinfo.ports[port_idx].const_activated)
+ knowledge.known_active.at(muxinfo.ports[port_idx].ctrl_sig)--;
+
+ for (int i = 0; i < GetSize(muxinfo.ports); i++) {
+ if (i == port_idx)
+ continue;
+ if (muxinfo.ports[i].ctrl_sig >= 0)
+ knowledge.known_inactive.at(muxinfo.ports[i].ctrl_sig)--;
+ }
+ }
+
+ void replace_known(knowledge_t &knowledge, muxinfo_t &muxinfo, IdString portname)
+ {
+ SigSpec sig = muxinfo.cell->getPort(portname);
+ bool did_something = false;
+
+ int width = 0;
+ idict<int> ctrl_bits;
+ if (portname == "\\B")
+ width = GetSize(muxinfo.cell->getPort("\\A"));
+ for (int bit : sig2bits(muxinfo.cell->getPort("\\S"), false))
+ ctrl_bits(bit);
+
+ int port_idx = 0, port_off = 0;
+ vector<int> bits = sig2bits(sig, false);
+ for (int i = 0; i < GetSize(bits); i++) {
+ if (bits[i] < 0)
+ continue;
+ if (knowledge.known_inactive.at(bits[i])) {
+ sig[i] = State::S0;
+ did_something = true;
+ } else
+ if (knowledge.known_active.at(bits[i])) {
+ sig[i] = State::S1;
+ did_something = true;
+ }
+ if (width) {
+ if (ctrl_bits.count(bits[i])) {
+ sig[i] = ctrl_bits.at(bits[i]) == port_idx ? State::S1 : State::S0;
+ did_something = true;
+ }
+ if (++port_off == width)
+ port_idx++, port_off=0;
+ } else {
+ if (ctrl_bits.count(bits[i])) {
+ sig[i] = State::S0;
+ did_something = true;
+ }
+ }
+ }
+
+ if (did_something) {
+ log(" Replacing known input bits on port %s of cell %s: %s -> %s\n", log_id(portname),
+ log_id(muxinfo.cell), log_signal(muxinfo.cell->getPort(portname)), log_signal(sig));
+ muxinfo.cell->setPort(portname, sig);
+ }
+ }
+
+ void eval_mux(knowledge_t &knowledge, int mux_idx, bool do_replace_known, bool do_enable_ports, int abort_count)
+ {
+ muxinfo_t &muxinfo = mux2info[mux_idx];
+
+ // set input ports to constants if we find known active or inactive signals
+ if (do_replace_known) {
+ replace_known(knowledge, muxinfo, "\\A");
+ replace_known(knowledge, muxinfo, "\\B");
+ }
+
+ // if there is a constant activated port we just use it
+ for (int port_idx = 0; port_idx < GetSize(muxinfo.ports); port_idx++)
+ {
+ portinfo_t &portinfo = muxinfo.ports[port_idx];
+ if (portinfo.const_activated) {
+ eval_mux_port(knowledge, mux_idx, port_idx, do_replace_known, do_enable_ports, abort_count);
+ return;
+ }
+ }
+
+ // compare ports with known_active signals. if we find a match, only this
+ // port can be active. do not include the last port (its the default port
+ // that has no control signals).
+ for (int port_idx = 0; port_idx < GetSize(muxinfo.ports)-1; port_idx++)
+ {
+ portinfo_t &portinfo = muxinfo.ports[port_idx];
+ if (portinfo.const_deactivated)
+ continue;
+ if (knowledge.known_active.at(portinfo.ctrl_sig)) {
+ eval_mux_port(knowledge, mux_idx, port_idx, do_replace_known, do_enable_ports, abort_count);
+ return;
+ }
+ }
+
+ // eval all ports that could be activated (control signal is not in
+ // known_inactive or const_deactivated).
+ for (int port_idx = 0; port_idx < GetSize(muxinfo.ports); port_idx++)
+ {
+ portinfo_t &portinfo = muxinfo.ports[port_idx];
+ if (portinfo.const_deactivated)
+ continue;
+ if (port_idx < GetSize(muxinfo.ports)-1)
+ 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);
+ }
+ }
+
+ void eval_root_mux(int mux_idx)
+ {
+ knowledge_t knowledge;
+ knowledge.known_inactive.resize(GetSize(bit2info));
+ knowledge.known_active.resize(GetSize(bit2info));
+ knowledge.visited_muxes.resize(GetSize(mux2info));
+ knowledge.visited_muxes[mux_idx] = true;
+ eval_mux(knowledge, mux_idx, true, root_enable_muxes.at(mux_idx), 3);
+ }
+};
+
+struct OptMuxtreePass : public Pass {
+ OptMuxtreePass() : Pass("opt_muxtree", "eliminate dead trees in multiplexer trees") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" opt_muxtree [selection]\n");
+ log("\n");
+ log("This pass analyzes the control signals for the multiplexer trees in the design\n");
+ log("and identifies inputs that can never be active. It then removes this dead\n");
+ log("branches from the multiplexer trees.\n");
+ log("\n");
+ log("This pass only operates on completely selected modules without processes.\n");
+ log("\n");
+ }
+ virtual void execute(vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n");
+ extra_args(args, 1, design);
+
+ int total_count = 0;
+ for (auto module : design->selected_whole_modules_warn()) {
+ if (module->has_processes_warn())
+ continue;
+ OptMuxtreeWorker worker(design, module);
+ total_count += worker.removed_count;
+ }
+ if (total_count)
+ design->scratchpad_set_bool("opt.did_something", true);
+ log("Removed %d multiplexer ports.\n", total_count);
+ }
+} OptMuxtreePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc
new file mode 100644
index 00000000..eb9d02ad
--- /dev/null
+++ b/passes/opt/opt_reduce.cc
@@ -0,0 +1,388 @@
+/*
+ * 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/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include "kernel/celltypes.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <set>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct OptReduceWorker
+{
+ RTLIL::Design *design;
+ RTLIL::Module *module;
+ SigMap assign_map;
+
+ int total_count;
+ bool did_something;
+
+ void opt_reduce(pool<RTLIL::Cell*> &cells, SigSet<RTLIL::Cell*> &drivers, RTLIL::Cell *cell)
+ {
+ if (cells.count(cell) == 0)
+ return;
+ cells.erase(cell);
+
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
+ pool<RTLIL::SigBit> new_sig_a_bits;
+
+ for (auto &bit : sig_a.to_sigbit_set())
+ {
+ if (bit == RTLIL::State::S0) {
+ if (cell->type == "$reduce_and") {
+ new_sig_a_bits.clear();
+ new_sig_a_bits.insert(RTLIL::State::S0);
+ break;
+ }
+ continue;
+ }
+ if (bit == RTLIL::State::S1) {
+ if (cell->type == "$reduce_or") {
+ new_sig_a_bits.clear();
+ new_sig_a_bits.insert(RTLIL::State::S1);
+ break;
+ }
+ continue;
+ }
+ if (bit.wire == NULL) {
+ new_sig_a_bits.insert(bit);
+ continue;
+ }
+
+ bool imported_children = false;
+ for (auto child_cell : drivers.find(bit)) {
+ if (child_cell->type == cell->type) {
+ opt_reduce(cells, drivers, child_cell);
+ if (child_cell->getPort("\\Y")[0] == bit) {
+ pool<RTLIL::SigBit> child_sig_a_bits = assign_map(child_cell->getPort("\\A")).to_sigbit_pool();
+ new_sig_a_bits.insert(child_sig_a_bits.begin(), child_sig_a_bits.end());
+ } else
+ new_sig_a_bits.insert(RTLIL::State::S0);
+ imported_children = true;
+ }
+ }
+ if (!imported_children)
+ new_sig_a_bits.insert(bit);
+ }
+
+ RTLIL::SigSpec new_sig_a(new_sig_a_bits);
+
+ if (new_sig_a != sig_a || sig_a.size() != cell->getPort("\\A").size()) {
+ log(" New input vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_a));
+ did_something = true;
+ total_count++;
+ }
+
+ cell->setPort("\\A", new_sig_a);
+ cell->parameters["\\A_WIDTH"] = RTLIL::Const(new_sig_a.size());
+ return;
+ }
+
+ void opt_mux(RTLIL::Cell *cell)
+ {
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
+ RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B"));
+ RTLIL::SigSpec sig_s = assign_map(cell->getPort("\\S"));
+
+ RTLIL::SigSpec new_sig_b, new_sig_s;
+ pool<RTLIL::SigSpec> handled_sig;
+
+ handled_sig.insert(sig_a);
+ for (int i = 0; i < sig_s.size(); i++)
+ {
+ RTLIL::SigSpec this_b = sig_b.extract(i*sig_a.size(), sig_a.size());
+ if (handled_sig.count(this_b) > 0)
+ continue;
+
+ RTLIL::SigSpec this_s = sig_s.extract(i, 1);
+ for (int j = i+1; j < sig_s.size(); j++) {
+ RTLIL::SigSpec that_b = sig_b.extract(j*sig_a.size(), sig_a.size());
+ if (this_b == that_b)
+ this_s.append(sig_s.extract(j, 1));
+ }
+
+ if (this_s.size() > 1)
+ {
+ RTLIL::Cell *reduce_or_cell = module->addCell(NEW_ID, "$reduce_or");
+ reduce_or_cell->setPort("\\A", this_s);
+ reduce_or_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0);
+ reduce_or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(this_s.size());
+ reduce_or_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
+
+ RTLIL::Wire *reduce_or_wire = module->addWire(NEW_ID);
+ this_s = RTLIL::SigSpec(reduce_or_wire);
+ reduce_or_cell->setPort("\\Y", this_s);
+ }
+
+ new_sig_b.append(this_b);
+ new_sig_s.append(this_s);
+ handled_sig.insert(this_b);
+ }
+
+ if (new_sig_s.size() != sig_s.size()) {
+ log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s));
+ did_something = true;
+ total_count++;
+ }
+
+ if (new_sig_s.size() == 0)
+ {
+ module->connect(RTLIL::SigSig(cell->getPort("\\Y"), cell->getPort("\\A")));
+ assign_map.add(cell->getPort("\\Y"), cell->getPort("\\A"));
+ module->remove(cell);
+ }
+ else
+ {
+ cell->setPort("\\B", new_sig_b);
+ cell->setPort("\\S", new_sig_s);
+ if (new_sig_s.size() > 1) {
+ cell->parameters["\\S_WIDTH"] = RTLIL::Const(new_sig_s.size());
+ } else {
+ cell->type = "$mux";
+ cell->parameters.erase("\\S_WIDTH");
+ }
+ }
+ }
+
+ void opt_mux_bits(RTLIL::Cell *cell)
+ {
+ std::vector<RTLIL::SigBit> sig_a = assign_map(cell->getPort("\\A")).to_sigbit_vector();
+ std::vector<RTLIL::SigBit> sig_b = assign_map(cell->getPort("\\B")).to_sigbit_vector();
+ std::vector<RTLIL::SigBit> sig_y = assign_map(cell->getPort("\\Y")).to_sigbit_vector();
+
+ std::vector<RTLIL::SigBit> new_sig_y;
+ RTLIL::SigSig old_sig_conn;
+
+ std::vector<std::vector<RTLIL::SigBit>> consolidated_in_tuples;
+ std::map<std::vector<RTLIL::SigBit>, RTLIL::SigBit> consolidated_in_tuples_map;
+
+ for (int i = 0; i < int(sig_y.size()); i++)
+ {
+ std::vector<RTLIL::SigBit> in_tuple;
+ bool all_tuple_bits_same = true;
+
+ in_tuple.push_back(sig_a.at(i));
+ for (int j = i; j < int(sig_b.size()); j += int(sig_a.size())) {
+ if (sig_b.at(j) != sig_a.at(i))
+ all_tuple_bits_same = false;
+ in_tuple.push_back(sig_b.at(j));
+ }
+
+ if (all_tuple_bits_same)
+ {
+ old_sig_conn.first.append_bit(sig_y.at(i));
+ old_sig_conn.second.append_bit(sig_a.at(i));
+ }
+ else if (consolidated_in_tuples_map.count(in_tuple))
+ {
+ old_sig_conn.first.append_bit(sig_y.at(i));
+ old_sig_conn.second.append_bit(consolidated_in_tuples_map.at(in_tuple));
+ }
+ else
+ {
+ consolidated_in_tuples_map[in_tuple] = sig_y.at(i);
+ consolidated_in_tuples.push_back(in_tuple);
+ new_sig_y.push_back(sig_y.at(i));
+ }
+ }
+
+ if (new_sig_y.size() != sig_y.size())
+ {
+ log(" Consolidated identical input bits for %s cell %s:\n", cell->type.c_str(), cell->name.c_str());
+ log(" Old ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort("\\A")),
+ log_signal(cell->getPort("\\B")), log_signal(cell->getPort("\\Y")));
+
+ cell->setPort("\\A", RTLIL::SigSpec());
+ for (auto &in_tuple : consolidated_in_tuples) {
+ RTLIL::SigSpec new_a = cell->getPort("\\A");
+ new_a.append(in_tuple.at(0));
+ cell->setPort("\\A", new_a);
+ }
+
+ cell->setPort("\\B", RTLIL::SigSpec());
+ for (int i = 1; i <= cell->getPort("\\S").size(); i++)
+ for (auto &in_tuple : consolidated_in_tuples) {
+ RTLIL::SigSpec new_b = cell->getPort("\\B");
+ new_b.append(in_tuple.at(i));
+ cell->setPort("\\B", new_b);
+ }
+
+ cell->parameters["\\WIDTH"] = RTLIL::Const(new_sig_y.size());
+ cell->setPort("\\Y", new_sig_y);
+
+ log(" New ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort("\\A")),
+ log_signal(cell->getPort("\\B")), log_signal(cell->getPort("\\Y")));
+ log(" New connections: %s = %s\n", log_signal(old_sig_conn.first), log_signal(old_sig_conn.second));
+
+ module->connect(old_sig_conn);
+ module->check();
+
+ did_something = true;
+ total_count++;
+ }
+ }
+
+ OptReduceWorker(RTLIL::Design *design, RTLIL::Module *module, bool do_fine) :
+ design(design), module(module), assign_map(module)
+ {
+ log(" Optimizing cells in module %s.\n", module->name.c_str());
+
+ total_count = 0;
+ did_something = true;
+
+ SigPool mem_wren_sigs;
+ for (auto &cell_it : module->cells_) {
+ RTLIL::Cell *cell = cell_it.second;
+ if (cell->type == "$mem")
+ mem_wren_sigs.add(assign_map(cell->getPort("\\WR_EN")));
+ if (cell->type == "$memwr")
+ mem_wren_sigs.add(assign_map(cell->getPort("\\EN")));
+ }
+ for (auto &cell_it : module->cells_) {
+ RTLIL::Cell *cell = cell_it.second;
+ if (cell->type == "$dff" && mem_wren_sigs.check_any(assign_map(cell->getPort("\\Q"))))
+ mem_wren_sigs.add(assign_map(cell->getPort("\\D")));
+ }
+
+ bool keep_expanding_mem_wren_sigs = true;
+ while (keep_expanding_mem_wren_sigs) {
+ keep_expanding_mem_wren_sigs = false;
+ for (auto &cell_it : module->cells_) {
+ RTLIL::Cell *cell = cell_it.second;
+ if (cell->type == "$mux" && mem_wren_sigs.check_any(assign_map(cell->getPort("\\Y")))) {
+ if (!mem_wren_sigs.check_all(assign_map(cell->getPort("\\A"))) ||
+ !mem_wren_sigs.check_all(assign_map(cell->getPort("\\B"))))
+ keep_expanding_mem_wren_sigs = true;
+ mem_wren_sigs.add(assign_map(cell->getPort("\\A")));
+ mem_wren_sigs.add(assign_map(cell->getPort("\\B")));
+ }
+ }
+ }
+
+ while (did_something)
+ {
+ did_something = false;
+
+ // merge trees of reduce_* cells to one single cell and unify input vectors
+ // (only handle reduce_and and reduce_or for various reasons)
+
+ const char *type_list[] = { "$reduce_or", "$reduce_and" };
+ for (auto type : type_list)
+ {
+ SigSet<RTLIL::Cell*> drivers;
+ pool<RTLIL::Cell*> cells;
+
+ for (auto &cell_it : module->cells_) {
+ RTLIL::Cell *cell = cell_it.second;
+ if (cell->type != type || !design->selected(module, cell))
+ continue;
+ drivers.insert(assign_map(cell->getPort("\\Y")), cell);
+ cells.insert(cell);
+ }
+
+ while (cells.size() > 0) {
+ RTLIL::Cell *cell = *cells.begin();
+ opt_reduce(cells, drivers, cell);
+ }
+ }
+
+ // merge identical inputs on $mux and $pmux cells
+
+ std::vector<RTLIL::Cell*> cells;
+
+ for (auto &it : module->cells_)
+ if ((it.second->type == "$mux" || it.second->type == "$pmux") && design->selected(module, it.second))
+ cells.push_back(it.second);
+
+ for (auto cell : cells)
+ {
+ // this optimization is to aggressive for most coarse-grain applications.
+ // but we always want it for multiplexers driving write enable ports.
+ if (do_fine || mem_wren_sigs.check_any(assign_map(cell->getPort("\\Y"))))
+ opt_mux_bits(cell);
+
+ opt_mux(cell);
+ }
+ }
+ }
+};
+
+struct OptReducePass : public Pass {
+ OptReducePass() : Pass("opt_reduce", "simplify large MUXes and AND/OR gates") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" opt_reduce [options] [selection]\n");
+ log("\n");
+ log("This pass performs two interlinked optimizations:\n");
+ log("\n");
+ log("1. it consolidates trees of large AND gates or OR gates and eliminates\n");
+ log("duplicated inputs.\n");
+ log("\n");
+ log("2. it identifies duplicated inputs to MUXes and replaces them with a single\n");
+ log("input with the original control signals OR'ed together.\n");
+ log("\n");
+ log(" -fine\n");
+ log(" perform fine-grain optimizations\n");
+ log("\n");
+ log(" -full\n");
+ log(" alias for -fine\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool do_fine = false;
+
+ log_header(design, "Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-fine") {
+ do_fine = true;
+ continue;
+ }
+ if (args[argidx] == "-full") {
+ do_fine = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ int total_count = 0;
+ for (auto module : design->selected_modules())
+ while (1) {
+ OptReduceWorker worker(design, module, do_fine);
+ total_count += worker.total_count;
+ if (worker.total_count == 0)
+ break;
+ }
+
+ if (total_count)
+ design->scratchpad_set_bool("opt.did_something", true);
+ log("Performed a total of %d changes.\n", total_count);
+ }
+} OptReducePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc
new file mode 100644
index 00000000..922f086f
--- /dev/null
+++ b/passes/opt/opt_rmdff.cc
@@ -0,0 +1,325 @@
+/*
+ * 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/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+SigMap assign_map, dff_init_map;
+SigSet<RTLIL::Cell*> mux_drivers;
+dict<SigBit, pool<SigBit>> init_attributes;
+bool keepdc;
+
+void remove_init_attr(SigSpec sig)
+{
+ for (auto bit : assign_map(sig))
+ if (init_attributes.count(bit))
+ for (auto wbit : init_attributes.at(bit))
+ wbit.wire->attributes.at("\\init")[wbit.offset] = State::Sx;
+}
+
+bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch)
+{
+ SigSpec sig_e = dlatch->getPort("\\EN");
+
+ if (sig_e == State::S0)
+ {
+ RTLIL::Const val_init;
+ for (auto bit : dff_init_map(dlatch->getPort("\\Q")))
+ val_init.bits.push_back(bit.wire == NULL ? bit.data : State::Sx);
+ mod->connect(dlatch->getPort("\\Q"), val_init);
+ goto delete_dlatch;
+ }
+
+ if (sig_e == State::S1)
+ {
+ mod->connect(dlatch->getPort("\\Q"), dlatch->getPort("\\D"));
+ goto delete_dlatch;
+ }
+
+ return false;
+
+delete_dlatch:
+ log("Removing %s (%s) from module %s.\n", log_id(dlatch), log_id(dlatch->type), log_id(mod));
+ remove_init_attr(dlatch->getPort("\\Q"));
+ mod->remove(dlatch);
+ return true;
+}
+
+bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
+{
+ RTLIL::SigSpec sig_d, sig_q, sig_c, sig_r;
+ RTLIL::Const val_cp, val_rp, val_rv;
+
+ if (dff->type == "$_FF_") {
+ sig_d = dff->getPort("\\D");
+ sig_q = dff->getPort("\\Q");
+ }
+ else if (dff->type == "$_DFF_N_" || dff->type == "$_DFF_P_") {
+ sig_d = dff->getPort("\\D");
+ sig_q = dff->getPort("\\Q");
+ sig_c = dff->getPort("\\C");
+ val_cp = RTLIL::Const(dff->type == "$_DFF_P_", 1);
+ }
+ else if (dff->type.substr(0,6) == "$_DFF_" && dff->type.substr(9) == "_" &&
+ (dff->type[6] == 'N' || dff->type[6] == 'P') &&
+ (dff->type[7] == 'N' || dff->type[7] == 'P') &&
+ (dff->type[8] == '0' || dff->type[8] == '1')) {
+ sig_d = dff->getPort("\\D");
+ sig_q = dff->getPort("\\Q");
+ sig_c = dff->getPort("\\C");
+ sig_r = dff->getPort("\\R");
+ val_cp = RTLIL::Const(dff->type[6] == 'P', 1);
+ val_rp = RTLIL::Const(dff->type[7] == 'P', 1);
+ val_rv = RTLIL::Const(dff->type[8] == '1', 1);
+ }
+ else if (dff->type == "$ff") {
+ sig_d = dff->getPort("\\D");
+ sig_q = dff->getPort("\\Q");
+ }
+ else if (dff->type == "$dff") {
+ sig_d = dff->getPort("\\D");
+ sig_q = dff->getPort("\\Q");
+ sig_c = dff->getPort("\\CLK");
+ val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1);
+ }
+ else if (dff->type == "$adff") {
+ sig_d = dff->getPort("\\D");
+ sig_q = dff->getPort("\\Q");
+ sig_c = dff->getPort("\\CLK");
+ sig_r = dff->getPort("\\ARST");
+ val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1);
+ val_rp = RTLIL::Const(dff->parameters["\\ARST_POLARITY"].as_bool(), 1);
+ val_rv = dff->parameters["\\ARST_VALUE"];
+ }
+ else
+ log_abort();
+
+ assign_map.apply(sig_d);
+ assign_map.apply(sig_q);
+ assign_map.apply(sig_c);
+ assign_map.apply(sig_r);
+
+ bool has_init = false;
+ RTLIL::Const val_init;
+ for (auto bit : dff_init_map(sig_q).to_sigbit_vector()) {
+ if (bit.wire == NULL || keepdc)
+ has_init = true;
+ val_init.bits.push_back(bit.wire == NULL ? bit.data : RTLIL::State::Sx);
+ }
+
+ if (dff->type.in("$ff", "$dff") && mux_drivers.has(sig_d)) {
+ std::set<RTLIL::Cell*> muxes;
+ mux_drivers.find(sig_d, muxes);
+ for (auto mux : muxes) {
+ RTLIL::SigSpec sig_a = assign_map(mux->getPort("\\A"));
+ RTLIL::SigSpec sig_b = assign_map(mux->getPort("\\B"));
+ if (sig_a == sig_q && sig_b.is_fully_const() && (!has_init || val_init == sig_b.as_const())) {
+ mod->connect(sig_q, sig_b);
+ goto delete_dff;
+ }
+ if (sig_b == sig_q && sig_a.is_fully_const() && (!has_init || val_init == sig_a.as_const())) {
+ mod->connect(sig_q, sig_a);
+ goto delete_dff;
+ }
+ }
+ }
+
+ if (!sig_c.empty() && sig_c.is_fully_const() && (!sig_r.size() || !has_init || val_init == val_rv)) {
+ if (val_rv.bits.size() == 0)
+ val_rv = val_init;
+ mod->connect(sig_q, val_rv);
+ goto delete_dff;
+ }
+
+ if (sig_d.is_fully_undef() && sig_r.size() && (!has_init || val_init == val_rv)) {
+ mod->connect(sig_q, val_rv);
+ goto delete_dff;
+ }
+
+ if (sig_d.is_fully_undef() && !sig_r.size() && has_init) {
+ mod->connect(sig_q, val_init);
+ goto delete_dff;
+ }
+
+ if (sig_d.is_fully_const() && (!sig_r.size() || val_rv == sig_d.as_const()) && (!has_init || val_init == sig_d.as_const())) {
+ mod->connect(sig_q, sig_d);
+ goto delete_dff;
+ }
+
+ if (sig_d == sig_q && (!sig_r.size() || !has_init || val_init == val_rv)) {
+ if (sig_r.size())
+ mod->connect(sig_q, val_rv);
+ if (has_init)
+ mod->connect(sig_q, val_init);
+ goto delete_dff;
+ }
+
+ return false;
+
+delete_dff:
+ log("Removing %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
+ remove_init_attr(dff->getPort("\\Q"));
+ mod->remove(dff);
+ return true;
+}
+
+struct OptRmdffPass : public Pass {
+ OptRmdffPass() : Pass("opt_rmdff", "remove DFFs with constant inputs") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" opt_rmdff [-keepdc] [selection]\n");
+ log("\n");
+ log("This pass identifies flip-flops with constant inputs and replaces them with\n");
+ log("a constant driver.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ int total_count = 0, total_initdrv = 0;
+ log_header(design, "Executing OPT_RMDFF pass (remove dff with constant values).\n");
+
+ keepdc = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-keepdc") {
+ keepdc = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ pool<SigBit> driven_bits;
+ dict<SigBit, State> init_bits;
+
+ assign_map.set(module);
+ dff_init_map.set(module);
+
+ for (auto wire : module->wires())
+ {
+ if (wire->attributes.count("\\init") != 0) {
+ Const initval = wire->attributes.at("\\init");
+ dff_init_map.add(wire, initval);
+ for (int i = 0; i < GetSize(wire); i++) {
+ SigBit wire_bit(wire, i), mapped_bit = assign_map(wire_bit);
+ if (mapped_bit.wire) {
+ init_attributes[mapped_bit].insert(wire_bit);
+ if (i < GetSize(initval))
+ init_bits[mapped_bit] = initval[i];
+ }
+ }
+ }
+
+ if (wire->port_input) {
+ for (auto bit : assign_map(wire))
+ driven_bits.insert(bit);
+ }
+ }
+ mux_drivers.clear();
+
+ std::vector<RTLIL::IdString> dff_list;
+ std::vector<RTLIL::IdString> dlatch_list;
+ for (auto cell : module->cells())
+ {
+ for (auto &conn : cell->connections())
+ if (cell->output(conn.first) || !cell->known())
+ for (auto bit : assign_map(conn.second))
+ driven_bits.insert(bit);
+
+ if (cell->type == "$mux" || cell->type == "$pmux") {
+ if (cell->getPort("\\A").size() == cell->getPort("\\B").size())
+ mux_drivers.insert(assign_map(cell->getPort("\\Y")), cell);
+ continue;
+ }
+
+ if (!design->selected(module, cell))
+ continue;
+
+ if (cell->type.in("$_FF_", "$_DFF_N_", "$_DFF_P_",
+ "$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_",
+ "$_DFF_PN0_", "$_DFF_PN1_", "$_DFF_PP0_", "$_DFF_PP1_",
+ "$ff", "$dff", "$adff"))
+ dff_list.push_back(cell->name);
+
+ if (cell->type == "$dlatch")
+ dlatch_list.push_back(cell->name);
+ }
+
+ for (auto &id : dff_list) {
+ if (module->cell(id) != nullptr &&
+ handle_dff(module, module->cells_[id]))
+ total_count++;
+ }
+
+ for (auto &id : dlatch_list) {
+ if (module->cell(id) != nullptr &&
+ handle_dlatch(module, module->cells_[id]))
+ total_count++;
+ }
+
+ SigSpec const_init_sigs;
+
+ for (auto bit : init_bits)
+ if (!driven_bits.count(bit.first))
+ const_init_sigs.append(bit.first);
+
+ const_init_sigs.sort_and_unify();
+
+ for (SigSpec sig : const_init_sigs.chunks())
+ {
+ Const val;
+
+ for (auto bit : sig)
+ val.bits.push_back(init_bits.at(bit));
+
+ log("Promoting init spec %s = %s to constant driver in module %s.\n",
+ log_signal(sig), log_signal(val), log_id(module));
+
+ module->connect(sig, val);
+ remove_init_attr(sig);
+ total_initdrv++;
+ }
+ }
+
+ assign_map.clear();
+ mux_drivers.clear();
+
+ if (total_count || total_initdrv)
+ design->scratchpad_set_bool("opt.did_something", true);
+
+ if (total_initdrv)
+ log("Promoted %d init specs to constant drivers.\n", total_initdrv);
+
+ if (total_count)
+ log("Replaced %d DFF cells.\n", total_count);
+ }
+} OptRmdffPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/share.cc b/passes/opt/share.cc
new file mode 100644
index 00000000..22914eaa
--- /dev/null
+++ b/passes/opt/share.cc
@@ -0,0 +1,1533 @@
+/*
+ * 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/satgen.h"
+#include "kernel/sigtools.h"
+#include "kernel/modtools.h"
+#include "kernel/utils.h"
+#include "kernel/macc.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+typedef RTLIL::IdString::compare_ptr_by_name<RTLIL::Cell> cell_ptr_cmp;
+typedef std::pair<RTLIL::SigSpec, RTLIL::Const> ssc_pair_t;
+
+struct ShareWorkerConfig
+{
+ int limit;
+ bool opt_force;
+ bool opt_aggressive;
+ bool opt_fast;
+ pool<RTLIL::IdString> generic_uni_ops, generic_bin_ops, generic_cbin_ops, generic_other_ops;
+};
+
+struct ShareWorker
+{
+ ShareWorkerConfig config;
+ pool<RTLIL::IdString> generic_ops;
+
+ RTLIL::Design *design;
+ RTLIL::Module *module;
+
+ CellTypes fwd_ct, cone_ct;
+ ModWalker modwalker;
+ ModIndex mi;
+
+ pool<RTLIL::Cell*> cells_to_remove;
+ pool<RTLIL::Cell*> recursion_state;
+
+ SigMap topo_sigmap;
+ std::map<RTLIL::Cell*, std::set<RTLIL::Cell*, cell_ptr_cmp>, cell_ptr_cmp> topo_cell_drivers;
+ std::map<RTLIL::SigBit, std::set<RTLIL::Cell*, cell_ptr_cmp>> topo_bit_drivers;
+
+ std::vector<std::pair<RTLIL::SigBit, RTLIL::SigBit>> exclusive_ctrls;
+
+
+ // ------------------------------------------------------------------------------
+ // Find terminal bits -- i.e. bits that do not (exclusively) feed into a mux tree
+ // ------------------------------------------------------------------------------
+
+ pool<RTLIL::SigBit> terminal_bits;
+
+ void find_terminal_bits()
+ {
+ pool<RTLIL::SigBit> queue_bits;
+ pool<RTLIL::Cell*> visited_cells;
+
+ queue_bits.insert(modwalker.signal_outputs.begin(), modwalker.signal_outputs.end());
+
+ for (auto &it : module->cells_)
+ if (!fwd_ct.cell_known(it.second->type)) {
+ pool<RTLIL::SigBit> &bits = modwalker.cell_inputs[it.second];
+ queue_bits.insert(bits.begin(), bits.end());
+ }
+
+ terminal_bits.insert(queue_bits.begin(), queue_bits.end());
+
+ while (!queue_bits.empty())
+ {
+ pool<ModWalker::PortBit> portbits;
+ modwalker.get_drivers(portbits, queue_bits);
+ queue_bits.clear();
+
+ for (auto &pbit : portbits) {
+ if (pbit.cell->type == "$mux" || pbit.cell->type == "$pmux") {
+ pool<RTLIL::SigBit> bits = modwalker.sigmap(pbit.cell->getPort("\\S")).to_sigbit_pool();
+ terminal_bits.insert(bits.begin(), bits.end());
+ queue_bits.insert(bits.begin(), bits.end());
+ visited_cells.insert(pbit.cell);
+ }
+ if (fwd_ct.cell_known(pbit.cell->type) && visited_cells.count(pbit.cell) == 0) {
+ pool<RTLIL::SigBit> &bits = modwalker.cell_inputs[pbit.cell];
+ terminal_bits.insert(bits.begin(), bits.end());
+ queue_bits.insert(bits.begin(), bits.end());
+ visited_cells.insert(pbit.cell);
+ }
+ }
+ }
+ }
+
+
+ // ---------------------------------------------------
+ // Code for sharing and comparing MACC cells
+ // ---------------------------------------------------
+
+ static int bits_macc_port(const Macc::port_t &p, int width)
+ {
+ if (GetSize(p.in_a) == 0 || GetSize(p.in_b) == 0)
+ return min(max(GetSize(p.in_a), GetSize(p.in_b)), width);
+ return min(GetSize(p.in_a), width) * min(GetSize(p.in_b), width) / 2;
+ }
+
+ static int bits_macc(const Macc &m, int width)
+ {
+ int bits = GetSize(m.bit_ports);
+ for (auto &p : m.ports)
+ bits += bits_macc_port(p, width);
+ return bits;
+ }
+
+ static int bits_macc(RTLIL::Cell *c)
+ {
+ Macc m(c);
+ int width = GetSize(c->getPort("\\Y"));
+ return bits_macc(m, width);
+ }
+
+ static bool cmp_macc_ports(const Macc::port_t &p1, const Macc::port_t &p2)
+ {
+ bool mul1 = GetSize(p1.in_a) && GetSize(p1.in_b);
+ bool mul2 = GetSize(p2.in_a) && GetSize(p2.in_b);
+
+ int w1 = mul1 ? GetSize(p1.in_a) * GetSize(p1.in_b) : GetSize(p1.in_a) + GetSize(p1.in_b);
+ int w2 = mul2 ? GetSize(p2.in_a) * GetSize(p2.in_b) : GetSize(p2.in_a) + GetSize(p2.in_b);
+
+ if (mul1 != mul2)
+ return mul1;
+
+ if (w1 != w2)
+ return w1 > w2;
+
+ if (p1.is_signed != p2.is_signed)
+ return p1.is_signed < p2.is_signed;
+
+ if (p1.do_subtract != p2.do_subtract)
+ return p1.do_subtract < p2.do_subtract;
+
+ if (p1.in_a != p2.in_a)
+ return p1.in_a < p2.in_a;
+
+ if (p1.in_b != p2.in_b)
+ return p1.in_b < p2.in_b;
+
+ return false;
+ }
+
+ int share_macc_ports(Macc::port_t &p1, Macc::port_t &p2, int w1, int w2,
+ RTLIL::SigSpec act = RTLIL::SigSpec(), Macc *supermacc = nullptr, pool<RTLIL::Cell*> *supercell_aux = nullptr)
+ {
+ if (p1.do_subtract != p2.do_subtract)
+ return -1;
+
+ bool mul1 = GetSize(p1.in_a) && GetSize(p1.in_b);
+ bool mul2 = GetSize(p2.in_a) && GetSize(p2.in_b);
+
+ if (mul1 != mul2)
+ return -1;
+
+ bool force_signed = false, force_not_signed = false;
+
+ if ((GetSize(p1.in_a) && GetSize(p1.in_a) < w1) || (GetSize(p1.in_b) && GetSize(p1.in_b) < w1)) {
+ if (p1.is_signed)
+ force_signed = true;
+ else
+ force_not_signed = true;
+ }
+
+ if ((GetSize(p2.in_a) && GetSize(p2.in_a) < w2) || (GetSize(p2.in_b) && GetSize(p2.in_b) < w2)) {
+ if (p2.is_signed)
+ force_signed = true;
+ else
+ force_not_signed = true;
+ }
+
+ if (force_signed && force_not_signed)
+ return -1;
+
+ if (supermacc)
+ {
+ RTLIL::SigSpec sig_a1 = p1.in_a, sig_b1 = p1.in_b;
+ RTLIL::SigSpec sig_a2 = p2.in_a, sig_b2 = p2.in_b;
+
+ RTLIL::SigSpec sig_a = GetSize(sig_a1) > GetSize(sig_a2) ? sig_a1 : sig_a2;
+ RTLIL::SigSpec sig_b = GetSize(sig_b1) > GetSize(sig_b2) ? sig_b1 : sig_b2;
+
+ sig_a1.extend_u0(GetSize(sig_a), p1.is_signed);
+ sig_b1.extend_u0(GetSize(sig_b), p1.is_signed);
+
+ sig_a2.extend_u0(GetSize(sig_a), p2.is_signed);
+ sig_b2.extend_u0(GetSize(sig_b), p2.is_signed);
+
+ if (supercell_aux && GetSize(sig_a)) {
+ sig_a = module->addWire(NEW_ID, GetSize(sig_a));
+ supercell_aux->insert(module->addMux(NEW_ID, sig_a2, sig_a1, act, sig_a));
+ }
+
+ if (supercell_aux && GetSize(sig_b)) {
+ sig_b = module->addWire(NEW_ID, GetSize(sig_b));
+ supercell_aux->insert(module->addMux(NEW_ID, sig_b2, sig_b1, act, sig_b));
+ }
+
+ Macc::port_t p;
+ p.in_a = sig_a;
+ p.in_b = sig_b;
+ p.is_signed = force_signed;
+ p.do_subtract = p1.do_subtract;
+ supermacc->ports.push_back(p);
+ }
+
+ int score = 1000 + abs(GetSize(p1.in_a) - GetSize(p2.in_a)) * max(abs(GetSize(p1.in_b) - GetSize(p2.in_b)), 1);
+
+ for (int i = 0; i < min(GetSize(p1.in_a), GetSize(p2.in_a)); i++)
+ if (p1.in_a[i] == p2.in_a[i] && score > 0)
+ score--;
+
+ for (int i = 0; i < min(GetSize(p1.in_b), GetSize(p2.in_b)); i++)
+ if (p1.in_b[i] == p2.in_b[i] && score > 0)
+ score--;
+
+ return score;
+ }
+
+ int share_macc(RTLIL::Cell *c1, RTLIL::Cell *c2,
+ RTLIL::SigSpec act = RTLIL::SigSpec(), RTLIL::Cell *supercell = nullptr, pool<RTLIL::Cell*> *supercell_aux = nullptr)
+ {
+ Macc m1(c1), m2(c2), supermacc;
+
+ int w1 = GetSize(c1->getPort("\\Y")), w2 = GetSize(c2->getPort("\\Y"));
+ int width = max(w1, w2);
+
+ m1.optimize(w1);
+ m2.optimize(w2);
+
+ std::sort(m1.ports.begin(), m1.ports.end(), cmp_macc_ports);
+ std::sort(m2.ports.begin(), m2.ports.end(), cmp_macc_ports);
+
+ std::set<int> m1_unmapped, m2_unmapped;
+
+ for (int i = 0; i < GetSize(m1.ports); i++)
+ m1_unmapped.insert(i);
+
+ for (int i = 0; i < GetSize(m2.ports); i++)
+ m2_unmapped.insert(i);
+
+ while (1)
+ {
+ int best_i = -1, best_j = -1, best_score = 0;
+
+ for (int i : m1_unmapped)
+ for (int j : m2_unmapped) {
+ int score = share_macc_ports(m1.ports[i], m2.ports[j], w1, w2);
+ if (score >= 0 && (best_i < 0 || best_score > score))
+ best_i = i, best_j = j, best_score = score;
+ }
+
+ if (best_i >= 0) {
+ m1_unmapped.erase(best_i);
+ m2_unmapped.erase(best_j);
+ share_macc_ports(m1.ports[best_i], m2.ports[best_j], w1, w2, act, &supermacc, supercell_aux);
+ } else
+ break;
+ }
+
+ for (int i : m1_unmapped)
+ {
+ RTLIL::SigSpec sig_a = m1.ports[i].in_a;
+ RTLIL::SigSpec sig_b = m1.ports[i].in_b;
+
+ if (supercell_aux && GetSize(sig_a)) {
+ sig_a = module->addWire(NEW_ID, GetSize(sig_a));
+ supercell_aux->insert(module->addMux(NEW_ID, RTLIL::SigSpec(0, GetSize(sig_a)), m1.ports[i].in_a, act, sig_a));
+ }
+
+ if (supercell_aux && GetSize(sig_b)) {
+ sig_b = module->addWire(NEW_ID, GetSize(sig_b));
+ supercell_aux->insert(module->addMux(NEW_ID, RTLIL::SigSpec(0, GetSize(sig_b)), m1.ports[i].in_b, act, sig_b));
+ }
+
+ Macc::port_t p;
+ p.in_a = sig_a;
+ p.in_b = sig_b;
+ p.is_signed = m1.ports[i].is_signed;
+ p.do_subtract = m1.ports[i].do_subtract;
+ supermacc.ports.push_back(p);
+ }
+
+ for (int i : m2_unmapped)
+ {
+ RTLIL::SigSpec sig_a = m2.ports[i].in_a;
+ RTLIL::SigSpec sig_b = m2.ports[i].in_b;
+
+ if (supercell_aux && GetSize(sig_a)) {
+ sig_a = module->addWire(NEW_ID, GetSize(sig_a));
+ supercell_aux->insert(module->addMux(NEW_ID, m2.ports[i].in_a, RTLIL::SigSpec(0, GetSize(sig_a)), act, sig_a));
+ }
+
+ if (supercell_aux && GetSize(sig_b)) {
+ sig_b = module->addWire(NEW_ID, GetSize(sig_b));
+ supercell_aux->insert(module->addMux(NEW_ID, m2.ports[i].in_b, RTLIL::SigSpec(0, GetSize(sig_b)), act, sig_b));
+ }
+
+ Macc::port_t p;
+ p.in_a = sig_a;
+ p.in_b = sig_b;
+ p.is_signed = m2.ports[i].is_signed;
+ p.do_subtract = m2.ports[i].do_subtract;
+ supermacc.ports.push_back(p);
+ }
+
+ if (supercell)
+ {
+ RTLIL::SigSpec sig_y = module->addWire(NEW_ID, width);
+
+ supercell_aux->insert(module->addPos(NEW_ID, sig_y, c1->getPort("\\Y")));
+ supercell_aux->insert(module->addPos(NEW_ID, sig_y, c2->getPort("\\Y")));
+
+ supercell->setParam("\\Y_WIDTH", width);
+ supercell->setPort("\\Y", sig_y);
+
+ supermacc.optimize(width);
+ supermacc.to_cell(supercell);
+ }
+
+ return bits_macc(supermacc, width);
+ }
+
+
+ // ---------------------------------------------------
+ // Find shareable cells and compatible groups of cells
+ // ---------------------------------------------------
+
+ pool<RTLIL::Cell*> shareable_cells;
+
+ void find_shareable_cells()
+ {
+ for (auto cell : module->cells())
+ {
+ if (!design->selected(module, cell) || !modwalker.ct.cell_known(cell->type))
+ continue;
+
+ for (auto &bit : modwalker.cell_outputs[cell])
+ if (terminal_bits.count(bit))
+ goto not_a_muxed_cell;
+
+ if (0)
+ not_a_muxed_cell:
+ continue;
+
+ if (config.opt_force) {
+ shareable_cells.insert(cell);
+ continue;
+ }
+
+ if (cell->type == "$memrd") {
+ if (cell->parameters.at("\\CLK_ENABLE").as_bool())
+ continue;
+ if (config.opt_aggressive || !modwalker.sigmap(cell->getPort("\\ADDR")).is_fully_const())
+ shareable_cells.insert(cell);
+ continue;
+ }
+
+ if (cell->type == "$mul" || cell->type == "$div" || cell->type == "$mod") {
+ if (config.opt_aggressive || cell->parameters.at("\\Y_WIDTH").as_int() >= 4)
+ shareable_cells.insert(cell);
+ continue;
+ }
+
+ if (cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr") {
+ if (config.opt_aggressive || cell->parameters.at("\\Y_WIDTH").as_int() >= 8)
+ shareable_cells.insert(cell);
+ continue;
+ }
+
+ if (generic_ops.count(cell->type)) {
+ if (config.opt_aggressive)
+ shareable_cells.insert(cell);
+ continue;
+ }
+ }
+ }
+
+ bool is_shareable_pair(RTLIL::Cell *c1, RTLIL::Cell *c2)
+ {
+ if (c1->type != c2->type)
+ return false;
+
+ if (c1->type == "$memrd")
+ {
+ if (c1->parameters.at("\\MEMID").decode_string() != c2->parameters.at("\\MEMID").decode_string())
+ return false;
+
+ return true;
+ }
+
+ if (config.generic_uni_ops.count(c1->type))
+ {
+ if (!config.opt_aggressive)
+ {
+ int a1_width = c1->parameters.at("\\A_WIDTH").as_int();
+ int y1_width = c1->parameters.at("\\Y_WIDTH").as_int();
+
+ int a2_width = c2->parameters.at("\\A_WIDTH").as_int();
+ int y2_width = c2->parameters.at("\\Y_WIDTH").as_int();
+
+ if (max(a1_width, a2_width) > 2 * min(a1_width, a2_width)) return false;
+ if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false;
+ }
+
+ return true;
+ }
+
+ if (config.generic_bin_ops.count(c1->type) || c1->type == "$alu")
+ {
+ if (!config.opt_aggressive)
+ {
+ int a1_width = c1->parameters.at("\\A_WIDTH").as_int();
+ int b1_width = c1->parameters.at("\\B_WIDTH").as_int();
+ int y1_width = c1->parameters.at("\\Y_WIDTH").as_int();
+
+ int a2_width = c2->parameters.at("\\A_WIDTH").as_int();
+ int b2_width = c2->parameters.at("\\B_WIDTH").as_int();
+ int y2_width = c2->parameters.at("\\Y_WIDTH").as_int();
+
+ if (max(a1_width, a2_width) > 2 * min(a1_width, a2_width)) return false;
+ if (max(b1_width, b2_width) > 2 * min(b1_width, b2_width)) return false;
+ if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false;
+ }
+
+ return true;
+ }
+
+ if (config.generic_cbin_ops.count(c1->type))
+ {
+ if (!config.opt_aggressive)
+ {
+ int a1_width = c1->parameters.at("\\A_WIDTH").as_int();
+ int b1_width = c1->parameters.at("\\B_WIDTH").as_int();
+ int y1_width = c1->parameters.at("\\Y_WIDTH").as_int();
+
+ int a2_width = c2->parameters.at("\\A_WIDTH").as_int();
+ int b2_width = c2->parameters.at("\\B_WIDTH").as_int();
+ int y2_width = c2->parameters.at("\\Y_WIDTH").as_int();
+
+ int min1_width = min(a1_width, b1_width);
+ int max1_width = max(a1_width, b1_width);
+
+ int min2_width = min(a2_width, b2_width);
+ int max2_width = max(a2_width, b2_width);
+
+ if (max(min1_width, min2_width) > 2 * min(min1_width, min2_width)) return false;
+ if (max(max1_width, max2_width) > 2 * min(max1_width, max2_width)) return false;
+ if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false;
+ }
+
+ return true;
+ }
+
+ if (c1->type == "$macc")
+ {
+ if (!config.opt_aggressive)
+ if (share_macc(c1, c2) > 2 * min(bits_macc(c1), bits_macc(c2))) return false;
+
+ return true;
+ }
+
+ for (auto &it : c1->parameters)
+ if (c2->parameters.count(it.first) == 0 || c2->parameters.at(it.first) != it.second)
+ return false;
+
+ for (auto &it : c2->parameters)
+ if (c1->parameters.count(it.first) == 0 || c1->parameters.at(it.first) != it.second)
+ return false;
+
+ return true;
+ }
+
+ void find_shareable_partners(std::vector<RTLIL::Cell*> &results, RTLIL::Cell *cell)
+ {
+ results.clear();
+ for (auto c : shareable_cells)
+ if (c != cell && is_shareable_pair(c, cell))
+ results.push_back(c);
+ }
+
+
+ // -----------------------
+ // Create replacement cell
+ // -----------------------
+
+ RTLIL::Cell *make_supercell(RTLIL::Cell *c1, RTLIL::Cell *c2, RTLIL::SigSpec act, pool<RTLIL::Cell*> &supercell_aux)
+ {
+ log_assert(c1->type == c2->type);
+
+ if (config.generic_uni_ops.count(c1->type))
+ {
+ if (c1->parameters.at("\\A_SIGNED").as_bool() != c2->parameters.at("\\A_SIGNED").as_bool())
+ {
+ RTLIL::Cell *unsigned_cell = c1->parameters.at("\\A_SIGNED").as_bool() ? c2 : c1;
+ if (unsigned_cell->getPort("\\A").to_sigbit_vector().back() != RTLIL::State::S0) {
+ unsigned_cell->parameters.at("\\A_WIDTH") = unsigned_cell->parameters.at("\\A_WIDTH").as_int() + 1;
+ RTLIL::SigSpec new_a = unsigned_cell->getPort("\\A");
+ new_a.append_bit(RTLIL::State::S0);
+ unsigned_cell->setPort("\\A", new_a);
+ }
+ unsigned_cell->parameters.at("\\A_SIGNED") = true;
+ unsigned_cell->check();
+ }
+
+ bool a_signed = c1->parameters.at("\\A_SIGNED").as_bool();
+ log_assert(a_signed == c2->parameters.at("\\A_SIGNED").as_bool());
+
+ RTLIL::SigSpec a1 = c1->getPort("\\A");
+ RTLIL::SigSpec y1 = c1->getPort("\\Y");
+
+ RTLIL::SigSpec a2 = c2->getPort("\\A");
+ RTLIL::SigSpec y2 = c2->getPort("\\Y");
+
+ int a_width = max(a1.size(), a2.size());
+ int y_width = max(y1.size(), y2.size());
+
+ a1.extend_u0(a_width, a_signed);
+ a2.extend_u0(a_width, a_signed);
+
+ RTLIL::SigSpec a = module->addWire(NEW_ID, a_width);
+ supercell_aux.insert(module->addMux(NEW_ID, a2, a1, act, a));
+
+ RTLIL::Wire *y = module->addWire(NEW_ID, y_width);
+
+ RTLIL::Cell *supercell = module->addCell(NEW_ID, c1->type);
+ supercell->parameters["\\A_SIGNED"] = a_signed;
+ supercell->parameters["\\A_WIDTH"] = a_width;
+ supercell->parameters["\\Y_WIDTH"] = y_width;
+ supercell->setPort("\\A", a);
+ supercell->setPort("\\Y", y);
+
+ supercell_aux.insert(module->addPos(NEW_ID, y, y1));
+ supercell_aux.insert(module->addPos(NEW_ID, y, y2));
+
+ supercell_aux.insert(supercell);
+ return supercell;
+ }
+
+ if (config.generic_bin_ops.count(c1->type) || config.generic_cbin_ops.count(c1->type) || c1->type == "$alu")
+ {
+ bool modified_src_cells = false;
+
+ if (config.generic_cbin_ops.count(c1->type))
+ {
+ int score_unflipped = max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()) +
+ max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int());
+
+ int score_flipped = max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()) +
+ max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int());
+
+ if (score_flipped < score_unflipped)
+ {
+ RTLIL::SigSpec tmp = c2->getPort("\\A");
+ c2->setPort("\\A", c2->getPort("\\B"));
+ c2->setPort("\\B", tmp);
+
+ std::swap(c2->parameters.at("\\A_WIDTH"), c2->parameters.at("\\B_WIDTH"));
+ std::swap(c2->parameters.at("\\A_SIGNED"), c2->parameters.at("\\B_SIGNED"));
+ modified_src_cells = true;
+ }
+ }
+
+ if (c1->parameters.at("\\A_SIGNED").as_bool() != c2->parameters.at("\\A_SIGNED").as_bool())
+
+ {
+ RTLIL::Cell *unsigned_cell = c1->parameters.at("\\A_SIGNED").as_bool() ? c2 : c1;
+ if (unsigned_cell->getPort("\\A").to_sigbit_vector().back() != RTLIL::State::S0) {
+ unsigned_cell->parameters.at("\\A_WIDTH") = unsigned_cell->parameters.at("\\A_WIDTH").as_int() + 1;
+ RTLIL::SigSpec new_a = unsigned_cell->getPort("\\A");
+ new_a.append_bit(RTLIL::State::S0);
+ unsigned_cell->setPort("\\A", new_a);
+ }
+ unsigned_cell->parameters.at("\\A_SIGNED") = true;
+ modified_src_cells = true;
+ }
+
+ if (c1->parameters.at("\\B_SIGNED").as_bool() != c2->parameters.at("\\B_SIGNED").as_bool())
+ {
+ RTLIL::Cell *unsigned_cell = c1->parameters.at("\\B_SIGNED").as_bool() ? c2 : c1;
+ if (unsigned_cell->getPort("\\B").to_sigbit_vector().back() != RTLIL::State::S0) {
+ unsigned_cell->parameters.at("\\B_WIDTH") = unsigned_cell->parameters.at("\\B_WIDTH").as_int() + 1;
+ RTLIL::SigSpec new_b = unsigned_cell->getPort("\\B");
+ new_b.append_bit(RTLIL::State::S0);
+ unsigned_cell->setPort("\\B", new_b);
+ }
+ unsigned_cell->parameters.at("\\B_SIGNED") = true;
+ modified_src_cells = true;
+ }
+
+ if (modified_src_cells) {
+ c1->check();
+ c2->check();
+ }
+
+ bool a_signed = c1->parameters.at("\\A_SIGNED").as_bool();
+ bool b_signed = c1->parameters.at("\\B_SIGNED").as_bool();
+
+ log_assert(a_signed == c2->parameters.at("\\A_SIGNED").as_bool());
+ log_assert(b_signed == c2->parameters.at("\\B_SIGNED").as_bool());
+
+ if (c1->type == "$shl" || c1->type == "$shr" || c1->type == "$sshl" || c1->type == "$sshr")
+ b_signed = false;
+
+ RTLIL::SigSpec a1 = c1->getPort("\\A");
+ RTLIL::SigSpec b1 = c1->getPort("\\B");
+ RTLIL::SigSpec y1 = c1->getPort("\\Y");
+
+ RTLIL::SigSpec a2 = c2->getPort("\\A");
+ RTLIL::SigSpec b2 = c2->getPort("\\B");
+ RTLIL::SigSpec y2 = c2->getPort("\\Y");
+
+ int a_width = max(a1.size(), a2.size());
+ int b_width = max(b1.size(), b2.size());
+ int y_width = max(y1.size(), y2.size());
+
+ if (c1->type == "$shr" && a_signed)
+ {
+ a_width = max(y_width, a_width);
+
+ if (a1.size() < y1.size()) a1.extend_u0(y1.size(), true);
+ if (a2.size() < y2.size()) a2.extend_u0(y2.size(), true);
+
+ a1.extend_u0(a_width, false);
+ a2.extend_u0(a_width, false);
+ }
+ else
+ {
+ a1.extend_u0(a_width, a_signed);
+ a2.extend_u0(a_width, a_signed);
+ }
+
+ b1.extend_u0(b_width, b_signed);
+ b2.extend_u0(b_width, b_signed);
+
+ RTLIL::SigSpec a = module->addWire(NEW_ID, a_width);
+ RTLIL::SigSpec b = module->addWire(NEW_ID, b_width);
+
+ supercell_aux.insert(module->addMux(NEW_ID, a2, a1, act, a));
+ supercell_aux.insert(module->addMux(NEW_ID, b2, b1, act, b));
+
+ RTLIL::Wire *y = module->addWire(NEW_ID, y_width);
+ RTLIL::Wire *x = c1->type == "$alu" ? module->addWire(NEW_ID, y_width) : nullptr;
+ RTLIL::Wire *co = c1->type == "$alu" ? module->addWire(NEW_ID, y_width) : nullptr;
+
+ RTLIL::Cell *supercell = module->addCell(NEW_ID, c1->type);
+ supercell->parameters["\\A_SIGNED"] = a_signed;
+ supercell->parameters["\\B_SIGNED"] = b_signed;
+ supercell->parameters["\\A_WIDTH"] = a_width;
+ supercell->parameters["\\B_WIDTH"] = b_width;
+ supercell->parameters["\\Y_WIDTH"] = y_width;
+ supercell->setPort("\\A", a);
+ supercell->setPort("\\B", b);
+ supercell->setPort("\\Y", y);
+ if (c1->type == "$alu") {
+ RTLIL::Wire *ci = module->addWire(NEW_ID), *bi = module->addWire(NEW_ID);
+ supercell_aux.insert(module->addMux(NEW_ID, c2->getPort("\\CI"), c1->getPort("\\CI"), act, ci));
+ supercell_aux.insert(module->addMux(NEW_ID, c2->getPort("\\BI"), c1->getPort("\\BI"), act, bi));
+ supercell->setPort("\\CI", ci);
+ supercell->setPort("\\BI", bi);
+ supercell->setPort("\\CO", co);
+ supercell->setPort("\\X", x);
+ }
+ supercell->check();
+
+ supercell_aux.insert(module->addPos(NEW_ID, y, y1));
+ supercell_aux.insert(module->addPos(NEW_ID, y, y2));
+ if (c1->type == "$alu") {
+ supercell_aux.insert(module->addPos(NEW_ID, co, c1->getPort("\\CO")));
+ supercell_aux.insert(module->addPos(NEW_ID, co, c2->getPort("\\CO")));
+ supercell_aux.insert(module->addPos(NEW_ID, x, c1->getPort("\\X")));
+ supercell_aux.insert(module->addPos(NEW_ID, x, c2->getPort("\\X")));
+ }
+
+ supercell_aux.insert(supercell);
+ return supercell;
+ }
+
+ if (c1->type == "$macc")
+ {
+ RTLIL::Cell *supercell = module->addCell(NEW_ID, c1->type);
+ supercell_aux.insert(supercell);
+ share_macc(c1, c2, act, supercell, &supercell_aux);
+ supercell->check();
+ return supercell;
+ }
+
+ if (c1->type == "$memrd")
+ {
+ 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));
+ supercell_aux.insert(module->addPos(NEW_ID, supercell->getPort("\\DATA"), c2->getPort("\\DATA")));
+ supercell_aux.insert(supercell);
+ return supercell;
+ }
+
+ log_abort();
+ }
+
+
+ // -------------------------------------------
+ // Finding forbidden control inputs for a cell
+ // -------------------------------------------
+
+ std::map<RTLIL::Cell*, pool<RTLIL::SigBit>, cell_ptr_cmp> forbidden_controls_cache;
+
+ const pool<RTLIL::SigBit> &find_forbidden_controls(RTLIL::Cell *cell)
+ {
+ if (recursion_state.count(cell)) {
+ static pool<RTLIL::SigBit> empty_controls_set;
+ return empty_controls_set;
+ }
+
+ if (forbidden_controls_cache.count(cell))
+ return forbidden_controls_cache.at(cell);
+
+ pool<ModWalker::PortBit> pbits;
+ pool<RTLIL::Cell*> consumer_cells;
+
+ modwalker.get_consumers(pbits, modwalker.cell_outputs[cell]);
+
+ for (auto &bit : pbits) {
+ if ((bit.cell->type == "$mux" || bit.cell->type == "$pmux") && bit.port == "\\S")
+ forbidden_controls_cache[cell].insert(bit.cell->getPort("\\S").extract(bit.offset, 1));
+ consumer_cells.insert(bit.cell);
+ }
+
+ recursion_state.insert(cell);
+
+ for (auto c : consumer_cells)
+ if (fwd_ct.cell_known(c->type)) {
+ const pool<RTLIL::SigBit> &bits = find_forbidden_controls(c);
+ forbidden_controls_cache[cell].insert(bits.begin(), bits.end());
+ }
+
+ log_assert(recursion_state.count(cell) != 0);
+ recursion_state.erase(cell);
+
+ return forbidden_controls_cache[cell];
+ }
+
+
+ // --------------------------------------------------------
+ // Finding control inputs and activation pattern for a cell
+ // --------------------------------------------------------
+
+ std::map<RTLIL::Cell*, pool<ssc_pair_t>, cell_ptr_cmp> activation_patterns_cache;
+
+ bool sort_check_activation_pattern(ssc_pair_t &p)
+ {
+ std::map<RTLIL::SigBit, RTLIL::State> p_bits;
+
+ std::vector<RTLIL::SigBit> p_first_bits = p.first;
+ for (int i = 0; i < GetSize(p_first_bits); i++) {
+ RTLIL::SigBit b = p_first_bits[i];
+ RTLIL::State v = p.second.bits[i];
+ if (p_bits.count(b) && p_bits.at(b) != v)
+ return false;
+ p_bits[b] = v;
+ }
+
+ p.first = RTLIL::SigSpec();
+ p.second.bits.clear();
+
+ for (auto &it : p_bits) {
+ p.first.append_bit(it.first);
+ p.second.bits.push_back(it.second);
+ }
+
+ return true;
+ }
+
+ void optimize_activation_patterns(pool<ssc_pair_t> &patterns)
+ {
+ // TODO: Remove patterns that are contained in other patterns
+
+ dict<SigSpec, pool<Const>> db;
+ bool did_something = false;
+
+ for (auto const &p : patterns)
+ {
+ auto &sig = p.first;
+ auto &val = p.second;
+ int len = GetSize(sig);
+
+ for (int i = 0; i < len; i++)
+ {
+ auto otherval = val;
+
+ if (otherval.bits[i] == State::S0)
+ otherval.bits[i] = State::S1;
+ else if (otherval.bits[i] == State::S1)
+ otherval.bits[i] = State::S0;
+ else
+ continue;
+
+ if (db[sig].count(otherval))
+ {
+ auto newsig = sig;
+ newsig.remove(i);
+
+ auto newval = val;
+ newval.bits.erase(newval.bits.begin() + i);
+
+ db[newsig].insert(newval);
+ db[sig].erase(otherval);
+
+ did_something = true;
+ goto next_pattern;
+ }
+ }
+
+ db[sig].insert(val);
+ next_pattern:;
+ }
+
+ if (!did_something)
+ return;
+
+ patterns.clear();
+ for (auto &it : db)
+ for (auto &val : it.second)
+ patterns.insert(make_pair(it.first, val));
+
+ optimize_activation_patterns(patterns);
+ }
+
+ const pool<ssc_pair_t> &find_cell_activation_patterns(RTLIL::Cell *cell, const char *indent)
+ {
+ if (recursion_state.count(cell)) {
+ static pool<ssc_pair_t> empty_patterns_set;
+ return empty_patterns_set;
+ }
+
+ if (activation_patterns_cache.count(cell))
+ return activation_patterns_cache.at(cell);
+
+ const pool<RTLIL::SigBit> &cell_out_bits = modwalker.cell_outputs[cell];
+ pool<RTLIL::Cell*> driven_cells, driven_data_muxes;
+
+ for (auto &bit : cell_out_bits)
+ {
+ if (terminal_bits.count(bit)) {
+ // Terminal cells are always active: unconditional activation pattern
+ activation_patterns_cache[cell].insert(ssc_pair_t());
+ return activation_patterns_cache.at(cell);
+ }
+ for (auto &pbit : modwalker.signal_consumers[bit]) {
+ log_assert(fwd_ct.cell_known(pbit.cell->type));
+ if ((pbit.cell->type == "$mux" || pbit.cell->type == "$pmux") && (pbit.port == "\\A" || pbit.port == "\\B"))
+ driven_data_muxes.insert(pbit.cell);
+ else
+ driven_cells.insert(pbit.cell);
+ }
+ }
+
+ recursion_state.insert(cell);
+
+ for (auto c : driven_data_muxes)
+ {
+ const pool<ssc_pair_t> &c_patterns = find_cell_activation_patterns(c, indent);
+
+ bool used_in_a = false;
+ std::set<int> used_in_b_parts;
+
+ int width = c->parameters.at("\\WIDTH").as_int();
+ std::vector<RTLIL::SigBit> sig_a = modwalker.sigmap(c->getPort("\\A"));
+ std::vector<RTLIL::SigBit> sig_b = modwalker.sigmap(c->getPort("\\B"));
+ std::vector<RTLIL::SigBit> sig_s = modwalker.sigmap(c->getPort("\\S"));
+
+ for (auto &bit : sig_a)
+ if (cell_out_bits.count(bit))
+ used_in_a = true;
+
+ for (int i = 0; i < GetSize(sig_b); i++)
+ if (cell_out_bits.count(sig_b[i]))
+ used_in_b_parts.insert(i / width);
+
+ if (used_in_a)
+ for (auto p : c_patterns) {
+ for (int i = 0; i < GetSize(sig_s); i++)
+ p.first.append_bit(sig_s[i]), p.second.bits.push_back(RTLIL::State::S0);
+ if (sort_check_activation_pattern(p))
+ activation_patterns_cache[cell].insert(p);
+ }
+
+ for (int idx : used_in_b_parts)
+ for (auto p : c_patterns) {
+ p.first.append_bit(sig_s[idx]), p.second.bits.push_back(RTLIL::State::S1);
+ if (sort_check_activation_pattern(p))
+ activation_patterns_cache[cell].insert(p);
+ }
+ }
+
+ for (auto c : driven_cells) {
+ const pool<ssc_pair_t> &c_patterns = find_cell_activation_patterns(c, indent);
+ activation_patterns_cache[cell].insert(c_patterns.begin(), c_patterns.end());
+ }
+
+ log_assert(recursion_state.count(cell) != 0);
+ recursion_state.erase(cell);
+
+ optimize_activation_patterns(activation_patterns_cache[cell]);
+ if (activation_patterns_cache[cell].empty()) {
+ log("%sFound cell that is never activated: %s\n", indent, log_id(cell));
+ RTLIL::SigSpec cell_outputs = modwalker.cell_outputs[cell];
+ module->connect(RTLIL::SigSig(cell_outputs, RTLIL::SigSpec(RTLIL::State::Sx, cell_outputs.size())));
+ cells_to_remove.insert(cell);
+ }
+
+ return activation_patterns_cache[cell];
+ }
+
+ RTLIL::SigSpec bits_from_activation_patterns(const pool<ssc_pair_t> &activation_patterns)
+ {
+ std::set<RTLIL::SigBit> all_bits;
+ for (auto &it : activation_patterns) {
+ std::vector<RTLIL::SigBit> bits = it.first;
+ all_bits.insert(bits.begin(), bits.end());
+ }
+
+ RTLIL::SigSpec signal;
+ for (auto &bit : all_bits)
+ signal.append_bit(bit);
+
+ return signal;
+ }
+
+ void filter_activation_patterns(pool<ssc_pair_t> &out,
+ const pool<ssc_pair_t> &in, const std::set<RTLIL::SigBit> &filter_bits)
+ {
+ for (auto &p : in)
+ {
+ std::vector<RTLIL::SigBit> p_first = p.first;
+ ssc_pair_t new_p;
+
+ for (int i = 0; i < GetSize(p_first); i++)
+ if (filter_bits.count(p_first[i]) == 0) {
+ new_p.first.append_bit(p_first[i]);
+ new_p.second.bits.push_back(p.second.bits.at(i));
+ }
+
+ out.insert(new_p);
+ }
+ }
+
+ RTLIL::SigSpec make_cell_activation_logic(const pool<ssc_pair_t> &activation_patterns, pool<RTLIL::Cell*> &supercell_aux)
+ {
+ RTLIL::Wire *all_cases_wire = module->addWire(NEW_ID, 0);
+
+ for (auto &p : activation_patterns) {
+ all_cases_wire->width++;
+ supercell_aux.insert(module->addEq(NEW_ID, p.first, p.second, RTLIL::SigSpec(all_cases_wire, all_cases_wire->width - 1)));
+ }
+
+ if (all_cases_wire->width == 1)
+ return all_cases_wire;
+
+ RTLIL::Wire *result_wire = module->addWire(NEW_ID);
+ supercell_aux.insert(module->addReduceOr(NEW_ID, all_cases_wire, result_wire));
+ return result_wire;
+ }
+
+
+ // -------------------------------------------------------------------------------------
+ // Helper functions used to make sure that this pass does not introduce new logic loops.
+ // -------------------------------------------------------------------------------------
+
+ bool module_has_scc()
+ {
+ CellTypes ct;
+ ct.setup_internals();
+ ct.setup_stdcells();
+
+ TopoSort<RTLIL::Cell*, cell_ptr_cmp> toposort;
+ toposort.analyze_loops = false;
+
+ topo_sigmap.set(module);
+ topo_bit_drivers.clear();
+
+ dict<RTLIL::Cell*, pool<RTLIL::SigBit>> cell_to_bits;
+ dict<RTLIL::SigBit, pool<RTLIL::Cell*>> bit_to_cells;
+
+ for (auto cell : module->cells())
+ if (ct.cell_known(cell->type))
+ for (auto &conn : cell->connections()) {
+ if (ct.cell_output(cell->type, conn.first))
+ for (auto bit : topo_sigmap(conn.second)) {
+ cell_to_bits[cell].insert(bit);
+ topo_bit_drivers[bit].insert(cell);
+ }
+ else
+ for (auto bit : topo_sigmap(conn.second))
+ bit_to_cells[bit].insert(cell);
+ }
+
+ for (auto &it : cell_to_bits)
+ {
+ RTLIL::Cell *c1 = it.first;
+
+ for (auto bit : it.second)
+ for (auto c2 : bit_to_cells[bit])
+ toposort.edge(c1, c2);
+ }
+
+ bool found_scc = !toposort.sort();
+ topo_cell_drivers = std::move(toposort.database);
+
+ if (found_scc && toposort.analyze_loops)
+ for (auto &loop : toposort.loops) {
+ log("### loop ###\n");
+ for (auto &c : loop)
+ log("%s (%s)\n", log_id(c), log_id(c->type));
+ }
+
+ return found_scc;
+ }
+
+ bool find_in_input_cone_worker(RTLIL::Cell *root, RTLIL::Cell *needle, pool<RTLIL::Cell*> &stop)
+ {
+ if (root == needle)
+ return true;
+
+ if (stop.count(root))
+ return false;
+
+ stop.insert(root);
+
+ for (auto c : topo_cell_drivers[root])
+ if (find_in_input_cone_worker(c, needle, stop))
+ return true;
+ return false;
+ }
+
+ bool find_in_input_cone(RTLIL::Cell *root, RTLIL::Cell *needle)
+ {
+ pool<RTLIL::Cell*> stop;
+ return find_in_input_cone_worker(root, needle, stop);
+ }
+
+ bool is_part_of_scc(RTLIL::Cell *cell)
+ {
+ CellTypes ct;
+ ct.setup_internals();
+ ct.setup_stdcells();
+
+ pool<RTLIL::Cell*> queue, covered;
+ queue.insert(cell);
+
+ while (!queue.empty())
+ {
+ pool<RTLIL::Cell*> new_queue;
+
+ for (auto c : queue) {
+ if (!ct.cell_known(c->type))
+ continue;
+ for (auto &conn : c->connections())
+ if (ct.cell_input(c->type, conn.first))
+ for (auto bit : conn.second)
+ for (auto &pi : mi.query_ports(bit))
+ if (ct.cell_known(pi.cell->type) && ct.cell_output(pi.cell->type, pi.port))
+ new_queue.insert(pi.cell);
+ covered.insert(c);
+ }
+
+ queue.clear();
+ for (auto c : new_queue) {
+ if (cells_to_remove.count(c))
+ continue;
+ if (c == cell)
+ return true;
+ if (!covered.count(c))
+ queue.insert(c);
+ }
+ }
+
+ return false;
+ }
+
+
+ // -------------
+ // Setup and run
+ // -------------
+
+ void remove_cell(Cell *cell)
+ {
+ shareable_cells.erase(cell);
+ forbidden_controls_cache.erase(cell);
+ activation_patterns_cache.erase(cell);
+ module->remove(cell);
+ }
+
+ ShareWorker(ShareWorkerConfig config, RTLIL::Design *design, RTLIL::Module *module) :
+ config(config), design(design), module(module), mi(module)
+ {
+ #ifndef NDEBUG
+ bool before_scc = module_has_scc();
+ #endif
+
+ generic_ops.insert(config.generic_uni_ops.begin(), config.generic_uni_ops.end());
+ generic_ops.insert(config.generic_bin_ops.begin(), config.generic_bin_ops.end());
+ generic_ops.insert(config.generic_cbin_ops.begin(), config.generic_cbin_ops.end());
+ generic_ops.insert(config.generic_other_ops.begin(), config.generic_other_ops.end());
+
+ fwd_ct.setup_internals();
+
+ cone_ct.setup_internals();
+ cone_ct.cell_types.erase("$mul");
+ cone_ct.cell_types.erase("$mod");
+ cone_ct.cell_types.erase("$div");
+ cone_ct.cell_types.erase("$pow");
+ cone_ct.cell_types.erase("$shl");
+ cone_ct.cell_types.erase("$shr");
+ cone_ct.cell_types.erase("$sshl");
+ cone_ct.cell_types.erase("$sshr");
+
+ modwalker.setup(design, module);
+
+ find_terminal_bits();
+ find_shareable_cells();
+
+ if (shareable_cells.size() < 2)
+ return;
+
+ log("Found %d cells in module %s that may be considered for resource sharing.\n",
+ GetSize(shareable_cells), log_id(module));
+
+ for (auto cell : module->cells())
+ if (cell->type == "$pmux")
+ for (auto bit : cell->getPort("\\S"))
+ for (auto other_bit : cell->getPort("\\S"))
+ if (bit < other_bit)
+ exclusive_ctrls.push_back(std::pair<RTLIL::SigBit, RTLIL::SigBit>(bit, other_bit));
+
+ while (!shareable_cells.empty() && config.limit != 0)
+ {
+ RTLIL::Cell *cell = *shareable_cells.begin();
+ shareable_cells.erase(cell);
+
+ log(" Analyzing resource sharing options for %s (%s):\n", log_id(cell), log_id(cell->type));
+
+ const pool<ssc_pair_t> &cell_activation_patterns = find_cell_activation_patterns(cell, " ");
+ RTLIL::SigSpec cell_activation_signals = bits_from_activation_patterns(cell_activation_patterns);
+
+ if (cell_activation_patterns.empty()) {
+ log(" Cell is never active. Sharing is pointless, we simply remove it.\n");
+ cells_to_remove.insert(cell);
+ continue;
+ }
+
+ if (cell_activation_patterns.count(ssc_pair_t())) {
+ log(" Cell is always active. Therefore no sharing is possible.\n");
+ continue;
+ }
+
+ log(" Found %d activation_patterns using ctrl signal %s.\n", GetSize(cell_activation_patterns), log_signal(cell_activation_signals));
+
+ std::vector<RTLIL::Cell*> candidates;
+ find_shareable_partners(candidates, cell);
+
+ if (candidates.empty()) {
+ log(" No candidates found.\n");
+ continue;
+ }
+
+ log(" Found %d candidates:", GetSize(candidates));
+ for (auto c : candidates)
+ log(" %s", log_id(c));
+ log("\n");
+
+ for (auto other_cell : candidates)
+ {
+ log(" Analyzing resource sharing with %s (%s):\n", log_id(other_cell), log_id(other_cell->type));
+
+ const pool<ssc_pair_t> &other_cell_activation_patterns = find_cell_activation_patterns(other_cell, " ");
+ RTLIL::SigSpec other_cell_activation_signals = bits_from_activation_patterns(other_cell_activation_patterns);
+
+ if (other_cell_activation_patterns.empty()) {
+ log(" Cell is never active. Sharing is pointless, we simply remove it.\n");
+ shareable_cells.erase(other_cell);
+ cells_to_remove.insert(other_cell);
+ continue;
+ }
+
+ if (other_cell_activation_patterns.count(ssc_pair_t())) {
+ log(" Cell is always active. Therefore no sharing is possible.\n");
+ shareable_cells.erase(other_cell);
+ continue;
+ }
+
+ log(" Found %d activation_patterns using ctrl signal %s.\n",
+ GetSize(other_cell_activation_patterns), log_signal(other_cell_activation_signals));
+
+ const pool<RTLIL::SigBit> &cell_forbidden_controls = find_forbidden_controls(cell);
+ const pool<RTLIL::SigBit> &other_cell_forbidden_controls = find_forbidden_controls(other_cell);
+
+ std::set<RTLIL::SigBit> union_forbidden_controls;
+ union_forbidden_controls.insert(cell_forbidden_controls.begin(), cell_forbidden_controls.end());
+ union_forbidden_controls.insert(other_cell_forbidden_controls.begin(), other_cell_forbidden_controls.end());
+
+ if (!union_forbidden_controls.empty())
+ log(" Forbidden control signals for this pair of cells: %s\n", log_signal(union_forbidden_controls));
+
+ pool<ssc_pair_t> filtered_cell_activation_patterns;
+ pool<ssc_pair_t> filtered_other_cell_activation_patterns;
+
+ filter_activation_patterns(filtered_cell_activation_patterns, cell_activation_patterns, union_forbidden_controls);
+ filter_activation_patterns(filtered_other_cell_activation_patterns, other_cell_activation_patterns, union_forbidden_controls);
+
+ optimize_activation_patterns(filtered_cell_activation_patterns);
+ optimize_activation_patterns(filtered_other_cell_activation_patterns);
+
+ ezSatPtr ez;
+ SatGen satgen(ez.get(), &modwalker.sigmap);
+
+ pool<RTLIL::Cell*> sat_cells;
+ std::set<RTLIL::SigBit> bits_queue;
+
+ std::vector<int> cell_active, other_cell_active;
+ RTLIL::SigSpec all_ctrl_signals;
+
+ for (auto &p : filtered_cell_activation_patterns) {
+ log(" Activation pattern for cell %s: %s = %s\n", log_id(cell), log_signal(p.first), log_signal(p.second));
+ cell_active.push_back(ez->vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second)));
+ all_ctrl_signals.append(p.first);
+ }
+
+ for (auto &p : filtered_other_cell_activation_patterns) {
+ log(" Activation pattern for cell %s: %s = %s\n", log_id(other_cell), log_signal(p.first), log_signal(p.second));
+ other_cell_active.push_back(ez->vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second)));
+ all_ctrl_signals.append(p.first);
+ }
+
+ for (auto &bit : cell_activation_signals.to_sigbit_vector())
+ bits_queue.insert(bit);
+
+ for (auto &bit : other_cell_activation_signals.to_sigbit_vector())
+ bits_queue.insert(bit);
+
+ while (!bits_queue.empty())
+ {
+ pool<ModWalker::PortBit> portbits;
+ modwalker.get_drivers(portbits, bits_queue);
+ bits_queue.clear();
+
+ for (auto &pbit : portbits)
+ if (sat_cells.count(pbit.cell) == 0 && cone_ct.cell_known(pbit.cell->type)) {
+ if (config.opt_fast && modwalker.cell_outputs[pbit.cell].size() >= 4)
+ continue;
+ // log(" Adding cell %s (%s) to SAT problem.\n", log_id(pbit.cell), log_id(pbit.cell->type));
+ bits_queue.insert(modwalker.cell_inputs[pbit.cell].begin(), modwalker.cell_inputs[pbit.cell].end());
+ satgen.importCell(pbit.cell);
+ sat_cells.insert(pbit.cell);
+ }
+
+ if (config.opt_fast && sat_cells.size() > 100)
+ break;
+ }
+
+ for (auto it : exclusive_ctrls)
+ if (satgen.importedSigBit(it.first) && satgen.importedSigBit(it.second)) {
+ log(" Adding exclusive control bits: %s vs. %s\n", log_signal(it.first), log_signal(it.second));
+ int sub1 = satgen.importSigBit(it.first);
+ int sub2 = satgen.importSigBit(it.second);
+ ez->assume(ez->NOT(ez->AND(sub1, sub2)));
+ }
+
+ if (!ez->solve(ez->expression(ez->OpOr, cell_active))) {
+ log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(cell));
+ cells_to_remove.insert(cell);
+ break;
+ }
+
+ if (!ez->solve(ez->expression(ez->OpOr, other_cell_active))) {
+ log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(other_cell));
+ cells_to_remove.insert(other_cell);
+ shareable_cells.erase(other_cell);
+ continue;
+ }
+
+ ez->non_incremental();
+
+ all_ctrl_signals.sort_and_unify();
+ std::vector<int> sat_model = satgen.importSigSpec(all_ctrl_signals);
+ std::vector<bool> sat_model_values;
+
+ int sub1 = ez->expression(ez->OpOr, cell_active);
+ int sub2 = ez->expression(ez->OpOr, other_cell_active);
+ ez->assume(ez->AND(sub1, sub2));
+
+ log(" Size of SAT problem: %d cells, %d variables, %d clauses\n",
+ GetSize(sat_cells), ez->numCnfVariables(), ez->numCnfClauses());
+
+ if (ez->solve(sat_model, sat_model_values)) {
+ log(" According to the SAT solver this pair of cells can not be shared.\n");
+ log(" Model from SAT solver: %s = %d'", log_signal(all_ctrl_signals), GetSize(sat_model_values));
+ for (int i = GetSize(sat_model_values)-1; i >= 0; i--)
+ log("%c", sat_model_values[i] ? '1' : '0');
+ log("\n");
+ continue;
+ }
+
+ log(" According to the SAT solver this pair of cells can be shared.\n");
+
+ if (find_in_input_cone(cell, other_cell)) {
+ log(" Sharing not possible: %s is in input cone of %s.\n", log_id(other_cell), log_id(cell));
+ continue;
+ }
+
+ if (find_in_input_cone(other_cell, cell)) {
+ log(" Sharing not possible: %s is in input cone of %s.\n", log_id(cell), log_id(other_cell));
+ continue;
+ }
+
+ shareable_cells.erase(other_cell);
+
+ int cell_select_score = 0;
+ int other_cell_select_score = 0;
+
+ for (auto &p : filtered_cell_activation_patterns)
+ cell_select_score += p.first.size();
+
+ for (auto &p : filtered_other_cell_activation_patterns)
+ other_cell_select_score += p.first.size();
+
+ RTLIL::Cell *supercell;
+ pool<RTLIL::Cell*> supercell_aux;
+ if (cell_select_score <= other_cell_select_score) {
+ RTLIL::SigSpec act = make_cell_activation_logic(filtered_cell_activation_patterns, supercell_aux);
+ supercell = make_supercell(cell, other_cell, act, supercell_aux);
+ log(" Activation signal for %s: %s\n", log_id(cell), log_signal(act));
+ } else {
+ RTLIL::SigSpec act = make_cell_activation_logic(filtered_other_cell_activation_patterns, supercell_aux);
+ supercell = make_supercell(other_cell, cell, act, supercell_aux);
+ log(" Activation signal for %s: %s\n", log_id(other_cell), log_signal(act));
+ }
+
+ log(" New cell: %s (%s)\n", log_id(supercell), log_id(supercell->type));
+
+ cells_to_remove.insert(cell);
+ cells_to_remove.insert(other_cell);
+
+ for (auto c : supercell_aux)
+ if (is_part_of_scc(c))
+ goto do_rollback;
+
+ if (0) {
+ do_rollback:
+ log(" New topology contains loops! Rolling back..\n");
+ cells_to_remove.erase(cell);
+ cells_to_remove.erase(other_cell);
+ shareable_cells.insert(other_cell);
+ for (auto cc : supercell_aux)
+ remove_cell(cc);
+ continue;
+ }
+
+ pool<ssc_pair_t> supercell_activation_patterns;
+ supercell_activation_patterns.insert(filtered_cell_activation_patterns.begin(), filtered_cell_activation_patterns.end());
+ supercell_activation_patterns.insert(filtered_other_cell_activation_patterns.begin(), filtered_other_cell_activation_patterns.end());
+ optimize_activation_patterns(supercell_activation_patterns);
+ activation_patterns_cache[supercell] = supercell_activation_patterns;
+ shareable_cells.insert(supercell);
+
+ for (auto bit : topo_sigmap(all_ctrl_signals))
+ for (auto c : topo_bit_drivers[bit])
+ topo_cell_drivers[supercell].insert(c);
+
+ topo_cell_drivers[supercell].insert(topo_cell_drivers[cell].begin(), topo_cell_drivers[cell].end());
+ topo_cell_drivers[supercell].insert(topo_cell_drivers[other_cell].begin(), topo_cell_drivers[other_cell].end());
+
+ topo_cell_drivers[cell] = { supercell };
+ topo_cell_drivers[other_cell] = { supercell };
+
+ if (config.limit > 0)
+ config.limit--;
+
+ break;
+ }
+ }
+
+ if (!cells_to_remove.empty()) {
+ log("Removing %d cells in module %s:\n", GetSize(cells_to_remove), log_id(module));
+ for (auto c : cells_to_remove) {
+ log(" Removing cell %s (%s).\n", log_id(c), log_id(c->type));
+ remove_cell(c);
+ }
+ }
+
+ log_assert(recursion_state.empty());
+
+ #ifndef NDEBUG
+ bool after_scc = before_scc || module_has_scc();
+ log_assert(before_scc == after_scc);
+ #endif
+ }
+};
+
+struct SharePass : public Pass {
+ SharePass() : Pass("share", "perform sat-based resource sharing") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" share [options] [selection]\n");
+ log("\n");
+ log("This pass merges shareable resources into a single resource. A SAT solver\n");
+ log("is used to determine if two resources are share-able.\n");
+ log("\n");
+ log(" -force\n");
+ log(" Per default the selection of cells that is considered for sharing is\n");
+ log(" narrowed using a list of cell types. With this option all selected\n");
+ log(" cells are considered for resource sharing.\n");
+ log("\n");
+ log(" IMPORTANT NOTE: If the -all option is used then no cells with internal\n");
+ log(" state must be selected!\n");
+ log("\n");
+ log(" -aggressive\n");
+ log(" Per default some heuristics are used to reduce the number of cells\n");
+ log(" considered for resource sharing to only large resources. This options\n");
+ log(" turns this heuristics off, resulting in much more cells being considered\n");
+ log(" for resource sharing.\n");
+ log("\n");
+ log(" -fast\n");
+ log(" Only consider the simple part of the control logic in SAT solving, resulting\n");
+ log(" in much easier SAT problems at the cost of maybe missing some opportunities\n");
+ log(" for resource sharing.\n");
+ log("\n");
+ log(" -limit N\n");
+ log(" Only perform the first N merges, then stop. This is useful for debugging.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ ShareWorkerConfig config;
+
+ config.limit = -1;
+ config.opt_force = false;
+ config.opt_aggressive = false;
+ config.opt_fast = false;
+
+ config.generic_uni_ops.insert("$not");
+ // config.generic_uni_ops.insert("$pos");
+ config.generic_uni_ops.insert("$neg");
+
+ config.generic_cbin_ops.insert("$and");
+ config.generic_cbin_ops.insert("$or");
+ config.generic_cbin_ops.insert("$xor");
+ config.generic_cbin_ops.insert("$xnor");
+
+ config.generic_bin_ops.insert("$shl");
+ config.generic_bin_ops.insert("$shr");
+ config.generic_bin_ops.insert("$sshl");
+ config.generic_bin_ops.insert("$sshr");
+
+ config.generic_bin_ops.insert("$lt");
+ config.generic_bin_ops.insert("$le");
+ config.generic_bin_ops.insert("$eq");
+ config.generic_bin_ops.insert("$ne");
+ config.generic_bin_ops.insert("$eqx");
+ config.generic_bin_ops.insert("$nex");
+ config.generic_bin_ops.insert("$ge");
+ config.generic_bin_ops.insert("$gt");
+
+ config.generic_cbin_ops.insert("$add");
+ config.generic_cbin_ops.insert("$mul");
+
+ config.generic_bin_ops.insert("$sub");
+ config.generic_bin_ops.insert("$div");
+ config.generic_bin_ops.insert("$mod");
+ // config.generic_bin_ops.insert("$pow");
+
+ config.generic_uni_ops.insert("$logic_not");
+ config.generic_cbin_ops.insert("$logic_and");
+ config.generic_cbin_ops.insert("$logic_or");
+
+ config.generic_other_ops.insert("$alu");
+ config.generic_other_ops.insert("$macc");
+
+ log_header(design, "Executing SHARE pass (SAT-based resource sharing).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-force") {
+ config.opt_force = true;
+ continue;
+ }
+ if (args[argidx] == "-aggressive") {
+ config.opt_aggressive = true;
+ continue;
+ }
+ if (args[argidx] == "-fast") {
+ config.opt_fast = true;
+ continue;
+ }
+ if (args[argidx] == "-limit" && argidx+1 < args.size()) {
+ config.limit = atoi(args[++argidx].c_str());
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto &mod_it : design->modules_)
+ if (design->selected(mod_it.second))
+ ShareWorker(config, design, mod_it.second);
+ }
+} SharePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc
new file mode 100644
index 00000000..07503fbb
--- /dev/null
+++ b/passes/opt/wreduce.cc
@@ -0,0 +1,436 @@
+/*
+ * 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/modtools.h"
+
+USING_YOSYS_NAMESPACE
+using namespace RTLIL;
+
+PRIVATE_NAMESPACE_BEGIN
+
+struct WreduceConfig
+{
+ pool<IdString> supported_cell_types;
+
+ WreduceConfig()
+ {
+ supported_cell_types = pool<IdString>({
+ "$not", "$pos", "$neg",
+ "$and", "$or", "$xor", "$xnor",
+ "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
+ "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
+ "$add", "$sub", "$mul", // "$div", "$mod", "$pow",
+ "$mux", "$pmux"
+ });
+ }
+};
+
+struct WreduceWorker
+{
+ WreduceConfig *config;
+ Module *module;
+ ModIndex mi;
+
+ std::set<Cell*, IdString::compare_ptr_by_name<Cell>> work_queue_cells;
+ std::set<SigBit> work_queue_bits;
+ pool<SigBit> keep_bits;
+
+ WreduceWorker(WreduceConfig *config, Module *module) :
+ config(config), module(module), mi(module) { }
+
+ void run_cell_mux(Cell *cell)
+ {
+ // Reduce size of MUX if inputs agree on a value for a bit or a output bit is unused
+
+ SigSpec sig_a = mi.sigmap(cell->getPort("\\A"));
+ SigSpec sig_b = mi.sigmap(cell->getPort("\\B"));
+ SigSpec sig_s = mi.sigmap(cell->getPort("\\S"));
+ SigSpec sig_y = mi.sigmap(cell->getPort("\\Y"));
+ std::vector<SigBit> bits_removed;
+
+ if (sig_y.has_const())
+ return;
+
+ for (int i = GetSize(sig_y)-1; i >= 0; i--)
+ {
+ auto info = mi.query(sig_y[i]);
+ if (!info->is_output && GetSize(info->ports) <= 1 && !keep_bits.count(mi.sigmap(sig_y[i]))) {
+ bits_removed.push_back(Sx);
+ continue;
+ }
+
+ SigBit ref = sig_a[i];
+ for (int k = 0; k < GetSize(sig_s); k++) {
+ if (ref != Sx && sig_b[k*GetSize(sig_a) + i] != Sx && ref != sig_b[k*GetSize(sig_a) + i])
+ goto no_match_ab;
+ if (sig_b[k*GetSize(sig_a) + i] != Sx)
+ ref = sig_b[k*GetSize(sig_a) + i];
+ }
+ if (0)
+ no_match_ab:
+ break;
+ bits_removed.push_back(ref);
+ }
+
+ if (bits_removed.empty())
+ return;
+
+ SigSpec sig_removed;
+ for (int i = GetSize(bits_removed)-1; i >= 0; i--)
+ sig_removed.append_bit(bits_removed[i]);
+
+ if (GetSize(bits_removed) == GetSize(sig_y)) {
+ log("Removed cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type));
+ module->connect(sig_y, sig_removed);
+ module->remove(cell);
+ return;
+ }
+
+ log("Removed top %d bits (of %d) from mux cell %s.%s (%s).\n",
+ GetSize(sig_removed), GetSize(sig_y), log_id(module), log_id(cell), log_id(cell->type));
+
+ int n_removed = GetSize(sig_removed);
+ int n_kept = GetSize(sig_y) - GetSize(sig_removed);
+
+ SigSpec new_work_queue_bits;
+ new_work_queue_bits.append(sig_a.extract(n_kept, n_removed));
+ new_work_queue_bits.append(sig_y.extract(n_kept, n_removed));
+
+ SigSpec new_sig_a = sig_a.extract(0, n_kept);
+ SigSpec new_sig_y = sig_y.extract(0, n_kept);
+ SigSpec new_sig_b;
+
+ for (int k = 0; k < GetSize(sig_s); k++) {
+ new_sig_b.append(sig_b.extract(k*GetSize(sig_a), n_kept));
+ new_work_queue_bits.append(sig_b.extract(k*GetSize(sig_a) + n_kept, n_removed));
+ }
+
+ for (auto bit : new_work_queue_bits)
+ work_queue_bits.insert(bit);
+
+ cell->setPort("\\A", new_sig_a);
+ cell->setPort("\\B", new_sig_b);
+ cell->setPort("\\Y", new_sig_y);
+ cell->fixup_parameters();
+
+ module->connect(sig_y.extract(n_kept, n_removed), sig_removed);
+ }
+
+ 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();
+ SigSpec sig = mi.sigmap(cell->getPort(stringf("\\%c", port)));
+
+ if (port == 'B' && cell->type.in("$shl", "$shr", "$sshl", "$sshr"))
+ port_signed = false;
+
+ int bits_removed = 0;
+ if (GetSize(sig) > max_port_size) {
+ bits_removed = GetSize(sig) - max_port_size;
+ for (auto bit : sig.extract(max_port_size, bits_removed))
+ work_queue_bits.insert(bit);
+ sig = sig.extract(0, max_port_size);
+ }
+
+ if (port_signed) {
+ while (GetSize(sig) > 1 && sig[GetSize(sig)-1] == sig[GetSize(sig)-2])
+ work_queue_bits.insert(sig[GetSize(sig)-1]), sig.remove(GetSize(sig)-1), bits_removed++;
+ } else {
+ while (GetSize(sig) > 1 && sig[GetSize(sig)-1] == S0)
+ work_queue_bits.insert(sig[GetSize(sig)-1]), sig.remove(GetSize(sig)-1), bits_removed++;
+ }
+
+ if (bits_removed) {
+ log("Removed top %d bits (of %d) from port %c of cell %s.%s (%s).\n",
+ bits_removed, GetSize(sig) + bits_removed, port, log_id(module), log_id(cell), log_id(cell->type));
+ cell->setPort(stringf("\\%c", port), sig);
+ did_something = true;
+ }
+ }
+
+ void run_cell(Cell *cell)
+ {
+ bool did_something = false;
+
+ if (!cell->type.in(config->supported_cell_types))
+ return;
+
+ if (cell->type.in("$mux", "$pmux"))
+ return run_cell_mux(cell);
+
+ SigSpec sig = mi.sigmap(cell->getPort("\\Y"));
+
+ if (sig.has_const())
+ return;
+
+
+ // Reduce size of ports A and B based on constant input bits and size of output port
+
+ int max_port_a_size = cell->hasPort("\\A") ? GetSize(cell->getPort("\\A")) : -1;
+ int max_port_b_size = cell->hasPort("\\B") ? GetSize(cell->getPort("\\B")) : -1;
+
+ if (cell->type.in("$not", "$pos", "$neg", "$and", "$or", "$xor", "$add", "$sub")) {
+ max_port_a_size = min(max_port_a_size, GetSize(sig));
+ max_port_b_size = min(max_port_b_size, GetSize(sig));
+ }
+
+ bool port_a_signed = false;
+ bool port_b_signed = false;
+
+ if (max_port_a_size >= 0 && cell->type != "$shiftx")
+ run_reduce_inport(cell, 'A', max_port_a_size, port_a_signed, did_something);
+
+ if (max_port_b_size >= 0)
+ run_reduce_inport(cell, 'B', max_port_b_size, port_b_signed, did_something);
+
+ if (cell->hasPort("\\A") && cell->hasPort("\\B") && port_a_signed && port_b_signed) {
+ SigSpec sig_a = mi.sigmap(cell->getPort("\\A")), sig_b = mi.sigmap(cell->getPort("\\B"));
+ if (GetSize(sig_a) > 0 && sig_a[GetSize(sig_a)-1] == State::S0 &&
+ GetSize(sig_b) > 0 && sig_b[GetSize(sig_b)-1] == State::S0) {
+ log("Converting cell %s.%s (%s) from signed to unsigned.\n",
+ log_id(module), log_id(cell), log_id(cell->type));
+ cell->setParam("\\A_SIGNED", 0);
+ cell->setParam("\\B_SIGNED", 0);
+ port_a_signed = false;
+ port_b_signed = false;
+ did_something = true;
+ }
+ }
+
+ if (cell->hasPort("\\A") && !cell->hasPort("\\B") && port_a_signed) {
+ SigSpec sig_a = mi.sigmap(cell->getPort("\\A"));
+ if (GetSize(sig_a) > 0 && sig_a[GetSize(sig_a)-1] == State::S0) {
+ log("Converting cell %s.%s (%s) from signed to unsigned.\n",
+ log_id(module), log_id(cell), log_id(cell->type));
+ cell->setParam("\\A_SIGNED", 0);
+ port_a_signed = false;
+ did_something = true;
+ }
+ }
+
+
+ // Reduce size of port Y based on sizes for A and B and unused bits in Y
+
+ int bits_removed = 0;
+ if (port_a_signed && cell->type == "$shr") {
+ // do not reduce size of output on $shr cells with signed A inputs
+ } else {
+ while (GetSize(sig) > 0)
+ {
+ auto info = mi.query(sig[GetSize(sig)-1]);
+
+ if (info->is_output || GetSize(info->ports) > 1)
+ break;
+
+ sig.remove(GetSize(sig)-1);
+ bits_removed++;
+ }
+ }
+
+ if (cell->type.in("$pos", "$add", "$mul", "$and", "$or", "$xor"))
+ {
+ bool is_signed = cell->getParam("\\A_SIGNED").as_bool();
+
+ int a_size = 0, b_size = 0;
+ if (cell->hasPort("\\A")) a_size = GetSize(cell->getPort("\\A"));
+ if (cell->hasPort("\\B")) b_size = GetSize(cell->getPort("\\B"));
+
+ int max_y_size = max(a_size, b_size);
+
+ if (cell->type == "$add")
+ max_y_size++;
+
+ if (cell->type == "$mul")
+ max_y_size = a_size + b_size;
+
+ while (GetSize(sig) > 1 && GetSize(sig) > max_y_size) {
+ module->connect(sig[GetSize(sig)-1], is_signed ? sig[GetSize(sig)-2] : S0);
+ sig.remove(GetSize(sig)-1);
+ bits_removed++;
+ }
+ }
+
+ if (GetSize(sig) == 0) {
+ log("Removed cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type));
+ module->remove(cell);
+ return;
+ }
+
+ if (bits_removed) {
+ log("Removed top %d bits (of %d) from port Y of cell %s.%s (%s).\n",
+ bits_removed, GetSize(sig) + bits_removed, log_id(module), log_id(cell), log_id(cell->type));
+ cell->setPort("\\Y", sig);
+ did_something = true;
+ }
+
+ if (did_something) {
+ cell->fixup_parameters();
+ run_cell(cell);
+ }
+ }
+
+ static int count_nontrivial_wire_attrs(RTLIL::Wire *w)
+ {
+ int count = w->attributes.size();
+ count -= w->attributes.count("\\src");
+ count -= w->attributes.count("\\unused_bits");
+ return count;
+ }
+
+ void run()
+ {
+ for (auto w : module->wires())
+ if (w->get_bool_attribute("\\keep"))
+ for (auto bit : mi.sigmap(w))
+ keep_bits.insert(bit);
+
+ for (auto c : module->selected_cells())
+ work_queue_cells.insert(c);
+
+ while (!work_queue_cells.empty())
+ {
+ work_queue_bits.clear();
+ for (auto c : work_queue_cells)
+ run_cell(c);
+
+ work_queue_cells.clear();
+ for (auto bit : work_queue_bits)
+ for (auto port : mi.query_ports(bit))
+ if (module->selected(port.cell))
+ work_queue_cells.insert(port.cell);
+ }
+
+ pool<SigSpec> complete_wires;
+ for (auto w : module->wires())
+ complete_wires.insert(mi.sigmap(w));
+
+ for (auto w : module->selected_wires())
+ {
+ int unused_top_bits = 0;
+
+ if (w->port_id > 0 || count_nontrivial_wire_attrs(w) > 0)
+ continue;
+
+ for (int i = GetSize(w)-1; i >= 0; i--) {
+ SigBit bit(w, i);
+ auto info = mi.query(bit);
+ if (info && (info->is_input || info->is_output || GetSize(info->ports) > 0))
+ break;
+ unused_top_bits++;
+ }
+
+ if (unused_top_bits == 0 || unused_top_bits == GetSize(w))
+ continue;
+
+ if (complete_wires[mi.sigmap(w).extract(0, GetSize(w) - unused_top_bits)])
+ continue;
+
+ log("Removed top %d bits (of %d) from wire %s.%s.\n", unused_top_bits, GetSize(w), log_id(module), log_id(w));
+ Wire *nw = module->addWire(NEW_ID, GetSize(w) - unused_top_bits);
+ module->connect(nw, SigSpec(w).extract(0, GetSize(nw)));
+ module->swap_names(w, nw);
+ }
+ }
+};
+
+struct WreducePass : public Pass {
+ WreducePass() : Pass("wreduce", "reduce the word size of operations if possible") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" wreduce [options] [selection]\n");
+ log("\n");
+ log("This command reduces the word size of operations. For example it will replace\n");
+ log("the 32 bit adders in the following code with adders of more appropriate widths:\n");
+ log("\n");
+ log(" module test(input [3:0] a, b, c, output [7:0] y);\n");
+ log(" assign y = a + b + c + 1;\n");
+ log(" endmodule\n");
+ log("\n");
+ log("Options:\n");
+ log("\n");
+ log(" -memx\n");
+ log(" Do not change the width of memory address ports. Use this options in\n");
+ log(" flows that use the 'memory_memx' pass.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, Design *design)
+ {
+ WreduceConfig config;
+ bool opt_memx = false;
+
+ log_header(design, "Executing WREDUCE pass (reducing word size of cells).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-memx") {
+ opt_memx = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ if (module->has_processes_warn())
+ continue;
+
+ for (auto c : module->selected_cells())
+ {
+ if (c->type.in("$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool",
+ "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
+ "$logic_not", "$logic_and", "$logic_or") && GetSize(c->getPort("\\Y")) > 1) {
+ SigSpec sig = c->getPort("\\Y");
+ if (!sig.has_const()) {
+ c->setPort("\\Y", sig[0]);
+ c->setParam("\\Y_WIDTH", 1);
+ sig.remove(0);
+ module->connect(sig, Const(0, GetSize(sig)));
+ }
+ }
+ if (!opt_memx && c->type.in("$memrd", "$memwr", "$meminit")) {
+ IdString memid = c->getParam("\\MEMID").decode_string();
+ RTLIL::Memory *mem = module->memories.at(memid);
+ if (mem->start_offset >= 0) {
+ int cur_addrbits = c->getParam("\\ABITS").as_int();
+ int max_addrbits = ceil_log2(mem->start_offset + mem->size);
+ if (cur_addrbits > max_addrbits) {
+ log("Removed top %d address bits (of %d) from memory %s port %s.%s (%s).\n",
+ cur_addrbits-max_addrbits, cur_addrbits,
+ c->type == "$memrd" ? "read" : c->type == "$memwr" ? "write" : "init",
+ log_id(module), log_id(c), log_id(memid));
+ c->setParam("\\ABITS", max_addrbits);
+ c->setPort("\\ADDR", c->getPort("\\ADDR").extract(0, max_addrbits));
+ }
+ }
+ }
+ }
+
+ WreduceWorker worker(&config, module);
+ worker.run();
+ }
+ }
+} WreducePass;
+
+PRIVATE_NAMESPACE_END
+
diff --git a/passes/proc/Makefile.inc b/passes/proc/Makefile.inc
new file mode 100644
index 00000000..397fe46a
--- /dev/null
+++ b/passes/proc/Makefile.inc
@@ -0,0 +1,10 @@
+
+OBJS += passes/proc/proc.o
+OBJS += passes/proc/proc_clean.o
+OBJS += passes/proc/proc_rmdead.o
+OBJS += passes/proc/proc_init.o
+OBJS += passes/proc/proc_arst.o
+OBJS += passes/proc/proc_mux.o
+OBJS += passes/proc/proc_dlatch.o
+OBJS += passes/proc/proc_dff.o
+
diff --git a/passes/proc/proc.cc b/passes/proc/proc.cc
new file mode 100644
index 00000000..d5366f26
--- /dev/null
+++ b/passes/proc/proc.cc
@@ -0,0 +1,100 @@
+/*
+ * 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/register.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct ProcPass : public Pass {
+ ProcPass() : Pass("proc", "translate processes to netlists") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" proc [options] [selection]\n");
+ log("\n");
+ log("This pass calls all the other proc_* passes in the most common order.\n");
+ log("\n");
+ log(" proc_clean\n");
+ log(" proc_rmdead\n");
+ log(" proc_init\n");
+ log(" proc_arst\n");
+ log(" proc_mux\n");
+ log(" proc_dlatch\n");
+ log(" proc_dff\n");
+ log(" proc_clean\n");
+ log("\n");
+ log("This replaces the processes in the design with multiplexers,\n");
+ log("flip-flops and latches.\n");
+ log("\n");
+ log("The following options are supported:\n");
+ log("\n");
+ log(" -global_arst [!]<netname>\n");
+ log(" This option is passed through to proc_arst.\n");
+ log("\n");
+ log(" -ifx\n");
+ log(" This option is passed through to proc_mux. proc_rmdead is not\n");
+ log(" executed in -ifx mode.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::string global_arst;
+ bool ifxmode = false;
+
+ log_header(design, "Executing PROC pass (convert processes to netlists).\n");
+ log_push();
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-global_arst" && argidx+1 < args.size()) {
+ global_arst = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-ifx") {
+ ifxmode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ Pass::call(design, "proc_clean");
+ if (!ifxmode)
+ Pass::call(design, "proc_rmdead");
+ Pass::call(design, "proc_init");
+ if (global_arst.empty())
+ Pass::call(design, "proc_arst");
+ else
+ Pass::call(design, "proc_arst -global_arst " + global_arst);
+ Pass::call(design, ifxmode ? "proc_mux -ifx" : "proc_mux");
+ Pass::call(design, "proc_dlatch");
+ Pass::call(design, "proc_dff");
+ Pass::call(design, "proc_clean");
+
+ log_pop();
+ }
+} ProcPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/proc/proc_arst.cc b/passes/proc/proc_arst.cc
new file mode 100644
index 00000000..216b00dd
--- /dev/null
+++ b/passes/proc/proc_arst.cc
@@ -0,0 +1,292 @@
+/*
+ * 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/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+YOSYS_NAMESPACE_BEGIN
+extern void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int max_depth);
+YOSYS_NAMESPACE_END
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+bool check_signal(RTLIL::Module *mod, RTLIL::SigSpec signal, RTLIL::SigSpec ref, bool &polarity)
+{
+ if (signal.size() != 1)
+ return false;
+ if (signal == ref)
+ return true;
+
+ for (auto cell : mod->cells())
+ {
+ if (cell->type == "$reduce_or" && cell->getPort("\\Y") == signal)
+ return check_signal(mod, cell->getPort("\\A"), ref, polarity);
+
+ if (cell->type == "$reduce_bool" && cell->getPort("\\Y") == signal)
+ return check_signal(mod, cell->getPort("\\A"), ref, polarity);
+
+ if (cell->type == "$logic_not" && cell->getPort("\\Y") == signal) {
+ polarity = !polarity;
+ return check_signal(mod, cell->getPort("\\A"), ref, polarity);
+ }
+
+ if (cell->type == "$not" && cell->getPort("\\Y") == signal) {
+ polarity = !polarity;
+ return check_signal(mod, cell->getPort("\\A"), ref, polarity);
+ }
+
+ if ((cell->type == "$eq" || cell->type == "$eqx") && cell->getPort("\\Y") == signal) {
+ if (cell->getPort("\\A").is_fully_const()) {
+ if (!cell->getPort("\\A").as_bool())
+ polarity = !polarity;
+ return check_signal(mod, cell->getPort("\\B"), ref, polarity);
+ }
+ if (cell->getPort("\\B").is_fully_const()) {
+ if (!cell->getPort("\\B").as_bool())
+ polarity = !polarity;
+ return check_signal(mod, cell->getPort("\\A"), ref, polarity);
+ }
+ }
+
+ if ((cell->type == "$ne" || cell->type == "$nex") && cell->getPort("\\Y") == signal) {
+ if (cell->getPort("\\A").is_fully_const()) {
+ if (cell->getPort("\\A").as_bool())
+ polarity = !polarity;
+ return check_signal(mod, cell->getPort("\\B"), ref, polarity);
+ }
+ if (cell->getPort("\\B").is_fully_const()) {
+ if (cell->getPort("\\B").as_bool())
+ polarity = !polarity;
+ return check_signal(mod, cell->getPort("\\A"), ref, polarity);
+ }
+ }
+ }
+
+ return false;
+}
+
+void apply_const(RTLIL::Module *mod, const RTLIL::SigSpec rspec, RTLIL::SigSpec &rval, RTLIL::CaseRule *cs, RTLIL::SigSpec const_sig, bool polarity, bool unknown)
+{
+ for (auto &action : cs->actions) {
+ if (unknown)
+ rspec.replace(action.first, RTLIL::SigSpec(RTLIL::State::Sm, action.second.size()), &rval);
+ else
+ rspec.replace(action.first, action.second, &rval);
+ }
+
+ for (auto sw : cs->switches) {
+ if (sw->signal.size() == 0) {
+ for (auto cs2 : sw->cases)
+ apply_const(mod, rspec, rval, cs2, const_sig, polarity, unknown);
+ }
+ bool this_polarity = polarity;
+ if (check_signal(mod, sw->signal, const_sig, this_polarity)) {
+ for (auto cs2 : sw->cases) {
+ for (auto comp : cs2->compare)
+ if (comp == RTLIL::SigSpec(this_polarity, 1))
+ goto matched_case;
+ if (cs2->compare.size() == 0) {
+ matched_case:
+ apply_const(mod, rspec, rval, cs2, const_sig, polarity, false);
+ break;
+ }
+ }
+ } else {
+ for (auto cs2 : sw->cases)
+ apply_const(mod, rspec, rval, cs2, const_sig, polarity, true);
+ }
+ }
+}
+
+void eliminate_const(RTLIL::Module *mod, RTLIL::CaseRule *cs, RTLIL::SigSpec const_sig, bool polarity)
+{
+ for (auto sw : cs->switches) {
+ bool this_polarity = polarity;
+ if (check_signal(mod, sw->signal, const_sig, this_polarity)) {
+ bool found_rem_path = false;
+ for (size_t i = 0; i < sw->cases.size(); i++) {
+ RTLIL::CaseRule *cs2 = sw->cases[i];
+ for (auto comp : cs2->compare)
+ if (comp == RTLIL::SigSpec(this_polarity, 1))
+ goto matched_case;
+ if (found_rem_path) {
+ matched_case:
+ sw->cases.erase(sw->cases.begin() + (i--));
+ delete cs2;
+ continue;
+ }
+ found_rem_path = true;
+ cs2->compare.clear();
+ }
+ sw->signal = RTLIL::SigSpec();
+ } else {
+ for (auto cs2 : sw->cases)
+ eliminate_const(mod, cs2, const_sig, polarity);
+ }
+ }
+
+ int dummy_count = 0;
+ bool did_something = true;
+ while (did_something) {
+ did_something = false;
+ proc_clean_case(cs, did_something, dummy_count, 1);
+ }
+}
+
+void proc_arst(RTLIL::Module *mod, RTLIL::Process *proc, SigMap &assign_map)
+{
+restart_proc_arst:
+ if (proc->root_case.switches.size() != 1)
+ return;
+
+ RTLIL::SigSpec root_sig = proc->root_case.switches[0]->signal;
+
+ for (auto &sync : proc->syncs) {
+ if (sync->type == RTLIL::SyncType::STp || sync->type == RTLIL::SyncType::STn) {
+ bool polarity = sync->type == RTLIL::SyncType::STp;
+ if (check_signal(mod, root_sig, sync->signal, polarity)) {
+ if (proc->syncs.size() == 1) {
+ log("Found VHDL-style edge-trigger %s in `%s.%s'.\n", log_signal(sync->signal), mod->name.c_str(), proc->name.c_str());
+ } else {
+ log("Found async reset %s in `%s.%s'.\n", log_signal(sync->signal), mod->name.c_str(), proc->name.c_str());
+ sync->type = sync->type == RTLIL::SyncType::STp ? RTLIL::SyncType::ST1 : RTLIL::SyncType::ST0;
+ }
+ for (auto &action : sync->actions) {
+ RTLIL::SigSpec rspec = action.second;
+ RTLIL::SigSpec rval = RTLIL::SigSpec(RTLIL::State::Sm, rspec.size());
+ for (int i = 0; i < GetSize(rspec); i++)
+ if (rspec[i].wire == NULL)
+ rval[i] = rspec[i];
+ RTLIL::SigSpec last_rval;
+ for (int count = 0; rval != last_rval; count++) {
+ last_rval = rval;
+ apply_const(mod, rspec, rval, &proc->root_case, root_sig, polarity, false);
+ assign_map.apply(rval);
+ if (rval.is_fully_const())
+ break;
+ if (count > 100)
+ log_error("Async reset %s yields endless loop at value %s for signal %s.\n",
+ log_signal(sync->signal), log_signal(rval), log_signal(action.first));
+ rspec = rval;
+ }
+ if (rval.has_marked_bits())
+ log_error("Async reset %s yields non-constant value %s for signal %s.\n",
+ log_signal(sync->signal), log_signal(rval), log_signal(action.first));
+ action.second = rval;
+ }
+ eliminate_const(mod, &proc->root_case, root_sig, polarity);
+ goto restart_proc_arst;
+ }
+ }
+ }
+}
+
+struct ProcArstPass : public Pass {
+ ProcArstPass() : Pass("proc_arst", "detect asynchronous resets") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" proc_arst [-global_arst [!]<netname>] [selection]\n");
+ log("\n");
+ log("This pass identifies asynchronous resets in the processes and converts them\n");
+ log("to a different internal representation that is suitable for generating\n");
+ log("flip-flop cells with asynchronous resets.\n");
+ log("\n");
+ log(" -global_arst [!]<netname>\n");
+ log(" In modules that have a net with the given name, use this net as async\n");
+ log(" reset for registers that have been assign initial values in their\n");
+ log(" declaration ('reg foobar = constant_value;'). Use the '!' modifier for\n");
+ log(" active low reset signals. Note: the frontend stores the default value\n");
+ log(" in the 'init' attribute on the net.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::string global_arst;
+ bool global_arst_neg = false;
+
+ log_header(design, "Executing PROC_ARST pass (detect async resets in processes).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-global_arst" && argidx+1 < args.size()) {
+ global_arst = args[++argidx];
+ if (!global_arst.empty() && global_arst[0] == '!') {
+ global_arst_neg = true;
+ global_arst = global_arst.substr(1);
+ }
+ global_arst = RTLIL::escape_id(global_arst);
+ continue;
+ }
+ break;
+ }
+
+ extra_args(args, argidx, design);
+ pool<Wire*> delete_initattr_wires;
+
+ for (auto mod : design->modules())
+ if (design->selected(mod)) {
+ SigMap assign_map(mod);
+ for (auto &proc_it : mod->processes) {
+ if (!design->selected(mod, proc_it.second))
+ continue;
+ proc_arst(mod, proc_it.second, assign_map);
+ if (global_arst.empty() || mod->wire(global_arst) == nullptr)
+ continue;
+ std::vector<RTLIL::SigSig> arst_actions;
+ for (auto sync : proc_it.second->syncs)
+ if (sync->type == RTLIL::SyncType::STp || sync->type == RTLIL::SyncType::STn)
+ for (auto &act : sync->actions) {
+ RTLIL::SigSpec arst_sig, arst_val;
+ for (auto &chunk : act.first.chunks())
+ if (chunk.wire && chunk.wire->attributes.count("\\init")) {
+ RTLIL::SigSpec value = chunk.wire->attributes.at("\\init");
+ value.extend_u0(chunk.wire->width, false);
+ arst_sig.append(chunk);
+ arst_val.append(value.extract(chunk.offset, chunk.width));
+ delete_initattr_wires.insert(chunk.wire);
+ }
+ if (arst_sig.size()) {
+ log("Added global reset to process %s: %s <- %s\n",
+ proc_it.first.c_str(), log_signal(arst_sig), log_signal(arst_val));
+ arst_actions.push_back(RTLIL::SigSig(arst_sig, arst_val));
+ }
+ }
+ if (!arst_actions.empty()) {
+ RTLIL::SyncRule *sync = new RTLIL::SyncRule;
+ sync->type = global_arst_neg ? RTLIL::SyncType::ST0 : RTLIL::SyncType::ST1;
+ sync->signal = mod->wire(global_arst);
+ sync->actions = arst_actions;
+ proc_it.second->syncs.push_back(sync);
+ }
+ }
+ }
+
+ for (auto wire : delete_initattr_wires)
+ wire->attributes.erase("\\init");
+ }
+} ProcArstPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc
new file mode 100644
index 00000000..7dbabc21
--- /dev/null
+++ b/passes/proc/proc_clean.cc
@@ -0,0 +1,187 @@
+/*
+ * 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/register.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+YOSYS_NAMESPACE_BEGIN
+extern void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int max_depth);
+YOSYS_NAMESPACE_END
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did_something, int &count, int max_depth)
+{
+ if (sw->signal.size() > 0 && sw->signal.is_fully_const())
+ {
+ int found_matching_case_idx = -1;
+ for (int i = 0; i < int(sw->cases.size()) && found_matching_case_idx < 0; i++)
+ {
+ RTLIL::CaseRule *cs = sw->cases[i];
+ if (cs->compare.size() == 0)
+ break;
+ for (int j = 0; j < int(cs->compare.size()); j++) {
+ RTLIL::SigSpec &val = cs->compare[j];
+ if (!val.is_fully_const())
+ continue;
+ if (val == sw->signal) {
+ cs->compare.clear();
+ found_matching_case_idx = i;
+ break;
+ } else
+ cs->compare.erase(cs->compare.begin()+(j--));
+ }
+ if (cs->compare.size() == 0 && found_matching_case_idx < 0) {
+ sw->cases.erase(sw->cases.begin()+(i--));
+ delete cs;
+ }
+ }
+ while (found_matching_case_idx >= 0 && int(sw->cases.size()) > found_matching_case_idx+1) {
+ delete sw->cases.back();
+ sw->cases.pop_back();
+ }
+ if (found_matching_case_idx == 0)
+ sw->signal = RTLIL::SigSpec();
+ }
+
+ if (parent->switches.front() == sw && sw->cases.size() == 1 &&
+ (sw->signal.size() == 0 || sw->cases[0]->compare.empty()))
+ {
+ did_something = true;
+ for (auto &action : sw->cases[0]->actions)
+ parent->actions.push_back(action);
+ for (auto sw2 : sw->cases[0]->switches)
+ parent->switches.push_back(sw2);
+ sw->cases[0]->switches.clear();
+ delete sw->cases[0];
+ sw->cases.clear();
+ }
+ 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;
+ if (max_depth != 0)
+ proc_clean_case(cs, did_something, count, max_depth-1);
+ }
+ if (all_cases_are_empty) {
+ did_something = true;
+ for (auto cs : sw->cases)
+ delete cs;
+ sw->cases.clear();
+ }
+ }
+}
+
+PRIVATE_NAMESPACE_END
+YOSYS_NAMESPACE_BEGIN
+
+void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int max_depth)
+{
+ for (size_t i = 0; i < cs->actions.size(); i++) {
+ if (cs->actions[i].first.size() == 0) {
+ did_something = true;
+ cs->actions.erase(cs->actions.begin() + (i--));
+ }
+ }
+ for (size_t i = 0; i < cs->switches.size(); i++) {
+ RTLIL::SwitchRule *sw = cs->switches[i];
+ if (sw->cases.size() == 0) {
+ cs->switches.erase(cs->switches.begin() + (i--));
+ did_something = true;
+ delete sw;
+ count++;
+ } else if (max_depth != 0)
+ proc_clean_switch(sw, cs, did_something, count, max_depth-1);
+ }
+}
+
+YOSYS_NAMESPACE_END
+PRIVATE_NAMESPACE_BEGIN
+
+void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count)
+{
+ int count = 0;
+ bool did_something = true;
+ for (size_t i = 0; i < proc->syncs.size(); i++) {
+ for (size_t j = 0; j < proc->syncs[i]->actions.size(); j++)
+ if (proc->syncs[i]->actions[j].first.size() == 0)
+ proc->syncs[i]->actions.erase(proc->syncs[i]->actions.begin() + (j--));
+ if (proc->syncs[i]->actions.size() == 0) {
+ delete proc->syncs[i];
+ proc->syncs.erase(proc->syncs.begin() + (i--));
+ }
+ }
+ while (did_something) {
+ did_something = false;
+ proc_clean_case(&proc->root_case, did_something, count, -1);
+ }
+ if (count > 0)
+ log("Found and cleaned up %d empty switch%s in `%s.%s'.\n", count, count == 1 ? "" : "es", mod->name.c_str(), proc->name.c_str());
+ total_count += count;
+}
+
+struct ProcCleanPass : public Pass {
+ ProcCleanPass() : Pass("proc_clean", "remove empty parts of processes") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" proc_clean [selection]\n");
+ log("\n");
+ log("This pass removes empty parts of processes and ultimately removes a process\n");
+ log("if it contains only empty structures.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ int total_count = 0;
+ log_header(design, "Executing PROC_CLEAN pass (remove empty switches from decision trees).\n");
+
+ extra_args(args, 1, design);
+
+ for (auto mod : design->modules()) {
+ std::vector<RTLIL::IdString> delme;
+ if (!design->selected(mod))
+ continue;
+ for (auto &proc_it : mod->processes) {
+ if (!design->selected(mod, proc_it.second))
+ continue;
+ proc_clean(mod, proc_it.second, total_count);
+ if (proc_it.second->syncs.size() == 0 && proc_it.second->root_case.switches.size() == 0 &&
+ proc_it.second->root_case.actions.size() == 0) {
+ log("Removing empty process `%s.%s'.\n", log_id(mod), proc_it.second->name.c_str());
+ delme.push_back(proc_it.first);
+ }
+ }
+ for (auto &id : delme) {
+ delete mod->processes[id];
+ mod->processes.erase(id);
+ }
+ }
+
+ log("Cleaned up %d empty switch%s.\n", total_count, total_count == 1 ? "" : "es");
+ }
+} ProcCleanPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc
new file mode 100644
index 00000000..98653dc6
--- /dev/null
+++ b/passes/proc/proc_dff.cc
@@ -0,0 +1,398 @@
+/*
+ * 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/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/consteval.h"
+#include "kernel/log.h"
+#include <sstream>
+#include <stdlib.h>
+#include <stdio.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+RTLIL::SigSpec find_any_lvalue(const RTLIL::Process *proc)
+{
+ RTLIL::SigSpec lvalue;
+
+ for (auto sync : proc->syncs)
+ for (auto &action : sync->actions)
+ if (action.first.size() > 0) {
+ lvalue = action.first;
+ lvalue.sort_and_unify();
+ break;
+ }
+
+ for (auto sync : proc->syncs) {
+ RTLIL::SigSpec this_lvalue;
+ for (auto &action : sync->actions)
+ this_lvalue.append(action.first);
+ this_lvalue.sort_and_unify();
+ RTLIL::SigSpec common_sig = this_lvalue.extract(lvalue);
+ if (common_sig.size() > 0)
+ lvalue = common_sig;
+ }
+
+ return lvalue;
+}
+
+void gen_dffsr_complex(RTLIL::Module *mod, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, RTLIL::SigSpec clk, bool clk_polarity,
+ std::map<RTLIL::SigSpec, std::set<RTLIL::SyncRule*>> &async_rules, RTLIL::Process *proc)
+{
+ RTLIL::SigSpec sig_sr_set = RTLIL::SigSpec(0, sig_d.size());
+ RTLIL::SigSpec sig_sr_clr = RTLIL::SigSpec(0, sig_d.size());
+
+ for (auto &it : async_rules)
+ {
+ RTLIL::SigSpec sync_value = it.first;
+ RTLIL::SigSpec sync_value_inv;
+ RTLIL::SigSpec sync_high_signals;
+ RTLIL::SigSpec sync_low_signals;
+
+ for (auto &it2 : it.second)
+ if (it2->type == RTLIL::SyncType::ST0)
+ sync_low_signals.append(it2->signal);
+ else if (it2->type == RTLIL::SyncType::ST1)
+ sync_high_signals.append(it2->signal);
+ else
+ log_abort();
+
+ if (sync_low_signals.size() > 1) {
+ RTLIL::Cell *cell = mod->addCell(NEW_ID, "$reduce_or");
+ cell->parameters["\\A_SIGNED"] = RTLIL::Const(0);
+ cell->parameters["\\A_WIDTH"] = RTLIL::Const(sync_low_signals.size());
+ cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
+ cell->setPort("\\A", sync_low_signals);
+ cell->setPort("\\Y", sync_low_signals = mod->addWire(NEW_ID));
+ }
+
+ if (sync_low_signals.size() > 0) {
+ RTLIL::Cell *cell = mod->addCell(NEW_ID, "$not");
+ cell->parameters["\\A_SIGNED"] = RTLIL::Const(0);
+ cell->parameters["\\A_WIDTH"] = RTLIL::Const(sync_low_signals.size());
+ cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
+ cell->setPort("\\A", sync_low_signals);
+ cell->setPort("\\Y", mod->addWire(NEW_ID));
+ sync_high_signals.append(cell->getPort("\\Y"));
+ }
+
+ if (sync_high_signals.size() > 1) {
+ RTLIL::Cell *cell = mod->addCell(NEW_ID, "$reduce_or");
+ cell->parameters["\\A_SIGNED"] = RTLIL::Const(0);
+ cell->parameters["\\A_WIDTH"] = RTLIL::Const(sync_high_signals.size());
+ cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
+ cell->setPort("\\A", sync_high_signals);
+ cell->setPort("\\Y", sync_high_signals = mod->addWire(NEW_ID));
+ }
+
+ RTLIL::Cell *inv_cell = mod->addCell(NEW_ID, "$not");
+ inv_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0);
+ inv_cell->parameters["\\A_WIDTH"] = RTLIL::Const(sig_d.size());
+ inv_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(sig_d.size());
+ inv_cell->setPort("\\A", sync_value);
+ inv_cell->setPort("\\Y", sync_value_inv = mod->addWire(NEW_ID, sig_d.size()));
+
+ RTLIL::Cell *mux_set_cell = mod->addCell(NEW_ID, "$mux");
+ mux_set_cell->parameters["\\WIDTH"] = RTLIL::Const(sig_d.size());
+ mux_set_cell->setPort("\\A", sig_sr_set);
+ mux_set_cell->setPort("\\B", sync_value);
+ mux_set_cell->setPort("\\S", sync_high_signals);
+ mux_set_cell->setPort("\\Y", sig_sr_set = mod->addWire(NEW_ID, sig_d.size()));
+
+ RTLIL::Cell *mux_clr_cell = mod->addCell(NEW_ID, "$mux");
+ mux_clr_cell->parameters["\\WIDTH"] = RTLIL::Const(sig_d.size());
+ mux_clr_cell->setPort("\\A", sig_sr_clr);
+ mux_clr_cell->setPort("\\B", sync_value_inv);
+ mux_clr_cell->setPort("\\S", sync_high_signals);
+ mux_clr_cell->setPort("\\Y", sig_sr_clr = mod->addWire(NEW_ID, sig_d.size()));
+ }
+
+ std::stringstream sstr;
+ sstr << "$procdff$" << (autoidx++);
+
+ RTLIL::Cell *cell = mod->addCell(sstr.str(), "$dffsr");
+ cell->attributes = proc->attributes;
+ cell->parameters["\\WIDTH"] = RTLIL::Const(sig_d.size());
+ cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity, 1);
+ cell->parameters["\\SET_POLARITY"] = RTLIL::Const(true, 1);
+ cell->parameters["\\CLR_POLARITY"] = RTLIL::Const(true, 1);
+ cell->setPort("\\D", sig_d);
+ cell->setPort("\\Q", sig_q);
+ cell->setPort("\\CLK", clk);
+ cell->setPort("\\SET", sig_sr_set);
+ cell->setPort("\\CLR", sig_sr_clr);
+
+ log(" created %s cell `%s' with %s edge clock and multiple level-sensitive resets.\n",
+ cell->type.c_str(), cell->name.c_str(), clk_polarity ? "positive" : "negative");
+}
+
+void gen_dffsr(RTLIL::Module *mod, RTLIL::SigSpec sig_in, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_out,
+ bool clk_polarity, bool set_polarity, RTLIL::SigSpec clk, RTLIL::SigSpec set, RTLIL::Process *proc)
+{
+ std::stringstream sstr;
+ sstr << "$procdff$" << (autoidx++);
+
+ RTLIL::SigSpec sig_set_inv = mod->addWire(NEW_ID, sig_in.size());
+ RTLIL::SigSpec sig_sr_set = mod->addWire(NEW_ID, sig_in.size());
+ RTLIL::SigSpec sig_sr_clr = mod->addWire(NEW_ID, sig_in.size());
+
+ RTLIL::Cell *inv_set = mod->addCell(NEW_ID, "$not");
+ inv_set->parameters["\\A_SIGNED"] = RTLIL::Const(0);
+ inv_set->parameters["\\A_WIDTH"] = RTLIL::Const(sig_in.size());
+ inv_set->parameters["\\Y_WIDTH"] = RTLIL::Const(sig_in.size());
+ inv_set->setPort("\\A", sig_set);
+ inv_set->setPort("\\Y", sig_set_inv);
+
+ RTLIL::Cell *mux_sr_set = mod->addCell(NEW_ID, "$mux");
+ mux_sr_set->parameters["\\WIDTH"] = RTLIL::Const(sig_in.size());
+ mux_sr_set->setPort(set_polarity ? "\\A" : "\\B", RTLIL::Const(0, sig_in.size()));
+ mux_sr_set->setPort(set_polarity ? "\\B" : "\\A", sig_set);
+ mux_sr_set->setPort("\\Y", sig_sr_set);
+ mux_sr_set->setPort("\\S", set);
+
+ RTLIL::Cell *mux_sr_clr = mod->addCell(NEW_ID, "$mux");
+ mux_sr_clr->parameters["\\WIDTH"] = RTLIL::Const(sig_in.size());
+ mux_sr_clr->setPort(set_polarity ? "\\A" : "\\B", RTLIL::Const(0, sig_in.size()));
+ mux_sr_clr->setPort(set_polarity ? "\\B" : "\\A", sig_set_inv);
+ mux_sr_clr->setPort("\\Y", sig_sr_clr);
+ mux_sr_clr->setPort("\\S", set);
+
+ RTLIL::Cell *cell = mod->addCell(sstr.str(), "$dffsr");
+ cell->attributes = proc->attributes;
+ cell->parameters["\\WIDTH"] = RTLIL::Const(sig_in.size());
+ cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity, 1);
+ cell->parameters["\\SET_POLARITY"] = RTLIL::Const(true, 1);
+ cell->parameters["\\CLR_POLARITY"] = RTLIL::Const(true, 1);
+ cell->setPort("\\D", sig_in);
+ cell->setPort("\\Q", sig_out);
+ cell->setPort("\\CLK", clk);
+ cell->setPort("\\SET", sig_sr_set);
+ cell->setPort("\\CLR", sig_sr_clr);
+
+ log(" created %s cell `%s' with %s edge clock and %s level non-const reset.\n", cell->type.c_str(), cell->name.c_str(),
+ clk_polarity ? "positive" : "negative", set_polarity ? "positive" : "negative");
+}
+
+void gen_dff(RTLIL::Module *mod, RTLIL::SigSpec sig_in, RTLIL::Const val_rst, RTLIL::SigSpec sig_out,
+ bool clk_polarity, bool arst_polarity, RTLIL::SigSpec clk, RTLIL::SigSpec *arst, RTLIL::Process *proc)
+{
+ std::stringstream sstr;
+ sstr << "$procdff$" << (autoidx++);
+
+ RTLIL::Cell *cell = mod->addCell(sstr.str(), clk.empty() ? "$ff" : arst ? "$adff" : "$dff");
+ cell->attributes = proc->attributes;
+
+ cell->parameters["\\WIDTH"] = RTLIL::Const(sig_in.size());
+ if (arst) {
+ cell->parameters["\\ARST_POLARITY"] = RTLIL::Const(arst_polarity, 1);
+ cell->parameters["\\ARST_VALUE"] = val_rst;
+ }
+ if (!clk.empty()) {
+ cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity, 1);
+ }
+
+ cell->setPort("\\D", sig_in);
+ cell->setPort("\\Q", sig_out);
+ if (arst)
+ cell->setPort("\\ARST", *arst);
+ if (!clk.empty())
+ cell->setPort("\\CLK", clk);
+
+ if (!clk.empty())
+ log(" created %s cell `%s' with %s edge clock", cell->type.c_str(), cell->name.c_str(), clk_polarity ? "positive" : "negative");
+ else
+ log(" created %s cell `%s' with global clock", cell->type.c_str(), cell->name.c_str());
+ if (arst)
+ log(" and %s level reset", arst_polarity ? "positive" : "negative");
+ log(".\n");
+}
+
+void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce)
+{
+ while (1)
+ {
+ RTLIL::SigSpec sig = find_any_lvalue(proc);
+ bool free_sync_level = false;
+
+ if (sig.size() == 0)
+ break;
+
+ log("Creating register for signal `%s.%s' using process `%s.%s'.\n",
+ mod->name.c_str(), log_signal(sig), mod->name.c_str(), proc->name.c_str());
+
+ RTLIL::SigSpec insig = RTLIL::SigSpec(RTLIL::State::Sz, sig.size());
+ RTLIL::SigSpec rstval = RTLIL::SigSpec(RTLIL::State::Sz, sig.size());
+ RTLIL::SyncRule *sync_level = NULL;
+ RTLIL::SyncRule *sync_edge = NULL;
+ RTLIL::SyncRule *sync_always = NULL;
+ bool global_clock = false;
+
+ std::map<RTLIL::SigSpec, std::set<RTLIL::SyncRule*>> many_async_rules;
+
+ for (auto sync : proc->syncs)
+ for (auto &action : sync->actions)
+ {
+ if (action.first.extract(sig).size() == 0)
+ continue;
+
+ if (sync->type == RTLIL::SyncType::ST0 || sync->type == RTLIL::SyncType::ST1) {
+ if (sync_level != NULL && sync_level != sync) {
+ // log_error("Multiple level sensitive events found for this signal!\n");
+ many_async_rules[rstval].insert(sync_level);
+ rstval = RTLIL::SigSpec(RTLIL::State::Sz, sig.size());
+ }
+ rstval = RTLIL::SigSpec(RTLIL::State::Sz, sig.size());
+ sig.replace(action.first, action.second, &rstval);
+ sync_level = sync;
+ }
+ else if (sync->type == RTLIL::SyncType::STp || sync->type == RTLIL::SyncType::STn) {
+ if (sync_edge != NULL && sync_edge != sync)
+ log_error("Multiple edge sensitive events found for this signal!\n");
+ sig.replace(action.first, action.second, &insig);
+ sync_edge = sync;
+ }
+ else if (sync->type == RTLIL::SyncType::STa) {
+ if (sync_always != NULL && sync_always != sync)
+ log_error("Multiple always events found for this signal!\n");
+ sig.replace(action.first, action.second, &insig);
+ sync_always = sync;
+ }
+ else if (sync->type == RTLIL::SyncType::STg) {
+ sig.replace(action.first, action.second, &insig);
+ global_clock = true;
+ }
+ else {
+ log_error("Event with any-edge sensitivity found for this signal!\n");
+ }
+
+ action.first.remove2(sig, &action.second);
+ }
+
+ if (many_async_rules.size() > 0)
+ {
+ many_async_rules[rstval].insert(sync_level);
+ if (many_async_rules.size() == 1)
+ {
+ sync_level = new RTLIL::SyncRule;
+ sync_level->type = RTLIL::SyncType::ST1;
+ sync_level->signal = mod->addWire(NEW_ID);
+ sync_level->actions.push_back(RTLIL::SigSig(sig, rstval));
+ free_sync_level = true;
+
+ RTLIL::SigSpec inputs, compare;
+ for (auto &it : many_async_rules[rstval]) {
+ inputs.append(it->signal);
+ compare.append(it->type == RTLIL::SyncType::ST0 ? RTLIL::State::S1 : RTLIL::State::S0);
+ }
+ log_assert(inputs.size() == compare.size());
+
+ RTLIL::Cell *cell = mod->addCell(NEW_ID, "$ne");
+ cell->parameters["\\A_SIGNED"] = RTLIL::Const(false, 1);
+ cell->parameters["\\B_SIGNED"] = RTLIL::Const(false, 1);
+ cell->parameters["\\A_WIDTH"] = RTLIL::Const(inputs.size());
+ cell->parameters["\\B_WIDTH"] = RTLIL::Const(inputs.size());
+ cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
+ cell->setPort("\\A", inputs);
+ cell->setPort("\\B", compare);
+ cell->setPort("\\Y", sync_level->signal);
+
+ many_async_rules.clear();
+ }
+ else
+ {
+ rstval = RTLIL::SigSpec(RTLIL::State::Sz, sig.size());
+ sync_level = NULL;
+ }
+ }
+
+ ce.assign_map.apply(insig);
+ ce.assign_map.apply(rstval);
+ ce.assign_map.apply(sig);
+
+ if (rstval == sig) {
+ rstval = RTLIL::SigSpec(RTLIL::State::Sz, sig.size());
+ sync_level = NULL;
+ }
+
+ if (sync_always) {
+ if (sync_edge || sync_level || many_async_rules.size() > 0)
+ log_error("Mixed always event with edge and/or level sensitive events!\n");
+ log(" created direct connection (no actual register cell created).\n");
+ mod->connect(RTLIL::SigSig(sig, insig));
+ continue;
+ }
+
+ if (!sync_edge && !global_clock)
+ log_error("Missing edge-sensitive event for this signal!\n");
+
+ if (many_async_rules.size() > 0)
+ {
+ log_warning("Complex async reset for dff `%s'.\n", log_signal(sig));
+ gen_dffsr_complex(mod, insig, sig, sync_edge->signal, sync_edge->type == RTLIL::SyncType::STp, many_async_rules, proc);
+ }
+ else if (!rstval.is_fully_const() && !ce.eval(rstval))
+ {
+ log_warning("Async reset value `%s' is not constant!\n", log_signal(rstval));
+ gen_dffsr(mod, insig, rstval, sig,
+ sync_edge->type == RTLIL::SyncType::STp,
+ sync_level && sync_level->type == RTLIL::SyncType::ST1,
+ sync_edge->signal, sync_level->signal, proc);
+ }
+ else
+ gen_dff(mod, insig, rstval.as_const(), sig,
+ sync_edge && sync_edge->type == RTLIL::SyncType::STp,
+ sync_level && sync_level->type == RTLIL::SyncType::ST1,
+ sync_edge ? sync_edge->signal : SigSpec(),
+ sync_level ? &sync_level->signal : NULL, proc);
+
+ if (free_sync_level)
+ delete sync_level;
+ }
+}
+
+struct ProcDffPass : public Pass {
+ ProcDffPass() : Pass("proc_dff", "extract flip-flops from processes") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" proc_dff [selection]\n");
+ log("\n");
+ log("This pass identifies flip-flops in the processes and converts them to\n");
+ log("d-type flip-flop cells.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing PROC_DFF pass (convert process syncs to FFs).\n");
+
+ extra_args(args, 1, design);
+
+ for (auto mod : design->modules())
+ if (design->selected(mod)) {
+ ConstEval ce(mod);
+ for (auto &proc_it : mod->processes)
+ if (design->selected(mod, proc_it.second))
+ proc_dff(mod, proc_it.second, ce);
+ }
+ }
+} ProcDffPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc
new file mode 100644
index 00000000..6621afd3
--- /dev/null
+++ b/passes/proc/proc_dlatch.cc
@@ -0,0 +1,449 @@
+/*
+ * 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/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/consteval.h"
+#include "kernel/log.h"
+#include <sstream>
+#include <stdlib.h>
+#include <stdio.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct proc_dlatch_db_t
+{
+ Module *module;
+ SigMap sigmap;
+
+ pool<Cell*> generated_dlatches;
+ dict<Cell*, vector<SigBit>> mux_srcbits;
+ dict<SigBit, pair<Cell*, int>> mux_drivers;
+ dict<SigBit, int> sigusers;
+
+ proc_dlatch_db_t(Module *module) : module(module), sigmap(module)
+ {
+ for (auto cell : module->cells())
+ {
+ if (cell->type.in("$mux", "$pmux"))
+ {
+ auto sig_y = sigmap(cell->getPort("\\Y"));
+ for (int i = 0; i < GetSize(sig_y); i++)
+ mux_drivers[sig_y[i]] = pair<Cell*, int>(cell, i);
+
+ pool<SigBit> mux_srcbits_pool;
+ for (auto bit : sigmap(cell->getPort("\\A")))
+ mux_srcbits_pool.insert(bit);
+ for (auto bit : sigmap(cell->getPort("\\B")))
+ mux_srcbits_pool.insert(bit);
+
+ vector<SigBit> mux_srcbits_vec;
+ for (auto bit : mux_srcbits_pool)
+ if (bit.wire != nullptr)
+ mux_srcbits_vec.push_back(bit);
+
+ mux_srcbits[cell].swap(mux_srcbits_vec);
+ }
+
+ for (auto &conn : cell->connections())
+ if (!cell->known() || cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ sigusers[bit]++;
+ }
+
+ for (auto wire : module->wires())
+ if (wire->port_input)
+ for (auto bit : sigmap(wire))
+ sigusers[bit]++;
+ }
+
+ bool quickcheck(const SigSpec &haystack, const SigSpec &needle)
+ {
+ pool<SigBit> haystack_bits = sigmap(haystack).to_sigbit_pool();
+ pool<SigBit> needle_bits = sigmap(needle).to_sigbit_pool();
+
+ pool<Cell*> cells_queue, cells_visited;
+ pool<SigBit> bits_queue, bits_visited;
+
+ bits_queue = haystack_bits;
+ while (!bits_queue.empty())
+ {
+ for (auto &bit : bits_queue) {
+ auto it = mux_drivers.find(bit);
+ if (it != mux_drivers.end())
+ if (!cells_visited.count(it->second.first))
+ cells_queue.insert(it->second.first);
+ bits_visited.insert(bit);
+ }
+
+ bits_queue.clear();
+
+ for (auto c : cells_queue) {
+ for (auto bit : mux_srcbits[c]) {
+ if (needle_bits.count(bit))
+ return true;
+ if (!bits_visited.count(bit))
+ bits_queue.insert(bit);
+ }
+ }
+
+ cells_queue.clear();
+ }
+
+ return false;
+ }
+
+ struct rule_node_t
+ {
+ // a node is true if "signal" equals "match" and [any
+ // of the child nodes is true or "children" is empty]
+ SigBit signal, match;
+ vector<int> children;
+
+ bool operator==(const rule_node_t &other) const {
+ return signal == other.signal && match == other.match && children == other.children;
+ }
+
+ unsigned int hash() const {
+ unsigned int h = mkhash_init;
+ mkhash(h, signal.hash());
+ mkhash(h, match.hash());
+ for (auto i : children) mkhash(h, i);
+ return h;
+ }
+ };
+
+ enum tf_node_types_t : int {
+ true_node = 1,
+ false_node = 2
+ };
+
+ idict<rule_node_t, 3> rules_db;
+ dict<int, SigBit> rules_sig;
+
+ int make_leaf(SigBit signal, SigBit match)
+ {
+ rule_node_t node;
+ node.signal = signal;
+ node.match = match;
+ return rules_db(node);
+ }
+
+ int make_inner(SigBit signal, SigBit match, int child)
+ {
+ rule_node_t node;
+ node.signal = signal;
+ node.match = match;
+ node.children.push_back(child);
+ return rules_db(node);
+ }
+
+ int make_inner(const pool<int> &children)
+ {
+ rule_node_t node;
+ node.signal = State::S0;
+ node.match = State::S0;
+ node.children = vector<int>(children.begin(), children.end());
+ std::sort(node.children.begin(), node.children.end());
+ return rules_db(node);
+ }
+
+ int find_mux_feedback(SigBit haystack, SigBit needle, bool set_undef)
+ {
+ if (sigusers[haystack] > 1)
+ set_undef = false;
+
+ if (haystack == needle)
+ return true_node;
+
+ auto it = mux_drivers.find(haystack);
+ if (it == mux_drivers.end())
+ return false_node;
+
+ Cell *cell = it->second.first;
+ int index = it->second.second;
+
+ SigSpec sig_a = sigmap(cell->getPort("\\A"));
+ SigSpec sig_b = sigmap(cell->getPort("\\B"));
+ SigSpec sig_s = sigmap(cell->getPort("\\S"));
+ int width = GetSize(sig_a);
+
+ pool<int> children;
+
+ int n = find_mux_feedback(sig_a[index], needle, set_undef);
+ if (n != false_node) {
+ if (set_undef && sig_a[index] == needle) {
+ SigSpec sig = cell->getPort("\\A");
+ sig[index] = State::Sx;
+ cell->setPort("\\A", sig);
+ }
+ for (int i = 0; i < GetSize(sig_s); i++)
+ n = make_inner(sig_s[i], State::S0, n);
+ children.insert(n);
+ }
+
+ for (int i = 0; i < GetSize(sig_s); i++) {
+ n = find_mux_feedback(sig_b[i*width + index], needle, set_undef);
+ if (n != false_node) {
+ if (set_undef && sig_b[i*width + index] == needle) {
+ SigSpec sig = cell->getPort("\\B");
+ sig[i*width + index] = State::Sx;
+ cell->setPort("\\B", sig);
+ }
+ children.insert(make_inner(sig_s[i], State::S1, n));
+ }
+ }
+
+ if (children.empty())
+ return false_node;
+
+ return make_inner(children);
+ }
+
+ SigBit make_hold(int n)
+ {
+ if (n == true_node)
+ return State::S1;
+
+ if (n == false_node)
+ return State::S0;
+
+ if (rules_sig.count(n))
+ return rules_sig.at(n);
+
+ const rule_node_t &rule = rules_db[n];
+ SigSpec and_bits;
+
+ if (rule.signal != rule.match) {
+ if (rule.match == State::S1)
+ and_bits.append(rule.signal);
+ else if (rule.match == State::S0)
+ and_bits.append(module->Not(NEW_ID, rule.signal));
+ else
+ and_bits.append(module->Eq(NEW_ID, rule.signal, rule.match));
+ }
+
+ if (!rule.children.empty()) {
+ SigSpec or_bits;
+ for (int k : rule.children)
+ or_bits.append(make_hold(k));
+ and_bits.append(module->ReduceOr(NEW_ID, or_bits));
+ }
+
+ if (GetSize(and_bits) == 2)
+ and_bits = module->And(NEW_ID, and_bits[0], and_bits[1]);
+ log_assert(GetSize(and_bits) == 1);
+
+ rules_sig[n] = and_bits[0];
+ return and_bits[0];
+ }
+
+ void fixup_mux(Cell *cell)
+ {
+ SigSpec sig_a = cell->getPort("\\A");
+ SigSpec sig_b = cell->getPort("\\B");
+ SigSpec sig_s = cell->getPort("\\S");
+ SigSpec sig_any_valid_b;
+
+ SigSpec sig_new_b, sig_new_s;
+ for (int i = 0; i < GetSize(sig_s); i++) {
+ SigSpec b = sig_b.extract(i*GetSize(sig_a), GetSize(sig_a));
+ if (!b.is_fully_undef()) {
+ sig_any_valid_b = b;
+ sig_new_b.append(b);
+ sig_new_s.append(sig_s[i]);
+ }
+ }
+
+ if (sig_new_s.empty()) {
+ sig_new_b = sig_a;
+ sig_new_s = State::S0;
+ }
+
+ if (sig_a.is_fully_undef() && !sig_any_valid_b.empty())
+ cell->setPort("\\A", sig_any_valid_b);
+
+ if (GetSize(sig_new_s) == 1) {
+ cell->type = "$mux";
+ cell->unsetParam("\\S_WIDTH");
+ } else {
+ cell->type = "$pmux";
+ cell->setParam("\\S_WIDTH", GetSize(sig_new_s));
+ }
+
+ cell->setPort("\\B", sig_new_b);
+ cell->setPort("\\S", sig_new_s);
+ }
+
+ void fixup_muxes()
+ {
+ pool<Cell*> visited, queue;
+ dict<Cell*, pool<SigBit>> upstream_cell2net;
+ dict<SigBit, pool<Cell*>> upstream_net2cell;
+
+ CellTypes ct;
+ ct.setup_internals();
+
+ for (auto cell : module->cells())
+ for (auto conn : cell->connections()) {
+ if (cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ upstream_cell2net[cell].insert(bit);
+ if (cell->output(conn.first))
+ for (auto bit : sigmap(conn.second))
+ upstream_net2cell[bit].insert(cell);
+ }
+
+ queue = generated_dlatches;
+ while (!queue.empty())
+ {
+ pool<Cell*> next_queue;
+
+ for (auto cell : queue) {
+ if (cell->type.in("$mux", "$pmux"))
+ fixup_mux(cell);
+ for (auto bit : upstream_cell2net[cell])
+ for (auto cell : upstream_net2cell[bit])
+ next_queue.insert(cell);
+ visited.insert(cell);
+ }
+
+ queue.clear();
+ for (auto cell : next_queue) {
+ if (!visited.count(cell) && ct.cell_known(cell->type))
+ queue.insert(cell);
+ }
+ }
+ }
+};
+
+void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc)
+{
+ std::vector<RTLIL::SyncRule*> new_syncs;
+ RTLIL::SigSig latches_bits, nolatches_bits;
+ dict<SigBit, SigBit> latches_out_in;
+ dict<SigBit, int> latches_hold;
+
+ for (auto sr : proc->syncs)
+ {
+ if (sr->type != RTLIL::SyncType::STa) {
+ new_syncs.push_back(sr);
+ continue;
+ }
+
+ for (auto ss : sr->actions)
+ {
+ db.sigmap.apply(ss.first);
+ db.sigmap.apply(ss.second);
+
+ if (!db.quickcheck(ss.second, ss.first)) {
+ nolatches_bits.first.append(ss.first);
+ nolatches_bits.second.append(ss.second);
+ continue;
+ }
+
+ for (int i = 0; i < GetSize(ss.first); i++)
+ latches_out_in[ss.first[i]] = ss.second[i];
+ }
+
+ delete sr;
+ }
+
+ latches_out_in.sort();
+ for (auto &it : latches_out_in) {
+ int n = db.find_mux_feedback(it.second, it.first, true);
+ if (n == db.false_node) {
+ nolatches_bits.first.append(it.first);
+ nolatches_bits.second.append(it.second);
+ } else {
+ latches_bits.first.append(it.first);
+ latches_bits.second.append(it.second);
+ latches_hold[it.first] = n;
+ }
+ }
+
+ int offset = 0;
+ for (auto chunk : nolatches_bits.first.chunks()) {
+ SigSpec lhs = chunk, rhs = nolatches_bits.second.extract(offset, chunk.width);
+ log("No latch inferred for signal `%s.%s' from process `%s.%s'.\n",
+ db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str());
+ db.module->connect(lhs, rhs);
+ offset += chunk.width;
+ }
+
+ offset = 0;
+ while (offset < GetSize(latches_bits.first))
+ {
+ int width = 1;
+ int n = latches_hold[latches_bits.first[offset]];
+ Wire *w = latches_bits.first[offset].wire;
+
+ if (w != nullptr)
+ {
+ while (offset+width < GetSize(latches_bits.first) &&
+ n == latches_hold[latches_bits.first[offset+width]] &&
+ w == latches_bits.first[offset+width].wire)
+ width++;
+
+ SigSpec lhs = latches_bits.first.extract(offset, width);
+ SigSpec rhs = latches_bits.second.extract(offset, width);
+
+ Cell *cell = db.module->addDlatch(NEW_ID, db.module->Not(NEW_ID, db.make_hold(n)), rhs, lhs);
+ db.generated_dlatches.insert(cell);
+
+ log("Latch inferred for signal `%s.%s' from process `%s.%s': %s\n",
+ db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str(), log_id(cell));
+ }
+
+ offset += width;
+ }
+
+ new_syncs.swap(proc->syncs);
+}
+
+struct ProcDlatchPass : public Pass {
+ ProcDlatchPass() : Pass("proc_dlatch", "extract latches from processes") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" proc_dlatch [selection]\n");
+ log("\n");
+ log("This pass identifies latches in the processes and converts them to\n");
+ log("d-type latches.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing PROC_DLATCH pass (convert process syncs to latches).\n");
+
+ extra_args(args, 1, design);
+
+ for (auto module : design->selected_modules()) {
+ proc_dlatch_db_t db(module);
+ for (auto &proc_it : module->processes)
+ if (design->selected(module, proc_it.second))
+ proc_dlatch(db, proc_it.second);
+ db.fixup_muxes();
+ }
+ }
+} ProcDlatchPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/proc/proc_init.cc b/passes/proc/proc_init.cc
new file mode 100644
index 00000000..0c8fb83d
--- /dev/null
+++ b/passes/proc/proc_init.cc
@@ -0,0 +1,130 @@
+/*
+ * 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/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void proc_get_const(RTLIL::SigSpec &sig, RTLIL::CaseRule &rule)
+{
+ log_assert(rule.compare.size() == 0);
+
+ while (1) {
+ RTLIL::SigSpec tmp = sig;
+ for (auto &it : rule.actions)
+ tmp.replace(it.first, it.second);
+ if (tmp == sig)
+ break;
+ sig = tmp;
+ }
+}
+
+void proc_init(RTLIL::Module *mod, RTLIL::Process *proc)
+{
+ bool found_init = false;
+
+ for (auto &sync : proc->syncs)
+ if (sync->type == RTLIL::SyncType::STi)
+ {
+ found_init = true;
+ log("Found init rule in `%s.%s'.\n", mod->name.c_str(), proc->name.c_str());
+
+ for (auto &action : sync->actions)
+ {
+ RTLIL::SigSpec lhs = action.first;
+ RTLIL::SigSpec rhs = action.second;
+
+ proc_get_const(rhs, proc->root_case);
+
+ if (!rhs.is_fully_const())
+ log_cmd_error("Failed to get a constant init value for %s: %s\n", log_signal(lhs), log_signal(rhs));
+
+ int offset = 0;
+ for (auto &lhs_c : lhs.chunks())
+ {
+ if (lhs_c.wire != nullptr)
+ {
+ SigSpec valuesig = rhs.extract(offset, lhs_c.width);
+ if (!valuesig.is_fully_const())
+ log_cmd_error("Non-const initialization value: %s = %s\n", log_signal(lhs_c), log_signal(valuesig));
+
+ Const value = valuesig.as_const();
+ Const &wireinit = lhs_c.wire->attributes["\\init"];
+
+ while (GetSize(wireinit.bits) < lhs_c.wire->width)
+ wireinit.bits.push_back(State::Sx);
+
+ for (int i = 0; i < lhs_c.width; i++) {
+ auto &initbit = wireinit.bits[i + lhs_c.offset];
+ if (initbit != State::Sx && initbit != value[i])
+ log_cmd_error("Conflicting initialization values for %s.\n", log_signal(lhs_c));
+ initbit = value[i];
+ }
+
+ log(" Set init value: %s = %s\n", log_signal(lhs_c.wire), log_signal(wireinit));
+ }
+ offset += lhs_c.width;
+ }
+ }
+ }
+
+ if (found_init) {
+ std::vector<RTLIL::SyncRule*> new_syncs;
+ for (auto &sync : proc->syncs)
+ if (sync->type == RTLIL::SyncType::STi)
+ delete sync;
+ else
+ new_syncs.push_back(sync);
+ proc->syncs.swap(new_syncs);
+ }
+}
+
+struct ProcInitPass : public Pass {
+ ProcInitPass() : Pass("proc_init", "convert initial block to init attributes") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" proc_init [selection]\n");
+ log("\n");
+ log("This pass extracts the 'init' actions from processes (generated from Verilog\n");
+ log("'initial' blocks) and sets the initial value to the 'init' attribute on the\n");
+ log("respective wire.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing PROC_INIT pass (extract init attributes).\n");
+
+ extra_args(args, 1, design);
+
+ for (auto mod : design->modules())
+ if (design->selected(mod))
+ for (auto &proc_it : mod->processes)
+ if (design->selected(mod, proc_it.second))
+ proc_init(mod, proc_it.second);
+ }
+} ProcInitPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc
new file mode 100644
index 00000000..57e131ca
--- /dev/null
+++ b/passes/proc/proc_mux.cc
@@ -0,0 +1,423 @@
+/*
+ * 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/register.h"
+#include "kernel/bitpattern.h"
+#include "kernel/log.h"
+#include <sstream>
+#include <stdlib.h>
+#include <stdio.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct SigSnippets
+{
+ idict<SigSpec> sigidx;
+ dict<SigBit, int> bit2snippet;
+ pool<int> snippets;
+
+ void insert(SigSpec sig)
+ {
+ if (sig.empty())
+ return;
+
+ int key = sigidx(sig);
+ if (snippets.count(key))
+ return;
+
+ SigSpec new_sig;
+
+ for (int i = 0; i < GetSize(sig); i++)
+ {
+ int other_key = bit2snippet.at(sig[i], -1);
+
+ if (other_key < 0) {
+ new_sig.append(sig[i]);
+ continue;
+ }
+
+ if (!new_sig.empty()) {
+ int new_key = sigidx(new_sig);
+ snippets.insert(new_key);
+ for (auto bit : new_sig)
+ bit2snippet[bit] = new_key;
+ new_sig = SigSpec();
+ }
+
+ SigSpec other_sig = sigidx[other_key];
+ int k = 0, n = 1;
+
+ while (other_sig[k] != sig[i]) {
+ k++;
+ log_assert(k < GetSize(other_sig));
+ }
+
+ while (i+n < GetSize(sig) && k+n < GetSize(other_sig) && sig[i+n] == other_sig[k+n])
+ n++;
+
+ SigSpec sig1 = other_sig.extract(0, k);
+ SigSpec sig2 = other_sig.extract(k, n);
+ SigSpec sig3 = other_sig.extract(k+n, GetSize(other_sig)-k-n);
+
+ for (auto bit : other_sig)
+ bit2snippet.erase(bit);
+ snippets.erase(other_key);
+
+ insert(sig1);
+ insert(sig2);
+ insert(sig3);
+
+ i += n-1;
+ }
+
+ if (!new_sig.empty()) {
+ int new_key = sigidx(new_sig);
+ snippets.insert(new_key);
+ for (auto bit : new_sig)
+ bit2snippet[bit] = new_key;
+ }
+ }
+
+ void insert(const RTLIL::CaseRule *cs)
+ {
+ for (auto &action : cs->actions)
+ insert(action.first);
+
+ for (auto sw : cs->switches)
+ for (auto cs2 : sw->cases)
+ insert(cs2);
+ }
+};
+
+struct SnippetSwCache
+{
+ dict<RTLIL::SwitchRule*, pool<int>, hash_ptr_ops> cache;
+ const SigSnippets *snippets;
+ int current_snippet;
+
+ bool check(RTLIL::SwitchRule *sw)
+ {
+ return cache[sw].count(current_snippet) != 0;
+ }
+
+ void insert(const RTLIL::CaseRule *cs, vector<RTLIL::SwitchRule*> &sw_stack)
+ {
+ for (auto &action : cs->actions)
+ for (auto bit : action.first) {
+ int sn = snippets->bit2snippet.at(bit, -1);
+ if (sn < 0)
+ continue;
+ for (auto sw : sw_stack)
+ cache[sw].insert(sn);
+ }
+
+ for (auto sw : cs->switches) {
+ sw_stack.push_back(sw);
+ for (auto cs2 : sw->cases)
+ insert(cs2, sw_stack);
+ sw_stack.pop_back();
+ }
+ }
+
+ void insert(const RTLIL::CaseRule *cs)
+ {
+ vector<RTLIL::SwitchRule*> sw_stack;
+ insert(cs, sw_stack);
+ }
+};
+
+RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw, bool ifxmode)
+{
+ std::stringstream sstr;
+ sstr << "$procmux$" << (autoidx++);
+
+ RTLIL::Wire *cmp_wire = mod->addWire(sstr.str() + "_CMP", 0);
+
+ for (auto comp : compare)
+ {
+ RTLIL::SigSpec sig = signal;
+
+ // get rid of don't-care bits
+ log_assert(sig.size() == comp.size());
+ for (int i = 0; i < comp.size(); i++)
+ if (comp[i] == RTLIL::State::Sa) {
+ sig.remove(i);
+ comp.remove(i--);
+ }
+ if (comp.size() == 0)
+ return RTLIL::SigSpec();
+
+ if (sig.size() == 1 && comp == RTLIL::SigSpec(1,1) && !ifxmode)
+ {
+ mod->connect(RTLIL::SigSig(RTLIL::SigSpec(cmp_wire, cmp_wire->width++), sig));
+ }
+ else
+ {
+ // create compare cell
+ RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str().c_str(), cmp_wire->width), ifxmode ? "$eqx" : "$eq");
+ eq_cell->attributes = sw->attributes;
+
+ eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0);
+ eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(0);
+
+ eq_cell->parameters["\\A_WIDTH"] = RTLIL::Const(sig.size());
+ eq_cell->parameters["\\B_WIDTH"] = RTLIL::Const(comp.size());
+ eq_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
+
+ eq_cell->setPort("\\A", sig);
+ eq_cell->setPort("\\B", comp);
+ eq_cell->setPort("\\Y", RTLIL::SigSpec(cmp_wire, cmp_wire->width++));
+ }
+ }
+
+ RTLIL::Wire *ctrl_wire;
+ if (cmp_wire->width == 1)
+ {
+ ctrl_wire = cmp_wire;
+ }
+ else
+ {
+ ctrl_wire = mod->addWire(sstr.str() + "_CTRL");
+
+ // reduce cmp vector to one logic signal
+ RTLIL::Cell *any_cell = mod->addCell(sstr.str() + "_ANY", "$reduce_or");
+ any_cell->attributes = sw->attributes;
+
+ any_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0);
+ any_cell->parameters["\\A_WIDTH"] = RTLIL::Const(cmp_wire->width);
+ any_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
+
+ any_cell->setPort("\\A", cmp_wire);
+ any_cell->setPort("\\Y", RTLIL::SigSpec(ctrl_wire));
+ }
+
+ return RTLIL::SigSpec(ctrl_wire);
+}
+
+RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode)
+{
+ log_assert(when_signal.size() == else_signal.size());
+
+ std::stringstream sstr;
+ sstr << "$procmux$" << (autoidx++);
+
+ // the trivial cases
+ if (compare.size() == 0 || when_signal == else_signal)
+ return when_signal;
+
+ // compare results
+ RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode);
+ if (ctrl_sig.size() == 0)
+ return when_signal;
+ log_assert(ctrl_sig.size() == 1);
+
+ // prepare multiplexer output signal
+ RTLIL::Wire *result_wire = mod->addWire(sstr.str() + "_Y", when_signal.size());
+
+ // create the multiplexer itself
+ RTLIL::Cell *mux_cell = mod->addCell(sstr.str(), "$mux");
+ mux_cell->attributes = sw->attributes;
+
+ mux_cell->parameters["\\WIDTH"] = RTLIL::Const(when_signal.size());
+ mux_cell->setPort("\\A", else_signal);
+ mux_cell->setPort("\\B", when_signal);
+ mux_cell->setPort("\\S", ctrl_sig);
+ mux_cell->setPort("\\Y", RTLIL::SigSpec(result_wire));
+
+ last_mux_cell = mux_cell;
+ return RTLIL::SigSpec(result_wire);
+}
+
+void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode)
+{
+ log_assert(last_mux_cell != NULL);
+ log_assert(when_signal.size() == last_mux_cell->getPort("\\A").size());
+
+ if (when_signal == last_mux_cell->getPort("\\A"))
+ return;
+
+ RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode);
+ log_assert(ctrl_sig.size() == 1);
+ last_mux_cell->type = "$pmux";
+
+ RTLIL::SigSpec new_s = last_mux_cell->getPort("\\S");
+ new_s.append(ctrl_sig);
+ last_mux_cell->setPort("\\S", new_s);
+
+ RTLIL::SigSpec new_b = last_mux_cell->getPort("\\B");
+ new_b.append(when_signal);
+ last_mux_cell->setPort("\\B", new_b);
+
+ last_mux_cell->parameters["\\S_WIDTH"] = last_mux_cell->getPort("\\S").size();
+}
+
+RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> &swpara,
+ RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval, bool ifxmode)
+{
+ RTLIL::SigSpec result = defval;
+
+ for (auto &action : cs->actions) {
+ sig.replace(action.first, action.second, &result);
+ action.first.remove2(sig, &action.second);
+ }
+
+ for (auto sw : cs->switches)
+ {
+ if (!swcache.check(sw))
+ continue;
+
+ // detect groups of parallel cases
+ std::vector<int> pgroups(sw->cases.size());
+ bool is_simple_parallel_case = true;
+
+ if (!sw->get_bool_attribute("\\parallel_case")) {
+ if (!swpara.count(sw)) {
+ pool<Const> case_values;
+ for (size_t i = 0; i < sw->cases.size(); i++) {
+ RTLIL::CaseRule *cs2 = sw->cases[i];
+ for (auto pat : cs2->compare) {
+ if (!pat.is_fully_def())
+ goto not_simple_parallel_case;
+ Const cpat = pat.as_const();
+ if (case_values.count(cpat))
+ goto not_simple_parallel_case;
+ case_values.insert(cpat);
+ }
+ }
+ if (0)
+ not_simple_parallel_case:
+ is_simple_parallel_case = false;
+ swpara[sw] = is_simple_parallel_case;
+ } else {
+ is_simple_parallel_case = swpara.at(sw);
+ }
+ }
+
+ if (!is_simple_parallel_case) {
+ BitPatternPool pool(sw->signal.size());
+ bool extra_group_for_next_case = false;
+ for (size_t i = 0; i < sw->cases.size(); i++) {
+ RTLIL::CaseRule *cs2 = sw->cases[i];
+ if (i != 0) {
+ pgroups[i] = pgroups[i-1];
+ if (extra_group_for_next_case) {
+ pgroups[i] = pgroups[i-1]+1;
+ extra_group_for_next_case = false;
+ }
+ for (auto pat : cs2->compare)
+ if (!pat.is_fully_const() || !pool.has_all(pat))
+ pgroups[i] = pgroups[i-1]+1;
+ if (cs2->compare.empty())
+ pgroups[i] = pgroups[i-1]+1;
+ if (pgroups[i] != pgroups[i-1])
+ pool = BitPatternPool(sw->signal.size());
+ }
+ for (auto pat : cs2->compare)
+ if (!pat.is_fully_const())
+ extra_group_for_next_case = true;
+ else if (!ifxmode)
+ pool.take(pat);
+ }
+ }
+
+ // evaluate in reverse order to give the first entry the top priority
+ RTLIL::SigSpec initial_val = result;
+ RTLIL::Cell *last_mux_cell = NULL;
+ for (size_t i = 0; i < sw->cases.size(); i++) {
+ int case_idx = sw->cases.size() - i - 1;
+ RTLIL::CaseRule *cs2 = sw->cases[case_idx];
+ RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, cs2, sig, initial_val, ifxmode);
+ if (last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1])
+ append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, ifxmode);
+ else
+ result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, ifxmode);
+ }
+ }
+
+ return result;
+}
+
+void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc, bool ifxmode)
+{
+ log("Creating decoders for process `%s.%s'.\n", mod->name.c_str(), proc->name.c_str());
+
+ SigSnippets sigsnip;
+ sigsnip.insert(&proc->root_case);
+
+ SnippetSwCache swcache;
+ swcache.snippets = &sigsnip;
+ swcache.insert(&proc->root_case);
+
+ dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> swpara;
+
+ int cnt = 0;
+ for (int idx : sigsnip.snippets)
+ {
+ swcache.current_snippet = idx;
+ RTLIL::SigSpec sig = sigsnip.sigidx[idx];
+
+ log("%6d/%d: %s\n", ++cnt, GetSize(sigsnip.snippets), log_signal(sig));
+
+ RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size()), ifxmode);
+ mod->connect(RTLIL::SigSig(sig, value));
+ }
+}
+
+struct ProcMuxPass : public Pass {
+ ProcMuxPass() : Pass("proc_mux", "convert decision trees to multiplexers") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" proc_mux [options] [selection]\n");
+ log("\n");
+ log("This pass converts the decision trees in processes (originating from if-else\n");
+ log("and case statements) to trees of multiplexer cells.\n");
+ log("\n");
+ log(" -ifx\n");
+ log(" Use Verilog simulation behavior with respect to undef values in\n");
+ log(" 'case' expressions and 'if' conditions.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool ifxmode = false;
+ log_header(design, "Executing PROC_MUX pass (convert decision trees to multiplexers).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-ifx") {
+ ifxmode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto mod : design->modules())
+ if (design->selected(mod))
+ for (auto &proc_it : mod->processes)
+ if (design->selected(mod, proc_it.second))
+ proc_mux(mod, proc_it.second, ifxmode);
+ }
+} ProcMuxPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/proc/proc_rmdead.cc b/passes/proc/proc_rmdead.cc
new file mode 100644
index 00000000..5672fb47
--- /dev/null
+++ b/passes/proc/proc_rmdead.cc
@@ -0,0 +1,104 @@
+/*
+ * 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/register.h"
+#include "kernel/bitpattern.h"
+#include "kernel/log.h"
+#include <sstream>
+#include <stdlib.h>
+#include <stdio.h>
+#include <set>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void proc_rmdead(RTLIL::SwitchRule *sw, int &counter)
+{
+ BitPatternPool pool(sw->signal);
+
+ for (size_t i = 0; i < sw->cases.size(); i++)
+ {
+ bool is_default = GetSize(sw->cases[i]->compare) == 0 && (!pool.empty() || GetSize(sw->signal) == 0);
+
+ for (size_t j = 0; j < sw->cases[i]->compare.size(); j++) {
+ RTLIL::SigSpec sig = sw->cases[i]->compare[j];
+ if (!sig.is_fully_const())
+ continue;
+ if (!pool.take(sig))
+ sw->cases[i]->compare.erase(sw->cases[i]->compare.begin() + (j--));
+ }
+
+ if (!is_default) {
+ if (sw->cases[i]->compare.size() == 0) {
+ delete sw->cases[i];
+ sw->cases.erase(sw->cases.begin() + (i--));
+ counter++;
+ continue;
+ }
+ // if (pool.empty())
+ // sw->cases[i]->compare.clear();
+ }
+
+ for (auto switch_it : sw->cases[i]->switches)
+ proc_rmdead(switch_it, counter);
+
+ if (is_default)
+ pool.take_all();
+ }
+}
+
+struct ProcRmdeadPass : public Pass {
+ ProcRmdeadPass() : Pass("proc_rmdead", "eliminate dead trees in decision trees") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" proc_rmdead [selection]\n");
+ log("\n");
+ log("This pass identifies unreachable branches in decision trees and removes them.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing PROC_RMDEAD pass (remove dead branches from decision trees).\n");
+
+ extra_args(args, 1, design);
+
+ int total_counter = 0;
+ for (auto mod : design->modules()) {
+ if (!design->selected(mod))
+ continue;
+ for (auto &proc_it : mod->processes) {
+ if (!design->selected(mod, proc_it.second))
+ continue;
+ int counter = 0;
+ for (auto switch_it : proc_it.second->root_case.switches)
+ proc_rmdead(switch_it, counter);
+ if (counter > 0)
+ log("Removed %d dead cases from process %s in module %s.\n", counter,
+ proc_it.first.c_str(), log_id(mod));
+ total_counter += counter;
+ }
+ }
+
+ log("Removed a total of %d dead cases.\n", total_counter);
+ }
+} ProcRmdeadPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/Makefile.inc b/passes/sat/Makefile.inc
new file mode 100644
index 00000000..6785b750
--- /dev/null
+++ b/passes/sat/Makefile.inc
@@ -0,0 +1,9 @@
+
+OBJS += passes/sat/sat.o
+OBJS += passes/sat/freduce.o
+OBJS += passes/sat/eval.o
+OBJS += passes/sat/miter.o
+OBJS += passes/sat/expose.o
+OBJS += passes/sat/assertpmux.o
+OBJS += passes/sat/clk2fflogic.o
+
diff --git a/passes/sat/assertpmux.cc b/passes/sat/assertpmux.cc
new file mode 100644
index 00000000..63a90767
--- /dev/null
+++ b/passes/sat/assertpmux.cc
@@ -0,0 +1,240 @@
+/*
+ * 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 AssertpmuxWorker
+{
+ Module *module;
+ SigMap sigmap;
+
+ bool flag_noinit;
+ bool flag_always;
+
+ // get<0> ... mux cell
+ // get<1> ... mux port index
+ // get<2> ... mux bit index
+ dict<SigBit, pool<tuple<Cell*, int, int>>> sigbit_muxusers;
+
+ dict<SigBit, SigBit> sigbit_actsignals;
+ dict<SigSpec, SigBit> sigspec_actsignals;
+ dict<tuple<Cell*, int>, SigBit> muxport_actsignal;
+
+ AssertpmuxWorker(Module *module, bool flag_noinit, bool flag_always) :
+ module(module), sigmap(module), flag_noinit(flag_noinit), flag_always(flag_always)
+ {
+ for (auto wire : module->wires())
+ {
+ if (wire->port_output)
+ for (auto bit : sigmap(wire))
+ sigbit_actsignals[bit] = State::S1;
+ }
+
+ for (auto cell : module->cells())
+ {
+ if (cell->type.in("$mux", "$pmux"))
+ {
+ int width = cell->getParam("\\WIDTH").as_int();
+ int numports = cell->type == "$mux" ? 2 : cell->getParam("\\S_WIDTH").as_int() + 1;
+
+ SigSpec sig_a = sigmap(cell->getPort("\\A"));
+ SigSpec sig_b = sigmap(cell->getPort("\\B"));
+ SigSpec sig_s = sigmap(cell->getPort("\\S"));
+
+ for (int i = 0; i < numports; i++) {
+ SigSpec bits = i == 0 ? sig_a : sig_b.extract(width*(i-1), width);
+ for (int k = 0; k < width; k++) {
+ tuple<Cell*, int, int> muxuser(cell, i, k);
+ sigbit_muxusers[bits[k]].insert(muxuser);
+ }
+ }
+ }
+ else
+ {
+ for (auto &conn : cell->connections()) {
+ if (!cell->known() || cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ sigbit_actsignals[bit] = State::S1;
+ }
+ }
+ }
+ }
+
+ SigBit get_bit_activation(SigBit bit)
+ {
+ sigmap.apply(bit);
+
+ if (sigbit_actsignals.count(bit) == 0)
+ {
+ SigSpec output;
+
+ for (auto muxuser : sigbit_muxusers.at(bit))
+ {
+ Cell *cell = std::get<0>(muxuser);
+ int portidx = std::get<1>(muxuser);
+ int bitidx = std::get<2>(muxuser);
+
+ tuple<Cell*, int> muxport(cell, portidx);
+
+ if (muxport_actsignal.count(muxport) == 0) {
+ if (portidx == 0)
+ muxport_actsignal[muxport] = module->LogicNot(NEW_ID, cell->getPort("\\S"));
+ else
+ muxport_actsignal[muxport] = cell->getPort("\\S")[portidx-1];
+ }
+
+ output.append(module->LogicAnd(NEW_ID, muxport_actsignal.at(muxport), get_bit_activation(cell->getPort("\\Y")[bitidx])));
+ }
+
+ output.sort_and_unify();
+
+ if (GetSize(output) == 0)
+ output = State::S0;
+ else if (GetSize(output) > 1)
+ output = module->ReduceOr(NEW_ID, output);
+
+ sigbit_actsignals[bit] = output.as_bit();
+ }
+
+ return sigbit_actsignals.at(bit);
+ }
+
+ SigBit get_activation(SigSpec sig)
+ {
+ sigmap.apply(sig);
+ sig.sort_and_unify();
+
+ if (sigspec_actsignals.count(sig) == 0)
+ {
+ SigSpec output;
+
+ for (auto bit : sig)
+ output.append(get_bit_activation(bit));
+
+ output.sort_and_unify();
+
+ if (GetSize(output) == 0)
+ output = State::S0;
+ else if (GetSize(output) > 1)
+ output = module->ReduceOr(NEW_ID, output);
+
+ sigspec_actsignals[sig] = output.as_bit();
+ }
+
+ return sigspec_actsignals.at(sig);
+ }
+
+ void run(Cell *pmux)
+ {
+ log("Adding assert for $pmux cell %s.%s.\n", log_id(module), log_id(pmux));
+
+ int swidth = pmux->getParam("\\S_WIDTH").as_int();
+ int cntbits = ceil_log2(swidth+1);
+
+ SigSpec sel = pmux->getPort("\\S");
+ SigSpec cnt(State::S0, cntbits);
+
+ for (int i = 0; i < swidth; i++)
+ cnt = module->Add(NEW_ID, cnt, sel[i]);
+
+ SigSpec assert_a = module->Le(NEW_ID, cnt, SigSpec(1, cntbits));
+ SigSpec assert_en;
+
+ if (flag_noinit)
+ assert_en.append(module->LogicNot(NEW_ID, module->Initstate(NEW_ID)));
+
+ if (!flag_always)
+ assert_en.append(get_activation(pmux->getPort("\\Y")));
+
+ if (GetSize(assert_en) == 0)
+ assert_en = State::S1;
+
+ if (GetSize(assert_en) == 2)
+ assert_en = module->LogicAnd(NEW_ID, assert_en[0], assert_en[1]);
+
+ Cell *assert_cell = module->addAssert(NEW_ID, assert_a, assert_en);
+
+ if (pmux->attributes.count("\\src") != 0)
+ assert_cell->attributes["\\src"] = pmux->attributes.at("\\src");
+ }
+};
+
+struct AssertpmuxPass : public Pass {
+ AssertpmuxPass() : Pass("assertpmux", "convert internal signals to module ports") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" assertpmux [options] [selection]\n");
+ log("\n");
+ log("This command adds asserts to the design that assert that all parallel muxes\n");
+ log("($pmux cells) have a maximum of one of their inputs enable at any time.\n");
+ log("\n");
+ log(" -noinit\n");
+ log(" do not enforce the pmux condition during the init state\n");
+ log("\n");
+ log(" -always\n");
+ log(" usually the $pmux condition is only checked when the $pmux output\n");
+ log(" is used be the mux tree it drives. this option will deactivate this\n");
+ log(" additional constrained and check the $pmux condition always.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool flag_noinit = false;
+ bool flag_always = false;
+
+ log_header(design, "Executing ASSERTPMUX pass (add asserts for $pmux cells).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-noinit") {
+ flag_noinit = true;
+ continue;
+ }
+ if (args[argidx] == "-always") {
+ flag_always = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ AssertpmuxWorker worker(module, flag_noinit, flag_always);
+ vector<Cell*> pmux_cells;
+
+ for (auto cell : module->selected_cells())
+ if (cell->type == "$pmux")
+ pmux_cells.push_back(cell);
+
+ for (auto cell : pmux_cells)
+ worker.run(cell);
+ }
+
+ }
+} AssertpmuxPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc
new file mode 100644
index 00000000..ef6d5dd7
--- /dev/null
+++ b/passes/sat/clk2fflogic.cc
@@ -0,0 +1,226 @@
+/*
+ * 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 Clk2fflogicPass : public Pass {
+ Clk2fflogicPass() : Pass("clk2fflogic", "convert clocked FFs to generic $ff cells") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" clk2fflogic [options] [selection]\n");
+ log("\n");
+ log("This command replaces clocked flip-flops with generic $ff cells that use the\n");
+ log("implicit global clock. This is useful for formal verification of designs with\n");
+ log("multiple clocks.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ // bool flag_noinit = false;
+
+ log_header(design, "Executing CLK2FFLOGIC pass (convert clocked FFs to generic $ff cells).\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);
+ dict<SigBit, State> initbits;
+ pool<SigBit> del_initbits;
+
+ for (auto wire : module->wires())
+ if (wire->attributes.count("\\init") > 0)
+ {
+ Const initval = wire->attributes.at("\\init");
+ SigSpec initsig = sigmap(wire);
+
+ for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++)
+ if (initval[i] == State::S0 || initval[i] == State::S1)
+ initbits[initsig[i]] = initval[i];
+ }
+
+ for (auto cell : vector<Cell*>(module->selected_cells()))
+ {
+ if (cell->type.in("$dlatch"))
+ {
+ bool enpol = cell->parameters["\\EN_POLARITY"].as_bool();
+
+ SigSpec sig_en = cell->getPort("\\EN");
+ SigSpec sig_d = cell->getPort("\\D");
+ SigSpec sig_q = cell->getPort("\\Q");
+
+ log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(sig_en), log_signal(sig_d), log_signal(sig_q));
+
+ Wire *past_q = module->addWire(NEW_ID, GetSize(sig_q));
+ module->addFf(NEW_ID, sig_q, past_q);
+
+ if (enpol)
+ module->addMux(NEW_ID, past_q, sig_d, sig_en, sig_q);
+ else
+ module->addMux(NEW_ID, sig_d, past_q, sig_en, sig_q);
+
+ Const initval;
+ bool assign_initval = false;
+ for (int i = 0; i < GetSize(sig_d); i++) {
+ SigBit qbit = sigmap(sig_q[i]);
+ if (initbits.count(qbit)) {
+ initval.bits.push_back(initbits.at(qbit));
+ del_initbits.insert(qbit);
+ } else
+ initval.bits.push_back(State::Sx);
+ if (initval.bits.back() != State::Sx)
+ assign_initval = true;
+ }
+
+ if (assign_initval)
+ past_q->attributes["\\init"] = initval;
+
+ module->remove(cell);
+ continue;
+ }
+
+ if (cell->type.in("$dff", "$adff", "$dffsr"))
+ {
+ bool clkpol = cell->parameters["\\CLK_POLARITY"].as_bool();
+
+ SigSpec clk = cell->getPort("\\CLK");
+ Wire *past_clk = module->addWire(NEW_ID);
+ past_clk->attributes["\\init"] = clkpol ? State::S1 : State::S0;
+ module->addFf(NEW_ID, clk, past_clk);
+
+ SigSpec sig_d = cell->getPort("\\D");
+ SigSpec sig_q = cell->getPort("\\Q");
+
+ log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(clk), log_signal(sig_d), log_signal(sig_q));
+
+ SigSpec clock_edge_pattern;
+
+ if (clkpol) {
+ clock_edge_pattern.append_bit(State::S0);
+ clock_edge_pattern.append_bit(State::S1);
+ } else {
+ clock_edge_pattern.append_bit(State::S1);
+ clock_edge_pattern.append_bit(State::S0);
+ }
+
+ SigSpec clock_edge = module->Eqx(NEW_ID, {clk, SigSpec(past_clk)}, clock_edge_pattern);
+
+ Wire *past_d = module->addWire(NEW_ID, GetSize(sig_d));
+ Wire *past_q = module->addWire(NEW_ID, GetSize(sig_q));
+ module->addFf(NEW_ID, sig_d, past_d);
+ module->addFf(NEW_ID, sig_q, past_q);
+
+ if (cell->type == "$adff")
+ {
+ SigSpec arst = cell->getPort("\\ARST");
+ SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge);
+ Const rstval = cell->parameters["\\ARST_VALUE"];
+
+ if (cell->parameters["\\ARST_POLARITY"].as_bool())
+ module->addMux(NEW_ID, qval, rstval, arst, sig_q);
+ else
+ module->addMux(NEW_ID, rstval, qval, arst, sig_q);
+ }
+ else
+ if (cell->type == "$dffsr")
+ {
+ SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge);
+ SigSpec setval = cell->getPort("\\SET");
+ SigSpec clrval = cell->getPort("\\CLR");
+
+ if (!cell->parameters["\\SET_POLARITY"].as_bool())
+ setval = module->Not(NEW_ID, setval);
+
+ if (cell->parameters["\\CLR_POLARITY"].as_bool())
+ clrval = module->Not(NEW_ID, clrval);
+
+ qval = module->Or(NEW_ID, qval, setval);
+ module->addAnd(NEW_ID, qval, clrval, sig_q);
+ }
+ else
+ {
+ module->addMux(NEW_ID, past_q, past_d, clock_edge, sig_q);
+ }
+
+ Const initval;
+ bool assign_initval = false;
+ for (int i = 0; i < GetSize(sig_d); i++) {
+ SigBit qbit = sigmap(sig_q[i]);
+ if (initbits.count(qbit)) {
+ initval.bits.push_back(initbits.at(qbit));
+ del_initbits.insert(qbit);
+ } else
+ initval.bits.push_back(State::Sx);
+ if (initval.bits.back() != State::Sx)
+ assign_initval = true;
+ }
+
+ if (assign_initval) {
+ past_d->attributes["\\init"] = initval;
+ past_q->attributes["\\init"] = initval;
+ }
+
+ module->remove(cell);
+ continue;
+ }
+ }
+
+ for (auto wire : module->wires())
+ if (wire->attributes.count("\\init") > 0)
+ {
+ bool delete_initattr = true;
+ Const initval = wire->attributes.at("\\init");
+ SigSpec initsig = sigmap(wire);
+
+ for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++)
+ if (del_initbits.count(initsig[i]) > 0)
+ initval[i] = State::Sx;
+ else if (initval[i] != State::Sx)
+ delete_initattr = false;
+
+ if (delete_initattr)
+ wire->attributes.erase("\\init");
+ else
+ wire->attributes.at("\\init") = initval;
+ }
+ }
+
+ }
+} Clk2fflogicPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/eval.cc b/passes/sat/eval.cc
new file mode 100644
index 00000000..09f69cc5
--- /dev/null
+++ b/passes/sat/eval.cc
@@ -0,0 +1,603 @@
+/*
+ * 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.
+ *
+ */
+
+// [[CITE]] VlogHammer Verilog Regression Test Suite
+// http://www.clifford.at/yosys/vloghammer.html
+
+#include "kernel/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/consteval.h"
+#include "kernel/sigtools.h"
+#include "kernel/satgen.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <algorithm>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+/* this should only be used for regression testing of ConstEval -- see vloghammer */
+struct BruteForceEquivChecker
+{
+ RTLIL::Module *mod1, *mod2;
+ RTLIL::SigSpec mod1_inputs, mod1_outputs;
+ RTLIL::SigSpec mod2_inputs, mod2_outputs;
+ int counter, errors;
+ bool ignore_x_mod1;
+
+ void run_checker(RTLIL::SigSpec &inputs)
+ {
+ if (inputs.size() < mod1_inputs.size()) {
+ RTLIL::SigSpec inputs0 = inputs, inputs1 = inputs;
+ inputs0.append(RTLIL::Const(0, 1));
+ inputs1.append(RTLIL::Const(1, 1));
+ run_checker(inputs0);
+ run_checker(inputs1);
+ return;
+ }
+
+ ConstEval ce1(mod1), ce2(mod2);
+ ce1.set(mod1_inputs, inputs.as_const());
+ ce2.set(mod2_inputs, inputs.as_const());
+
+ RTLIL::SigSpec sig1 = mod1_outputs, undef1;
+ RTLIL::SigSpec sig2 = mod2_outputs, undef2;
+
+ if (!ce1.eval(sig1, undef1))
+ log("Failed ConstEval of module 1 outputs at signal %s (input: %s = %s).\n",
+ log_signal(undef1), log_signal(mod1_inputs), log_signal(inputs));
+ if (!ce2.eval(sig2, undef2))
+ log("Failed ConstEval of module 2 outputs at signal %s (input: %s = %s).\n",
+ log_signal(undef2), log_signal(mod1_inputs), log_signal(inputs));
+
+ if (ignore_x_mod1) {
+ for (int i = 0; i < GetSize(sig1); i++)
+ if (sig1[i] == RTLIL::State::Sx)
+ sig2[i] = RTLIL::State::Sx;
+ }
+
+ if (sig1 != sig2) {
+ log("Found counter-example (ignore_x_mod1 = %s):\n", ignore_x_mod1 ? "active" : "inactive");
+ log(" Module 1: %s = %s => %s = %s\n", log_signal(mod1_inputs), log_signal(inputs), log_signal(mod1_outputs), log_signal(sig1));
+ log(" Module 2: %s = %s => %s = %s\n", log_signal(mod2_inputs), log_signal(inputs), log_signal(mod2_outputs), log_signal(sig2));
+ errors++;
+ }
+
+ counter++;
+ }
+
+ BruteForceEquivChecker(RTLIL::Module *mod1, RTLIL::Module *mod2, bool ignore_x_mod1) :
+ mod1(mod1), mod2(mod2), counter(0), errors(0), ignore_x_mod1(ignore_x_mod1)
+ {
+ log("Checking for equivalence (brute-force): %s vs %s\n", mod1->name.c_str(), mod2->name.c_str());
+ for (auto &w : mod1->wires_)
+ {
+ RTLIL::Wire *wire1 = w.second;
+ if (wire1->port_id == 0)
+ continue;
+
+ if (mod2->wires_.count(wire1->name) == 0)
+ log_cmd_error("Port %s in module 1 has no counterpart in module 2!\n", wire1->name.c_str());
+
+ RTLIL::Wire *wire2 = mod2->wires_.at(wire1->name);
+ if (wire1->width != wire2->width || wire1->port_input != wire2->port_input || wire1->port_output != wire2->port_output)
+ log_cmd_error("Port %s in module 1 does not match its counterpart in module 2!\n", wire1->name.c_str());
+
+ if (wire1->port_input) {
+ mod1_inputs.append(wire1);
+ mod2_inputs.append(wire2);
+ } else {
+ mod1_outputs.append(wire1);
+ mod2_outputs.append(wire2);
+ }
+ }
+
+ RTLIL::SigSpec inputs;
+ run_checker(inputs);
+ }
+};
+
+/* this should only be used for regression testing of ConstEval -- see vloghammer */
+struct VlogHammerReporter
+{
+ RTLIL::Design *design;
+ std::vector<RTLIL::Module*> modules;
+ std::vector<std::string> module_names;
+ std::vector<RTLIL::IdString> inputs;
+ std::vector<int> input_widths;
+ std::vector<RTLIL::Const> patterns;
+ int total_input_width;
+
+ std::vector<std::string> split(std::string text, const char *delim)
+ {
+ std::vector<std::string> list;
+ char *p = strdup(text.c_str());
+ char *t = strtok(p, delim);
+ while (t != NULL) {
+ list.push_back(t);
+ t = strtok(NULL, delim);
+ }
+ free(p);
+ return list;
+ }
+
+ void sat_check(RTLIL::Module *module, RTLIL::SigSpec recorded_set_vars, RTLIL::Const recorded_set_vals, RTLIL::SigSpec expected_y, bool model_undef)
+ {
+ log("Verifying SAT model (%s)..\n", model_undef ? "with undef" : "without undef");
+
+ ezSatPtr ez;
+ SigMap sigmap(module);
+ SatGen satgen(ez.get(), &sigmap);
+ satgen.model_undef = model_undef;
+
+ for (auto &c : module->cells_)
+ if (!satgen.importCell(c.second))
+ log_error("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(c.first), RTLIL::id2cstr(c.second->type));
+
+ ez->assume(satgen.signals_eq(recorded_set_vars, recorded_set_vals));
+
+ std::vector<int> y_vec = satgen.importDefSigSpec(module->wires_.at("\\y"));
+ std::vector<bool> y_values;
+
+ if (model_undef) {
+ std::vector<int> y_undef_vec = satgen.importUndefSigSpec(module->wires_.at("\\y"));
+ y_vec.insert(y_vec.end(), y_undef_vec.begin(), y_undef_vec.end());
+ }
+
+ log(" Created SAT problem with %d variables and %d clauses.\n",
+ ez->numCnfVariables(), ez->numCnfClauses());
+
+ if (!ez->solve(y_vec, y_values))
+ log_error("Failed to find solution to SAT problem.\n");
+
+ for (int i = 0; i < expected_y.size(); i++) {
+ RTLIL::State solution_bit = y_values.at(i) ? RTLIL::State::S1 : RTLIL::State::S0;
+ RTLIL::State expected_bit = expected_y[i].data;
+ if (model_undef) {
+ if (y_values.at(expected_y.size()+i))
+ solution_bit = RTLIL::State::Sx;
+ } else {
+ if (expected_bit == RTLIL::State::Sx)
+ continue;
+ }
+ if (solution_bit != expected_bit) {
+ std::string sat_bits, rtl_bits;
+ for (int k = expected_y.size()-1; k >= 0; k--) {
+ if (model_undef && y_values.at(expected_y.size()+k))
+ sat_bits += "x";
+ else
+ sat_bits += y_values.at(k) ? "1" : "0";
+ rtl_bits += expected_y[k] == RTLIL::State::Sx ? "x" : expected_y[k] == RTLIL::State::S1 ? "1" : "0";
+ }
+ log_error("Found error in SAT model: y[%d] = %s, should be %s:\n SAT: %s\n RTL: %s\n %*s^\n",
+ int(i), log_signal(solution_bit), log_signal(expected_bit),
+ sat_bits.c_str(), rtl_bits.c_str(), expected_y.size()-i-1, "");
+ }
+ }
+
+ if (model_undef)
+ {
+ std::vector<int> cmp_vars;
+ std::vector<bool> cmp_vals;
+
+ std::vector<bool> y_undef(y_values.begin() + expected_y.size(), y_values.end());
+
+ for (int i = 0; i < expected_y.size(); i++)
+ if (y_undef.at(i))
+ {
+ log(" Toggling undef bit %d to test undef gating.\n", i);
+ if (!ez->solve(y_vec, y_values, ez->IFF(y_vec.at(i), y_values.at(i) ? ez->CONST_FALSE : ez->CONST_TRUE)))
+ log_error("Failed to find solution with toggled bit!\n");
+
+ cmp_vars.push_back(y_vec.at(expected_y.size() + i));
+ cmp_vals.push_back(true);
+ }
+ else
+ {
+ cmp_vars.push_back(y_vec.at(i));
+ cmp_vals.push_back(y_values.at(i));
+
+ cmp_vars.push_back(y_vec.at(expected_y.size() + i));
+ cmp_vals.push_back(false);
+ }
+
+ log(" Testing if SAT solution is unique.\n");
+ ez->assume(ez->vec_ne(cmp_vars, ez->vec_const(cmp_vals)));
+ if (ez->solve(y_vec, y_values))
+ log_error("Found two distinct solutions to SAT problem.\n");
+ }
+ else
+ {
+ log(" Testing if SAT solution is unique.\n");
+ ez->assume(ez->vec_ne(y_vec, ez->vec_const(y_values)));
+ if (ez->solve(y_vec, y_values))
+ log_error("Found two distinct solutions to SAT problem.\n");
+ }
+
+ log(" SAT model verified.\n");
+ }
+
+ void run()
+ {
+ for (int idx = 0; idx < int(patterns.size()); idx++)
+ {
+ log("Creating report for pattern %d: %s\n", idx, log_signal(patterns[idx]));
+ std::string input_pattern_list;
+ RTLIL::SigSpec rtl_sig;
+
+ for (int mod = 0; mod < int(modules.size()); mod++)
+ {
+ RTLIL::SigSpec recorded_set_vars;
+ RTLIL::Const recorded_set_vals;
+ RTLIL::Module *module = modules[mod];
+ std::string module_name = module_names[mod].c_str();
+ ConstEval ce(module);
+
+ std::vector<RTLIL::State> bits(patterns[idx].bits.begin(), patterns[idx].bits.begin() + total_input_width);
+ for (int i = 0; i < int(inputs.size()); i++) {
+ RTLIL::Wire *wire = module->wires_.at(inputs[i]);
+ for (int j = input_widths[i]-1; j >= 0; j--) {
+ ce.set(RTLIL::SigSpec(wire, j), bits.back());
+ recorded_set_vars.append(RTLIL::SigSpec(wire, j));
+ recorded_set_vals.bits.push_back(bits.back());
+ bits.pop_back();
+ }
+ if (module == modules.front()) {
+ RTLIL::SigSpec sig(wire);
+ if (!ce.eval(sig))
+ log_error("Can't read back value for port %s!\n", RTLIL::id2cstr(inputs[i]));
+ input_pattern_list += stringf(" %s", sig.as_const().as_string().c_str());
+ log("++PAT++ %d %s %s #\n", idx, RTLIL::id2cstr(inputs[i]), sig.as_const().as_string().c_str());
+ }
+ }
+
+ if (module->wires_.count("\\y") == 0)
+ log_error("No output wire (y) found in module %s!\n", RTLIL::id2cstr(module->name));
+
+ RTLIL::SigSpec sig(module->wires_.at("\\y"));
+ RTLIL::SigSpec undef;
+
+ while (!ce.eval(sig, undef)) {
+ // log_error("Evaluation of y in module %s failed: sig=%s, undef=%s\n", RTLIL::id2cstr(module->name), log_signal(sig), log_signal(undef));
+ log_warning("Setting signal %s in module %s to undef.\n", log_signal(undef), RTLIL::id2cstr(module->name));
+ ce.set(undef, RTLIL::Const(RTLIL::State::Sx, undef.size()));
+ }
+
+ log("++VAL++ %d %s %s #\n", idx, module_name.c_str(), sig.as_const().as_string().c_str());
+
+ if (module_name == "rtl") {
+ rtl_sig = sig;
+ sat_check(module, recorded_set_vars, recorded_set_vals, sig, false);
+ sat_check(module, recorded_set_vars, recorded_set_vals, sig, true);
+ } else if (rtl_sig.size() > 0) {
+ if (rtl_sig.size() != sig.size())
+ log_error("Output (y) has a different width in module %s compared to rtl!\n", RTLIL::id2cstr(module->name));
+ for (int i = 0; i < GetSize(sig); i++)
+ if (rtl_sig[i] == RTLIL::State::Sx)
+ sig[i] = RTLIL::State::Sx;
+ }
+
+ log("++RPT++ %d%s %s %s\n", idx, input_pattern_list.c_str(), sig.as_const().as_string().c_str(), module_name.c_str());
+ }
+
+ log("++RPT++ ----\n");
+ }
+ log("++OK++\n");
+ }
+
+ VlogHammerReporter(RTLIL::Design *design, std::string module_prefix, std::string module_list, std::string input_list, std::string pattern_list) : design(design)
+ {
+ for (auto name : split(module_list, ",")) {
+ RTLIL::IdString esc_name = RTLIL::escape_id(module_prefix + name);
+ if (design->modules_.count(esc_name) == 0)
+ log_error("Can't find module %s in current design!\n", name.c_str());
+ log("Using module %s (%s).\n", esc_name.c_str(), name.c_str());
+ modules.push_back(design->modules_.at(esc_name));
+ module_names.push_back(name);
+ }
+
+ total_input_width = 0;
+ for (auto name : split(input_list, ",")) {
+ int width = -1;
+ RTLIL::IdString esc_name = RTLIL::escape_id(name);
+ for (auto mod : modules) {
+ if (mod->wires_.count(esc_name) == 0)
+ log_error("Can't find input %s in module %s!\n", name.c_str(), RTLIL::id2cstr(mod->name));
+ RTLIL::Wire *port = mod->wires_.at(esc_name);
+ if (!port->port_input || port->port_output)
+ log_error("Wire %s in module %s is not an input!\n", name.c_str(), RTLIL::id2cstr(mod->name));
+ if (width >= 0 && width != port->width)
+ log_error("Port %s has different sizes in the different modules!\n", name.c_str());
+ width = port->width;
+ }
+ log("Using input port %s with width %d.\n", esc_name.c_str(), width);
+ inputs.push_back(esc_name);
+ input_widths.push_back(width);
+ total_input_width += width;
+ }
+
+ for (auto pattern : split(pattern_list, ",")) {
+ RTLIL::SigSpec sig;
+ bool invert_pattern = false;
+ if (pattern.size() > 0 && pattern[0] == '~') {
+ invert_pattern = true;
+ pattern = pattern.substr(1);
+ }
+ if (!RTLIL::SigSpec::parse(sig, NULL, pattern) || !sig.is_fully_const())
+ log_error("Failed to parse pattern %s!\n", pattern.c_str());
+ if (sig.size() < total_input_width)
+ log_error("Pattern %s is to short!\n", pattern.c_str());
+ patterns.push_back(sig.as_const());
+ if (invert_pattern) {
+ for (auto &bit : patterns.back().bits)
+ if (bit == RTLIL::State::S0)
+ bit = RTLIL::State::S1;
+ else if (bit == RTLIL::State::S1)
+ bit = RTLIL::State::S0;
+ }
+ log("Using pattern %s.\n", patterns.back().as_string().c_str());
+ }
+ }
+};
+
+struct EvalPass : public Pass {
+ EvalPass() : Pass("eval", "evaluate the circuit given an input") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" eval [options] [selection]\n");
+ log("\n");
+ log("This command evaluates the value of a signal given the value of all required\n");
+ log("inputs.\n");
+ log("\n");
+ log(" -set <signal> <value>\n");
+ log(" set the specified signal to the specified value.\n");
+ log("\n");
+ log(" -set-undef\n");
+ log(" set all unspecified source signals to undef (x)\n");
+ log("\n");
+ log(" -table <signal>\n");
+ log(" create a truth table using the specified input signals\n");
+ log("\n");
+ log(" -show <signal>\n");
+ log(" show the value for the specified signal. if no -show option is passed\n");
+ log(" then all output ports of the current module are used.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::vector<std::pair<std::string, std::string>> sets;
+ std::vector<std::string> shows, tables;
+ bool set_undef = false;
+
+ log_header(design, "Executing EVAL pass (evaluate the circuit given an input).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-set" && argidx+2 < args.size()) {
+ std::string lhs = args[++argidx].c_str();
+ std::string rhs = args[++argidx].c_str();
+ sets.push_back(std::pair<std::string, std::string>(lhs, rhs));
+ continue;
+ }
+ if (args[argidx] == "-set-undef") {
+ set_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-show" && argidx+1 < args.size()) {
+ shows.push_back(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-table" && argidx+1 < args.size()) {
+ tables.push_back(args[++argidx]);
+ continue;
+ }
+ if ((args[argidx] == "-brute_force_equiv_checker" || args[argidx] == "-brute_force_equiv_checker_x") && argidx+3 == args.size()) {
+ /* this should only be used for regression testing of ConstEval -- see vloghammer */
+ std::string mod1_name = RTLIL::escape_id(args[++argidx]);
+ std::string mod2_name = RTLIL::escape_id(args[++argidx]);
+ if (design->modules_.count(mod1_name) == 0)
+ log_error("Can't find module `%s'!\n", mod1_name.c_str());
+ if (design->modules_.count(mod2_name) == 0)
+ log_error("Can't find module `%s'!\n", mod2_name.c_str());
+ BruteForceEquivChecker checker(design->modules_.at(mod1_name), design->modules_.at(mod2_name), args[argidx-2] == "-brute_force_equiv_checker_x");
+ if (checker.errors > 0)
+ log_cmd_error("Modules are not equivalent!\n");
+ log("Verified %s = %s (using brute-force check on %d cases).\n",
+ mod1_name.c_str(), mod2_name.c_str(), checker.counter);
+ return;
+ }
+ if (args[argidx] == "-vloghammer_report" && argidx+5 == args.size()) {
+ /* this should only be used for regression testing of ConstEval -- see vloghammer */
+ std::string module_prefix = args[++argidx];
+ std::string module_list = args[++argidx];
+ std::string input_list = args[++argidx];
+ std::string pattern_list = args[++argidx];
+ VlogHammerReporter reporter(design, module_prefix, module_list, input_list, pattern_list);
+ reporter.run();
+ return;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ RTLIL::Module *module = NULL;
+ for (auto &mod_it : design->modules_)
+ if (design->selected(mod_it.second)) {
+ if (module)
+ log_cmd_error("Only one module must be selected for the EVAL pass! (selected: %s and %s)\n",
+ RTLIL::id2cstr(module->name), RTLIL::id2cstr(mod_it.first));
+ module = mod_it.second;
+ }
+ if (module == NULL)
+ log_cmd_error("Can't perform EVAL on an empty selection!\n");
+
+ ConstEval ce(module);
+
+ for (auto &it : sets) {
+ RTLIL::SigSpec lhs, rhs;
+ if (!RTLIL::SigSpec::parse_sel(lhs, design, module, it.first))
+ log_cmd_error("Failed to parse lhs set expression `%s'.\n", it.first.c_str());
+ if (!RTLIL::SigSpec::parse_rhs(lhs, rhs, module, it.second))
+ log_cmd_error("Failed to parse rhs set expression `%s'.\n", it.second.c_str());
+ if (!rhs.is_fully_const())
+ log_cmd_error("Right-hand-side set expression `%s' is not constant.\n", it.second.c_str());
+ if (lhs.size() != rhs.size())
+ log_cmd_error("Set expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n",
+ it.first.c_str(), log_signal(lhs), lhs.size(), it.second.c_str(), log_signal(rhs), rhs.size());
+ ce.set(lhs, rhs.as_const());
+ }
+
+ if (shows.size() == 0) {
+ for (auto &it : module->wires_)
+ if (it.second->port_output)
+ shows.push_back(it.second->name.str());
+ }
+
+ if (tables.empty())
+ {
+ for (auto &it : shows) {
+ RTLIL::SigSpec signal, value, undef;
+ if (!RTLIL::SigSpec::parse_sel(signal, design, module, it))
+ log_cmd_error("Failed to parse show expression `%s'.\n", it.c_str());
+ value = signal;
+ if (set_undef) {
+ while (!ce.eval(value, undef)) {
+ log("Failed to evaluate signal %s: Missing value for %s. -> setting to undef\n", log_signal(signal), log_signal(undef));
+ ce.set(undef, RTLIL::Const(RTLIL::State::Sx, undef.size()));
+ undef = RTLIL::SigSpec();
+ }
+ log("Eval result: %s = %s.\n", log_signal(signal), log_signal(value));
+ } else {
+ if (!ce.eval(value, undef))
+ log("Failed to evaluate signal %s: Missing value for %s.\n", log_signal(signal), log_signal(undef));
+ else
+ log("Eval result: %s = %s.\n", log_signal(signal), log_signal(value));
+ }
+ }
+ }
+ else
+ {
+ RTLIL::SigSpec tabsigs, signal, value, undef;
+ std::vector<std::vector<std::string>> tab;
+ int tab_sep_colidx = 0;
+
+ for (auto &it : shows) {
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, it))
+ log_cmd_error("Failed to parse show expression `%s'.\n", it.c_str());
+ signal.append(sig);
+ }
+
+ for (auto &it : tables) {
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, it))
+ log_cmd_error("Failed to parse table expression `%s'.\n", it.c_str());
+ tabsigs.append(sig);
+ }
+
+ std::vector<std::string> tab_line;
+ for (auto &c : tabsigs.chunks())
+ tab_line.push_back(log_signal(c));
+ tab_sep_colidx = tab_line.size();
+ for (auto &c : signal.chunks())
+ tab_line.push_back(log_signal(c));
+ tab.push_back(tab_line);
+ tab_line.clear();
+
+ RTLIL::Const tabvals(0, tabsigs.size());
+ do
+ {
+ ce.push();
+ ce.set(tabsigs, tabvals);
+ value = signal;
+
+ RTLIL::SigSpec this_undef;
+ while (!ce.eval(value, this_undef)) {
+ if (!set_undef) {
+ log("Failed to evaluate signal %s at %s = %s: Missing value for %s.\n", log_signal(signal),
+ log_signal(tabsigs), log_signal(tabvals), log_signal(this_undef));
+ return;
+ }
+ ce.set(this_undef, RTLIL::Const(RTLIL::State::Sx, this_undef.size()));
+ undef.append(this_undef);
+ this_undef = RTLIL::SigSpec();
+ }
+
+ int pos = 0;
+ for (auto &c : tabsigs.chunks()) {
+ tab_line.push_back(log_signal(RTLIL::SigSpec(tabvals).extract(pos, c.width)));
+ pos += c.width;
+ }
+
+ pos = 0;
+ for (auto &c : signal.chunks()) {
+ tab_line.push_back(log_signal(value.extract(pos, c.width)));
+ pos += c.width;
+ }
+
+ tab.push_back(tab_line);
+ tab_line.clear();
+ ce.pop();
+
+ tabvals = RTLIL::const_add(tabvals, RTLIL::Const(1), false, false, tabvals.bits.size());
+ }
+ while (tabvals.as_bool());
+
+ std::vector<int> tab_column_width;
+ for (auto &row : tab) {
+ if (tab_column_width.size() < row.size())
+ tab_column_width.resize(row.size());
+ for (size_t i = 0; i < row.size(); i++)
+ tab_column_width[i] = max(tab_column_width[i], int(row[i].size()));
+ }
+
+ log("\n");
+ bool first = true;
+ for (auto &row : tab) {
+ for (size_t i = 0; i < row.size(); i++) {
+ int k = int(i) < tab_sep_colidx ? tab_sep_colidx - i - 1 : i;
+ log(" %s%*s", k == tab_sep_colidx ? "| " : "", tab_column_width[k], row[k].c_str());
+ }
+ log("\n");
+ if (first) {
+ for (size_t i = 0; i < row.size(); i++) {
+ int k = int(i) < tab_sep_colidx ? tab_sep_colidx - i - 1 : i;
+ log(" %s", k == tab_sep_colidx ? "| " : "");
+ for (int j = 0; j < tab_column_width[k]; j++)
+ log("-");
+ }
+ log("\n");
+ first = false;
+ }
+ }
+
+ log("\n");
+ if (undef.size() > 0) {
+ undef.sort_and_unify();
+ log("Assumed undef (x) value for the following signals: %s\n\n", log_signal(undef));
+ }
+ }
+ }
+} EvalPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/example.v b/passes/sat/example.v
new file mode 100644
index 00000000..aa0ddb6e
--- /dev/null
+++ b/passes/sat/example.v
@@ -0,0 +1,85 @@
+
+module example001(a, y);
+
+input [15:0] a;
+output y;
+
+wire gt = a > 12345;
+wire lt = a < 12345;
+assign y = !gt && !lt;
+
+endmodule
+
+// ------------------------------------
+
+module example002(a, y);
+
+input [3:0] a;
+output y;
+reg [1:0] t1, t2;
+
+always @* begin
+ casex (a)
+ 16'b1xxx:
+ t1 <= 1;
+ 16'bx1xx:
+ t1 <= 2;
+ 16'bxx1x:
+ t1 <= 3;
+ 16'bxxx1:
+ t1 <= 4;
+ default:
+ t1 <= 0;
+ endcase
+ casex (a)
+ 16'b1xxx:
+ t2 <= 1;
+ 16'b01xx:
+ t2 <= 2;
+ 16'b001x:
+ t2 <= 3;
+ 16'b0001:
+ t2 <= 4;
+ default:
+ t2 <= 0;
+ endcase
+end
+
+assign y = t1 != t2;
+
+endmodule
+
+// ------------------------------------
+
+module example003(a_shl, a_shr, a_sshl, a_sshr, sh, y_shl, y_shr, y_sshl, y_sshr);
+
+input [7:0] a_shl, a_shr;
+input signed [7:0] a_sshl, a_sshr;
+input [3:0] sh;
+
+output [7:0] y_shl = a_shl << sh, y_shr = a_shr >> sh;
+output signed [7:0] y_sshl = a_sshl <<< sh, y_sshr = a_sshr >>> sh;
+
+endmodule
+
+// ------------------------------------
+
+module example004(clk, rst, y);
+
+input clk, rst;
+output y;
+
+reg [3:0] counter;
+
+always @(posedge clk)
+ case (1'b1)
+ rst, counter == 9:
+ counter <= 0;
+ default:
+ counter <= counter+1;
+ endcase
+
+assign y = counter == 12;
+
+endmodule
+
diff --git a/passes/sat/example.ys b/passes/sat/example.ys
new file mode 100644
index 00000000..cc72faac
--- /dev/null
+++ b/passes/sat/example.ys
@@ -0,0 +1,14 @@
+
+read_verilog example.v
+proc; opt_clean
+echo on
+
+sat -set y 1'b1 example001
+sat -set y 1'b1 example002
+sat -set y_sshl 8'hf0 -set y_sshr 8'hf0 -set sh 4'd3 example003
+sat -set y 1'b1 -ignore_unknown_cells example004
+sat -show rst,counter -set-at 3 y 1'b1 -seq 4 example004
+
+sat -prove y 1'b0 -show rst,counter,y -ignore_unknown_cells example004
+sat -prove y 1'b0 -tempinduct -show rst,counter,y -set-at 1 rst 1'b1 -seq 1 example004
+
diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc
new file mode 100644
index 00000000..9427547f
--- /dev/null
+++ b/passes/sat/expose.cc
@@ -0,0 +1,650 @@
+/*
+ * 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/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/sigtools.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct dff_map_info_t {
+ RTLIL::SigSpec sig_d, sig_clk, sig_arst;
+ bool clk_polarity, arst_polarity;
+ RTLIL::Const arst_value;
+ std::vector<RTLIL::IdString> cells;
+};
+
+struct dff_map_bit_info_t {
+ RTLIL::SigBit bit_d, bit_clk, bit_arst;
+ bool clk_polarity, arst_polarity;
+ RTLIL::State arst_value;
+ RTLIL::Cell *cell;
+};
+
+bool consider_wire(RTLIL::Wire *wire, std::map<RTLIL::IdString, dff_map_info_t> &dff_dq_map)
+{
+ if (wire->name[0] == '$' || dff_dq_map.count(wire->name))
+ return false;
+ if (wire->port_input)
+ return false;
+ return true;
+}
+
+bool consider_cell(RTLIL::Design *design, std::set<RTLIL::IdString> &dff_cells, RTLIL::Cell *cell)
+{
+ if (cell->name[0] == '$' || dff_cells.count(cell->name))
+ return false;
+ if (cell->type[0] == '\\' && !design->modules_.count(cell->type))
+ return false;
+ return true;
+}
+
+bool compare_wires(RTLIL::Wire *wire1, RTLIL::Wire *wire2)
+{
+ log_assert(wire1->name == wire2->name);
+ if (wire1->width != wire2->width)
+ return false;
+ return true;
+}
+
+bool compare_cells(RTLIL::Cell *cell1, RTLIL::Cell *cell2)
+{
+ log_assert(cell1->name == cell2->name);
+ if (cell1->type != cell2->type)
+ return false;
+ if (cell1->parameters != cell2->parameters)
+ return false;
+ return true;
+}
+
+void find_dff_wires(std::set<RTLIL::IdString> &dff_wires, RTLIL::Module *module)
+{
+ CellTypes ct;
+ ct.setup_internals_mem();
+ ct.setup_stdcells_mem();
+
+ SigMap sigmap(module);
+ SigPool dffsignals;
+
+ for (auto &it : module->cells_) {
+ if (ct.cell_known(it.second->type) && it.second->hasPort("\\Q"))
+ dffsignals.add(sigmap(it.second->getPort("\\Q")));
+ }
+
+ for (auto &it : module->wires_) {
+ if (dffsignals.check_any(it.second))
+ dff_wires.insert(it.first);
+ }
+}
+
+void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::Design *design, RTLIL::Module *module)
+{
+ std::map<RTLIL::SigBit, dff_map_bit_info_t> bit_info;
+ SigMap sigmap(module);
+
+ for (auto &it : module->cells_)
+ {
+ if (!design->selected(module, it.second))
+ continue;
+
+ dff_map_bit_info_t info;
+ info.bit_d = RTLIL::State::Sm;
+ info.bit_clk = RTLIL::State::Sm;
+ info.bit_arst = RTLIL::State::Sm;
+ info.clk_polarity = false;
+ info.arst_polarity = false;
+ info.arst_value = RTLIL::State::Sm;
+ info.cell = it.second;
+
+ if (info.cell->type == "$dff") {
+ info.bit_clk = sigmap(info.cell->getPort("\\CLK")).as_bit();
+ info.clk_polarity = info.cell->parameters.at("\\CLK_POLARITY").as_bool();
+ std::vector<RTLIL::SigBit> sig_d = sigmap(info.cell->getPort("\\D")).to_sigbit_vector();
+ std::vector<RTLIL::SigBit> sig_q = sigmap(info.cell->getPort("\\Q")).to_sigbit_vector();
+ for (size_t i = 0; i < sig_d.size(); i++) {
+ info.bit_d = sig_d.at(i);
+ bit_info[sig_q.at(i)] = info;
+ }
+ continue;
+ }
+
+ if (info.cell->type == "$adff") {
+ info.bit_clk = sigmap(info.cell->getPort("\\CLK")).as_bit();
+ info.bit_arst = sigmap(info.cell->getPort("\\ARST")).as_bit();
+ info.clk_polarity = info.cell->parameters.at("\\CLK_POLARITY").as_bool();
+ info.arst_polarity = info.cell->parameters.at("\\ARST_POLARITY").as_bool();
+ std::vector<RTLIL::SigBit> sig_d = sigmap(info.cell->getPort("\\D")).to_sigbit_vector();
+ std::vector<RTLIL::SigBit> sig_q = sigmap(info.cell->getPort("\\Q")).to_sigbit_vector();
+ std::vector<RTLIL::State> arst_value = info.cell->parameters.at("\\ARST_VALUE").bits;
+ for (size_t i = 0; i < sig_d.size(); i++) {
+ info.bit_d = sig_d.at(i);
+ info.arst_value = arst_value.at(i);
+ bit_info[sig_q.at(i)] = info;
+ }
+ continue;
+ }
+
+ if (info.cell->type == "$_DFF_N_" || info.cell->type == "$_DFF_P_") {
+ info.bit_clk = sigmap(info.cell->getPort("\\C")).as_bit();
+ info.clk_polarity = info.cell->type == "$_DFF_P_";
+ info.bit_d = sigmap(info.cell->getPort("\\D")).as_bit();
+ bit_info[sigmap(info.cell->getPort("\\Q")).as_bit()] = info;
+ continue;
+ }
+
+ if (info.cell->type.size() == 10 && info.cell->type.substr(0, 6) == "$_DFF_") {
+ info.bit_clk = sigmap(info.cell->getPort("\\C")).as_bit();
+ info.bit_arst = sigmap(info.cell->getPort("\\R")).as_bit();
+ info.clk_polarity = info.cell->type[6] == 'P';
+ info.arst_polarity = info.cell->type[7] == 'P';
+ info.arst_value = info.cell->type[0] == '1' ? RTLIL::State::S1 : RTLIL::State::S0;
+ info.bit_d = sigmap(info.cell->getPort("\\D")).as_bit();
+ bit_info[sigmap(info.cell->getPort("\\Q")).as_bit()] = info;
+ continue;
+ }
+ }
+
+ std::map<RTLIL::IdString, dff_map_info_t> empty_dq_map;
+ for (auto &it : module->wires_)
+ {
+ if (!consider_wire(it.second, empty_dq_map))
+ continue;
+
+ std::vector<RTLIL::SigBit> bits_q = sigmap(it.second).to_sigbit_vector();
+ std::vector<RTLIL::SigBit> bits_d;
+ std::vector<RTLIL::State> arst_value;
+ std::set<RTLIL::Cell*> cells;
+
+ if (bits_q.empty() || !bit_info.count(bits_q.front()))
+ continue;
+
+ dff_map_bit_info_t ref_info = bit_info.at(bits_q.front());
+ for (auto &bit : bits_q) {
+ if (!bit_info.count(bit))
+ break;
+ dff_map_bit_info_t info = bit_info.at(bit);
+ if (info.bit_clk != ref_info.bit_clk)
+ break;
+ if (info.bit_arst != ref_info.bit_arst)
+ break;
+ if (info.clk_polarity != ref_info.clk_polarity)
+ break;
+ if (info.arst_polarity != ref_info.arst_polarity)
+ break;
+ bits_d.push_back(info.bit_d);
+ arst_value.push_back(info.arst_value);
+ cells.insert(info.cell);
+ }
+
+ if (bits_d.size() != bits_q.size())
+ continue;
+
+ dff_map_info_t info;
+ info.sig_d = bits_d;
+ info.sig_clk = ref_info.bit_clk;
+ info.sig_arst = ref_info.bit_arst;
+ info.clk_polarity = ref_info.clk_polarity;
+ info.arst_polarity = ref_info.arst_polarity;
+ info.arst_value = arst_value;
+ for (auto it : cells)
+ info.cells.push_back(it->name);
+ map[it.first] = info;
+ }
+}
+
+RTLIL::Wire *add_new_wire(RTLIL::Module *module, RTLIL::IdString name, int width = 1)
+{
+ if (module->count_id(name))
+ log_error("Attempting to create wire %s, but a wire of this name exists already! Hint: Try another value for -sep.\n", log_id(name));
+ return module->addWire(name, width);
+}
+
+struct ExposePass : public Pass {
+ ExposePass() : Pass("expose", "convert internal signals to module ports") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" expose [options] [selection]\n");
+ log("\n");
+ log("This command exposes all selected internal signals of a module as additional\n");
+ log("outputs.\n");
+ log("\n");
+ log(" -dff\n");
+ log(" only consider wires that are directly driven by register cell.\n");
+ log("\n");
+ log(" -cut\n");
+ log(" when exposing a wire, create an input/output pair and cut the internal\n");
+ log(" signal path at that wire.\n");
+ log("\n");
+ log(" -shared\n");
+ log(" only expose those signals that are shared among the selected modules.\n");
+ log(" this is useful for preparing modules for equivalence checking.\n");
+ log("\n");
+ log(" -evert\n");
+ log(" also turn connections to instances of other modules to additional\n");
+ log(" inputs and outputs and remove the module instances.\n");
+ log("\n");
+ log(" -evert-dff\n");
+ log(" turn flip-flops to sets of inputs and outputs.\n");
+ log("\n");
+ log(" -sep <separator>\n");
+ log(" when creating new wire/port names, the original object name is suffixed\n");
+ log(" with this separator (default: '.') and the port name or a type\n");
+ log(" designator for the exposed signal.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool flag_shared = false;
+ bool flag_evert = false;
+ bool flag_dff = false;
+ bool flag_cut = false;
+ bool flag_evert_dff = false;
+ std::string sep = ".";
+
+ log_header(design, "Executing EXPOSE pass (exposing internal signals as outputs).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-shared") {
+ flag_shared = true;
+ continue;
+ }
+ if (args[argidx] == "-evert") {
+ flag_evert = true;
+ continue;
+ }
+ if (args[argidx] == "-dff") {
+ flag_dff = true;
+ continue;
+ }
+ if (args[argidx] == "-cut") {
+ flag_cut = true;
+ continue;
+ }
+ if (args[argidx] == "-evert-dff") {
+ flag_evert_dff = true;
+ continue;
+ }
+ if (args[argidx] == "-sep" && argidx+1 < args.size()) {
+ sep = args[++argidx];
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ CellTypes ct(design);
+
+ std::map<RTLIL::Module*, std::map<RTLIL::IdString, dff_map_info_t>> dff_dq_maps;
+ std::map<RTLIL::Module*, std::set<RTLIL::IdString>> dff_cells;
+
+ if (flag_evert_dff)
+ {
+ RTLIL::Module *first_module = NULL;
+ std::set<RTLIL::IdString> shared_dff_wires;
+
+ for (auto &mod_it : design->modules_)
+ {
+ if (!design->selected(mod_it.second))
+ continue;
+
+ create_dff_dq_map(dff_dq_maps[mod_it.second], design, mod_it.second);
+
+ if (!flag_shared)
+ continue;
+
+ if (first_module == NULL) {
+ for (auto &it : dff_dq_maps[mod_it.second])
+ shared_dff_wires.insert(it.first);
+ first_module = mod_it.second;
+ } else {
+ std::set<RTLIL::IdString> new_shared_dff_wires;
+ for (auto &it : shared_dff_wires) {
+ if (!dff_dq_maps[mod_it.second].count(it))
+ continue;
+ if (!compare_wires(first_module->wires_.at(it), mod_it.second->wires_.at(it)))
+ continue;
+ new_shared_dff_wires.insert(it);
+ }
+ shared_dff_wires.swap(new_shared_dff_wires);
+ }
+ }
+
+ if (flag_shared)
+ for (auto &map_it : dff_dq_maps)
+ {
+ std::map<RTLIL::IdString, dff_map_info_t> new_map;
+ for (auto &it : map_it.second)
+ if (shared_dff_wires.count(it.first))
+ new_map[it.first] = it.second;
+ map_it.second.swap(new_map);
+ }
+
+ for (auto &it1 : dff_dq_maps)
+ for (auto &it2 : it1.second)
+ for (auto &it3 : it2.second.cells)
+ dff_cells[it1.first].insert(it3);
+ }
+
+ std::set<RTLIL::IdString> shared_wires, shared_cells;
+ std::set<RTLIL::IdString> used_names;
+
+ if (flag_shared)
+ {
+ RTLIL::Module *first_module = NULL;
+
+ for (auto &mod_it : design->modules_)
+ {
+ RTLIL::Module *module = mod_it.second;
+
+ if (!design->selected(module))
+ continue;
+
+ std::set<RTLIL::IdString> dff_wires;
+ if (flag_dff)
+ find_dff_wires(dff_wires, module);
+
+ if (first_module == NULL)
+ {
+ for (auto &it : module->wires_)
+ if (design->selected(module, it.second) && consider_wire(it.second, dff_dq_maps[module]))
+ if (!flag_dff || dff_wires.count(it.first))
+ shared_wires.insert(it.first);
+
+ if (flag_evert)
+ for (auto &it : module->cells_)
+ if (design->selected(module, it.second) && consider_cell(design, dff_cells[module], it.second))
+ shared_cells.insert(it.first);
+
+ first_module = module;
+ }
+ else
+ {
+ std::vector<RTLIL::IdString> delete_shared_wires, delete_shared_cells;
+
+ for (auto &it : shared_wires)
+ {
+ RTLIL::Wire *wire;
+
+ if (module->wires_.count(it) == 0)
+ goto delete_shared_wire;
+
+ wire = module->wires_.at(it);
+
+ if (!design->selected(module, wire))
+ goto delete_shared_wire;
+ if (!consider_wire(wire, dff_dq_maps[module]))
+ goto delete_shared_wire;
+ if (!compare_wires(first_module->wires_.at(it), wire))
+ goto delete_shared_wire;
+ if (flag_dff && !dff_wires.count(it))
+ goto delete_shared_wire;
+
+ if (0)
+ delete_shared_wire:
+ delete_shared_wires.push_back(it);
+ }
+
+ if (flag_evert)
+ for (auto &it : shared_cells)
+ {
+ RTLIL::Cell *cell;
+
+ if (module->cells_.count(it) == 0)
+ goto delete_shared_cell;
+
+ cell = module->cells_.at(it);
+
+ if (!design->selected(module, cell))
+ goto delete_shared_cell;
+ if (!consider_cell(design, dff_cells[module], cell))
+ goto delete_shared_cell;
+ if (!compare_cells(first_module->cells_.at(it), cell))
+ goto delete_shared_cell;
+
+ if (0)
+ delete_shared_cell:
+ delete_shared_cells.push_back(it);
+ }
+
+ for (auto &it : delete_shared_wires)
+ shared_wires.erase(it);
+ for (auto &it : delete_shared_cells)
+ shared_cells.erase(it);
+ }
+ }
+ }
+
+ for (auto &mod_it : design->modules_)
+ {
+ RTLIL::Module *module = mod_it.second;
+
+ if (!design->selected(module))
+ continue;
+
+ std::set<RTLIL::IdString> dff_wires;
+ if (flag_dff && !flag_shared)
+ find_dff_wires(dff_wires, module);
+
+ SigMap sigmap(module);
+
+ SigMap out_to_in_map;
+
+ for (auto &it : module->wires_)
+ {
+ if (flag_shared) {
+ if (shared_wires.count(it.first) == 0)
+ continue;
+ } else {
+ if (!design->selected(module, it.second) || !consider_wire(it.second, dff_dq_maps[module]))
+ continue;
+ if (flag_dff && !dff_wires.count(it.first))
+ continue;
+ }
+
+ if (!it.second->port_output) {
+ it.second->port_output = true;
+ log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(it.second->name));
+ }
+
+ if (flag_cut) {
+ RTLIL::Wire *in_wire = add_new_wire(module, it.second->name.str() + sep + "i", it.second->width);
+ in_wire->port_input = true;
+ out_to_in_map.add(sigmap(it.second), in_wire);
+ }
+ }
+
+ if (flag_cut)
+ {
+ for (auto &it : module->cells_) {
+ if (!ct.cell_known(it.second->type))
+ continue;
+ for (auto &conn : it.second->connections_)
+ if (ct.cell_input(it.second->type, conn.first))
+ conn.second = out_to_in_map(sigmap(conn.second));
+ }
+
+ for (auto &conn : module->connections_)
+ conn.second = out_to_in_map(sigmap(conn.second));
+ }
+
+ std::set<RTLIL::SigBit> set_q_bits;
+
+ for (auto &dq : dff_dq_maps[module])
+ {
+ if (!module->wires_.count(dq.first))
+ continue;
+
+ RTLIL::Wire *wire = module->wires_.at(dq.first);
+ std::set<RTLIL::SigBit> wire_bits_set = sigmap(wire).to_sigbit_set();
+ std::vector<RTLIL::SigBit> wire_bits_vec = sigmap(wire).to_sigbit_vector();
+
+ dff_map_info_t &info = dq.second;
+
+ RTLIL::Wire *wire_dummy_q = add_new_wire(module, NEW_ID, 0);
+
+ for (auto &cell_name : info.cells) {
+ RTLIL::Cell *cell = module->cells_.at(cell_name);
+ std::vector<RTLIL::SigBit> cell_q_bits = sigmap(cell->getPort("\\Q")).to_sigbit_vector();
+ for (auto &bit : cell_q_bits)
+ if (wire_bits_set.count(bit))
+ bit = RTLIL::SigBit(wire_dummy_q, wire_dummy_q->width++);
+ cell->setPort("\\Q", cell_q_bits);
+ }
+
+ RTLIL::Wire *wire_q = add_new_wire(module, wire->name.str() + sep + "q", wire->width);
+ wire_q->port_input = true;
+ log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire_q->name));
+
+ RTLIL::SigSig connect_q;
+ for (size_t i = 0; i < wire_bits_vec.size(); i++) {
+ if (set_q_bits.count(wire_bits_vec[i]))
+ continue;
+ connect_q.first.append(wire_bits_vec[i]);
+ connect_q.second.append(RTLIL::SigBit(wire_q, i));
+ set_q_bits.insert(wire_bits_vec[i]);
+ }
+ module->connect(connect_q);
+
+ RTLIL::Wire *wire_d = add_new_wire(module, wire->name.str() + sep + "d", wire->width);
+ wire_d->port_output = true;
+ log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire_d->name));
+ module->connect(RTLIL::SigSig(wire_d, info.sig_d));
+
+ RTLIL::Wire *wire_c = add_new_wire(module, wire->name.str() + sep + "c");
+ wire_c->port_output = true;
+ log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire_c->name));
+ if (info.clk_polarity) {
+ module->connect(RTLIL::SigSig(wire_c, info.sig_clk));
+ } else {
+ RTLIL::Cell *c = module->addCell(NEW_ID, "$not");
+ c->parameters["\\A_SIGNED"] = 0;
+ c->parameters["\\A_WIDTH"] = 1;
+ c->parameters["\\Y_WIDTH"] = 1;
+ c->setPort("\\A", info.sig_clk);
+ c->setPort("\\Y", wire_c);
+ }
+
+ if (info.sig_arst != RTLIL::State::Sm)
+ {
+ RTLIL::Wire *wire_r = add_new_wire(module, wire->name.str() + sep + "r");
+ wire_r->port_output = true;
+ log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire_r->name));
+ if (info.arst_polarity) {
+ module->connect(RTLIL::SigSig(wire_r, info.sig_arst));
+ } else {
+ RTLIL::Cell *c = module->addCell(NEW_ID, "$not");
+ c->parameters["\\A_SIGNED"] = 0;
+ c->parameters["\\A_WIDTH"] = 1;
+ c->parameters["\\Y_WIDTH"] = 1;
+ c->setPort("\\A", info.sig_arst);
+ c->setPort("\\Y", wire_r);
+ }
+
+ RTLIL::Wire *wire_v = add_new_wire(module, wire->name.str() + sep + "v", wire->width);
+ wire_v->port_output = true;
+ log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire_v->name));
+ module->connect(RTLIL::SigSig(wire_v, info.arst_value));
+ }
+ }
+
+ if (flag_evert)
+ {
+ std::vector<RTLIL::Cell*> delete_cells;
+
+ for (auto &it : module->cells_)
+ {
+ if (flag_shared) {
+ if (shared_cells.count(it.first) == 0)
+ continue;
+ } else {
+ if (!design->selected(module, it.second) || !consider_cell(design, dff_cells[module], it.second))
+ continue;
+ }
+
+ RTLIL::Cell *cell = it.second;
+
+ if (design->modules_.count(cell->type))
+ {
+ RTLIL::Module *mod = design->modules_.at(cell->type);
+
+ for (auto &it : mod->wires_)
+ {
+ RTLIL::Wire *p = it.second;
+ if (!p->port_input && !p->port_output)
+ continue;
+
+ RTLIL::Wire *w = add_new_wire(module, cell->name.str() + sep + RTLIL::unescape_id(p->name), p->width);
+ if (p->port_input)
+ w->port_output = true;
+ if (p->port_output)
+ w->port_input = true;
+
+ log("New module port: %s/%s (%s)\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(w->name), RTLIL::id2cstr(cell->type));
+
+ RTLIL::SigSpec sig;
+ if (cell->hasPort(p->name))
+ sig = cell->getPort(p->name);
+ sig.extend_u0(w->width);
+ if (w->port_input)
+ module->connect(RTLIL::SigSig(sig, w));
+ else
+ module->connect(RTLIL::SigSig(w, sig));
+ }
+ }
+ else
+ {
+ for (auto &it : cell->connections())
+ {
+ RTLIL::Wire *w = add_new_wire(module, cell->name.str() + sep + RTLIL::unescape_id(it.first), it.second.size());
+ if (ct.cell_input(cell->type, it.first))
+ w->port_output = true;
+ if (ct.cell_output(cell->type, it.first))
+ w->port_input = true;
+
+ log("New module port: %s/%s (%s)\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(w->name), RTLIL::id2cstr(cell->type));
+
+ if (w->port_input)
+ module->connect(RTLIL::SigSig(it.second, w));
+ else
+ module->connect(RTLIL::SigSig(w, it.second));
+ }
+ }
+
+ delete_cells.push_back(cell);
+ }
+
+ for (auto cell : delete_cells) {
+ log("Removing cell: %s/%s (%s)\n", log_id(module), log_id(cell), log_id(cell->type));
+ module->remove(cell);
+ }
+ }
+
+ module->fixup_ports();
+ }
+ }
+} ExposePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/freduce.cc b/passes/sat/freduce.cc
new file mode 100644
index 00000000..77263f6a
--- /dev/null
+++ b/passes/sat/freduce.cc
@@ -0,0 +1,840 @@
+/*
+ * 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/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/consteval.h"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include "kernel/satgen.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <algorithm>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+bool inv_mode;
+int verbose_level, reduce_counter, reduce_stop_at;
+typedef std::map<RTLIL::SigBit, std::pair<RTLIL::Cell*, std::set<RTLIL::SigBit>>> drivers_t;
+std::string dump_prefix;
+
+struct equiv_bit_t
+{
+ int depth;
+ bool inverted;
+ RTLIL::Cell *drv;
+ RTLIL::SigBit bit;
+
+ bool operator<(const equiv_bit_t &other) const {
+ if (depth != other.depth)
+ return depth < other.depth;
+ if (inverted != other.inverted)
+ return inverted < other.inverted;
+ if (drv != other.drv)
+ return drv < other.drv;
+ return bit < other.bit;
+ }
+};
+
+struct CountBitUsage
+{
+ SigMap &sigmap;
+ std::map<RTLIL::SigBit, int> &cache;
+
+ CountBitUsage(SigMap &sigmap, std::map<RTLIL::SigBit, int> &cache) : sigmap(sigmap), cache(cache) { }
+
+ void operator()(RTLIL::SigSpec &sig) {
+ std::vector<RTLIL::SigBit> vec = sigmap(sig).to_sigbit_vector();
+ for (auto &bit : vec)
+ cache[bit]++;
+ }
+};
+
+struct FindReducedInputs
+{
+ SigMap &sigmap;
+ drivers_t &drivers;
+
+ ezSatPtr ez;
+ std::set<RTLIL::Cell*> ez_cells;
+ SatGen satgen;
+
+ std::map<RTLIL::SigBit, int> sat_pi;
+ std::vector<int> sat_pi_uniq_bitvec;
+
+ FindReducedInputs(SigMap &sigmap, drivers_t &drivers) :
+ sigmap(sigmap), drivers(drivers), satgen(ez.get(), &sigmap)
+ {
+ satgen.model_undef = true;
+ }
+
+ int get_bits(int val)
+ {
+ int bits = 0;
+ for (int i = 8*sizeof(int); val; i = i >> 1)
+ if (val >> (i-1)) {
+ bits += i;
+ val = val >> i;
+ }
+ return bits;
+ }
+
+ void register_pi_bit(RTLIL::SigBit bit)
+ {
+ if (sat_pi.count(bit) != 0)
+ return;
+
+ satgen.setContext(&sigmap, "A");
+ int sat_a = satgen.importSigSpec(bit).front();
+ ez->assume(ez->NOT(satgen.importUndefSigSpec(bit).front()));
+
+ satgen.setContext(&sigmap, "B");
+ int sat_b = satgen.importSigSpec(bit).front();
+ ez->assume(ez->NOT(satgen.importUndefSigSpec(bit).front()));
+
+ int idx = sat_pi.size();
+ size_t idx_bits = get_bits(idx);
+
+ if (sat_pi_uniq_bitvec.size() != idx_bits) {
+ sat_pi_uniq_bitvec.push_back(ez->frozen_literal(stringf("uniq_%d", int(idx_bits)-1)));
+ for (auto &it : sat_pi)
+ ez->assume(ez->OR(ez->NOT(it.second), ez->NOT(sat_pi_uniq_bitvec.back())));
+ }
+ log_assert(sat_pi_uniq_bitvec.size() == idx_bits);
+
+ sat_pi[bit] = ez->frozen_literal(stringf("p, falsei_%s", log_signal(bit)));
+ ez->assume(ez->IFF(ez->XOR(sat_a, sat_b), sat_pi[bit]));
+
+ for (size_t i = 0; i < idx_bits; i++)
+ if ((idx & (1 << i)) == 0)
+ ez->assume(ez->OR(ez->NOT(sat_pi[bit]), ez->NOT(sat_pi_uniq_bitvec[i])));
+ else
+ ez->assume(ez->OR(ez->NOT(sat_pi[bit]), sat_pi_uniq_bitvec[i]));
+ }
+
+ void register_cone_worker(std::set<RTLIL::SigBit> &pi, std::set<RTLIL::SigBit> &sigdone, RTLIL::SigBit out)
+ {
+ if (out.wire == NULL)
+ return;
+ if (sigdone.count(out) != 0)
+ return;
+ sigdone.insert(out);
+
+ if (drivers.count(out) != 0) {
+ std::pair<RTLIL::Cell*, std::set<RTLIL::SigBit>> &drv = drivers.at(out);
+ if (ez_cells.count(drv.first) == 0) {
+ satgen.setContext(&sigmap, "A");
+ if (!satgen.importCell(drv.first))
+ log_error("Can't create SAT model for cell %s (%s)!\n", RTLIL::id2cstr(drv.first->name), RTLIL::id2cstr(drv.first->type));
+ satgen.setContext(&sigmap, "B");
+ if (!satgen.importCell(drv.first))
+ log_abort();
+ ez_cells.insert(drv.first);
+ }
+ for (auto &bit : drv.second)
+ register_cone_worker(pi, sigdone, bit);
+ } else {
+ register_pi_bit(out);
+ pi.insert(out);
+ }
+ }
+
+ void register_cone(std::vector<RTLIL::SigBit> &pi, RTLIL::SigBit out)
+ {
+ std::set<RTLIL::SigBit> pi_set, sigdone;
+ register_cone_worker(pi_set, sigdone, out);
+ pi.clear();
+ pi.insert(pi.end(), pi_set.begin(), pi_set.end());
+ }
+
+ void analyze(std::vector<RTLIL::SigBit> &reduced_inputs, RTLIL::SigBit output, int prec)
+ {
+ if (verbose_level >= 1)
+ log("[%2d%%] Analyzing input cone for signal %s:\n", prec, log_signal(output));
+
+ std::vector<RTLIL::SigBit> pi;
+ register_cone(pi, output);
+
+ if (verbose_level >= 1)
+ log(" Found %d input signals and %d cells.\n", int(pi.size()), int(ez_cells.size()));
+
+ satgen.setContext(&sigmap, "A");
+ int output_a = satgen.importSigSpec(output).front();
+ int output_undef_a = satgen.importUndefSigSpec(output).front();
+
+ satgen.setContext(&sigmap, "B");
+ int output_b = satgen.importSigSpec(output).front();
+ int output_undef_b = satgen.importUndefSigSpec(output).front();
+
+ std::set<int> unused_pi_idx;
+
+ for (size_t i = 0; i < pi.size(); i++)
+ unused_pi_idx.insert(i);
+
+ while (1)
+ {
+ std::vector<int> model_pi_idx;
+ std::vector<int> model_expr;
+ std::vector<bool> model;
+
+ for (size_t i = 0; i < pi.size(); i++)
+ if (unused_pi_idx.count(i) != 0) {
+ model_pi_idx.push_back(i);
+ model_expr.push_back(sat_pi.at(pi[i]));
+ }
+
+ if (!ez->solve(model_expr, model, ez->expression(ezSAT::OpOr, model_expr), ez->XOR(output_a, output_b), ez->NOT(output_undef_a), ez->NOT(output_undef_b)))
+ break;
+
+ int found_count = 0;
+ for (size_t i = 0; i < model_pi_idx.size(); i++)
+ if (model[i]) {
+ if (verbose_level >= 2)
+ log(" Found relevant input: %s\n", log_signal(pi[model_pi_idx[i]]));
+ unused_pi_idx.erase(model_pi_idx[i]);
+ found_count++;
+ }
+ log_assert(found_count == 1);
+ }
+
+ for (size_t i = 0; i < pi.size(); i++)
+ if (unused_pi_idx.count(i) == 0)
+ reduced_inputs.push_back(pi[i]);
+
+ if (verbose_level >= 1)
+ log(" Reduced input cone contains %d inputs.\n", int(reduced_inputs.size()));
+ }
+};
+
+struct PerformReduction
+{
+ SigMap &sigmap;
+ drivers_t &drivers;
+ std::set<std::pair<RTLIL::SigBit, RTLIL::SigBit>> &inv_pairs;
+ pool<SigBit> recursion_guard;
+
+ ezSatPtr ez;
+ SatGen satgen;
+
+ std::vector<int> sat_pi, sat_out, sat_def;
+ std::vector<RTLIL::SigBit> out_bits, pi_bits;
+ std::vector<bool> out_inverted;
+ std::vector<int> out_depth;
+ int cone_size;
+
+ int register_cone_worker(std::set<RTLIL::Cell*> &celldone, std::map<RTLIL::SigBit, int> &sigdepth, RTLIL::SigBit out)
+ {
+ if (out.wire == NULL)
+ return 0;
+ if (sigdepth.count(out) != 0)
+ return sigdepth.at(out);
+
+ if (recursion_guard.count(out)) {
+ string loop_signals;
+ for (auto loop_bit : recursion_guard)
+ loop_signals += string(" ") + log_signal(loop_bit);
+ log_error("Found logic loop:%s\n", loop_signals.c_str());
+ }
+
+ recursion_guard.insert(out);
+
+ if (drivers.count(out) != 0) {
+ std::pair<RTLIL::Cell*, std::set<RTLIL::SigBit>> &drv = drivers.at(out);
+ if (celldone.count(drv.first) == 0) {
+ if (!satgen.importCell(drv.first))
+ log_error("Can't create SAT model for cell %s (%s)!\n", RTLIL::id2cstr(drv.first->name), RTLIL::id2cstr(drv.first->type));
+ celldone.insert(drv.first);
+ }
+ int max_child_depth = 0;
+ for (auto &bit : drv.second)
+ max_child_depth = max(register_cone_worker(celldone, sigdepth, bit), max_child_depth);
+ sigdepth[out] = max_child_depth + 1;
+ } else {
+ pi_bits.push_back(out);
+ sat_pi.push_back(satgen.importSigSpec(out).front());
+ ez->assume(ez->NOT(satgen.importUndefSigSpec(out).front()));
+ sigdepth[out] = 0;
+ }
+
+ recursion_guard.erase(out);
+ return sigdepth.at(out);
+ }
+
+ PerformReduction(SigMap &sigmap, drivers_t &drivers, std::set<std::pair<RTLIL::SigBit, RTLIL::SigBit>> &inv_pairs, std::vector<RTLIL::SigBit> &bits, int cone_size) :
+ sigmap(sigmap), drivers(drivers), inv_pairs(inv_pairs), satgen(ez.get(), &sigmap), out_bits(bits), cone_size(cone_size)
+ {
+ satgen.model_undef = true;
+
+ std::set<RTLIL::Cell*> celldone;
+ std::map<RTLIL::SigBit, int> sigdepth;
+
+ for (auto &bit : bits) {
+ out_depth.push_back(register_cone_worker(celldone, sigdepth, bit));
+ sat_out.push_back(satgen.importSigSpec(bit).front());
+ sat_def.push_back(ez->NOT(satgen.importUndefSigSpec(bit).front()));
+ }
+
+ if (inv_mode && cone_size > 0) {
+ if (!ez->solve(sat_out, out_inverted, ez->expression(ezSAT::OpAnd, sat_def)))
+ log_error("Solving for initial model failed!\n");
+ for (size_t i = 0; i < sat_out.size(); i++)
+ if (out_inverted.at(i))
+ sat_out[i] = ez->NOT(sat_out[i]);
+ } else
+ out_inverted = std::vector<bool>(sat_out.size(), false);
+ }
+
+ void analyze_const(std::vector<std::vector<equiv_bit_t>> &results, int idx)
+ {
+ if (verbose_level == 1)
+ log(" Finding const value for %s.\n", log_signal(out_bits[idx]));
+
+ bool can_be_set = ez->solve(ez->AND(sat_out[idx], sat_def[idx]));
+ bool can_be_clr = ez->solve(ez->AND(ez->NOT(sat_out[idx]), sat_def[idx]));
+ log_assert(!can_be_set || !can_be_clr);
+
+ RTLIL::SigBit value(RTLIL::State::Sx);
+ if (can_be_set)
+ value = RTLIL::State::S1;
+ if (can_be_clr)
+ value = RTLIL::State::S0;
+ if (verbose_level == 1)
+ log(" Constant value for this signal: %s\n", log_signal(value));
+
+ int result_idx = -1;
+ for (size_t i = 0; i < results.size(); i++) {
+ if (results[i].front().bit == value) {
+ result_idx = i;
+ break;
+ }
+ }
+
+ if (result_idx == -1) {
+ result_idx = results.size();
+ results.push_back(std::vector<equiv_bit_t>());
+ equiv_bit_t bit;
+ bit.depth = 0;
+ bit.inverted = false;
+ bit.drv = NULL;
+ bit.bit = value;
+ results.back().push_back(bit);
+ }
+
+ equiv_bit_t bit;
+ bit.depth = 1;
+ bit.inverted = false;
+ bit.drv = drivers.count(out_bits[idx]) ? drivers.at(out_bits[idx]).first : NULL;
+ bit.bit = out_bits[idx];
+ results[result_idx].push_back(bit);
+ }
+
+ void analyze(std::vector<std::set<int>> &results, std::map<int, int> &results_map, std::vector<int> &bucket, std::string indent1, std::string indent2)
+ {
+ std::string indent = indent1 + indent2;
+ const char *indt = indent.c_str();
+
+ if (bucket.size() <= 1)
+ return;
+
+ if (verbose_level == 1)
+ log("%s Trying to shatter bucket with %d signals.\n", indt, int(bucket.size()));
+
+ if (verbose_level > 1) {
+ std::vector<RTLIL::SigBit> bucket_sigbits;
+ for (int idx : bucket)
+ bucket_sigbits.push_back(out_bits[idx]);
+ log("%s Trying to shatter bucket with %d signals: %s\n", indt, int(bucket.size()), log_signal(bucket_sigbits));
+ }
+
+ std::vector<int> sat_set_list, sat_clr_list;
+ for (int idx : bucket) {
+ sat_set_list.push_back(ez->AND(sat_out[idx], sat_def[idx]));
+ sat_clr_list.push_back(ez->AND(ez->NOT(sat_out[idx]), sat_def[idx]));
+ }
+
+ std::vector<int> modelVars = sat_out;
+ std::vector<bool> model;
+
+ modelVars.insert(modelVars.end(), sat_def.begin(), sat_def.end());
+ if (verbose_level >= 2)
+ modelVars.insert(modelVars.end(), sat_pi.begin(), sat_pi.end());
+
+ if (ez->solve(modelVars, model, ez->expression(ezSAT::OpOr, sat_set_list), ez->expression(ezSAT::OpOr, sat_clr_list)))
+ {
+ int iter_count = 1;
+
+ while (1)
+ {
+ sat_set_list.clear();
+ sat_clr_list.clear();
+
+ std::vector<int> sat_def_list;
+
+ for (int idx : bucket)
+ if (!model[sat_out.size() + idx]) {
+ sat_set_list.push_back(ez->AND(sat_out[idx], sat_def[idx]));
+ sat_clr_list.push_back(ez->AND(ez->NOT(sat_out[idx]), sat_def[idx]));
+ } else {
+ sat_def_list.push_back(sat_def[idx]);
+ }
+
+ if (!ez->solve(modelVars, model, ez->expression(ezSAT::OpOr, sat_set_list), ez->expression(ezSAT::OpOr, sat_clr_list), ez->expression(ezSAT::OpAnd, sat_def_list)))
+ break;
+ iter_count++;
+ }
+
+ if (verbose_level >= 1) {
+ int count_set = 0, count_clr = 0, count_undef = 0;
+ for (int idx : bucket)
+ if (!model[sat_out.size() + idx])
+ count_undef++;
+ else if (model[idx])
+ count_set++;
+ else
+ count_clr++;
+ log("%s After %d iterations: %d set vs. %d clr vs %d undef\n", indt, iter_count, count_set, count_clr, count_undef);
+ }
+
+ if (verbose_level >= 2) {
+ for (size_t i = 0; i < pi_bits.size(); i++)
+ log("%s -> PI %c == %s\n", indt, model[2*sat_out.size() + i] ? '1' : '0', log_signal(pi_bits[i]));
+ for (int idx : bucket)
+ log("%s -> OUT %c == %s%s\n", indt, model[sat_out.size() + idx] ? model[idx] ? '1' : '0' : 'x',
+ out_inverted.at(idx) ? "~" : "", log_signal(out_bits[idx]));
+ }
+
+ std::vector<int> buckets_a;
+ std::vector<int> buckets_b;
+
+ for (int idx : bucket) {
+ if (!model[sat_out.size() + idx] || model[idx])
+ buckets_a.push_back(idx);
+ if (!model[sat_out.size() + idx] || !model[idx])
+ buckets_b.push_back(idx);
+ }
+ analyze(results, results_map, buckets_a, indent1 + ".", indent2 + " ");
+ analyze(results, results_map, buckets_b, indent1 + "x", indent2 + " ");
+ }
+ else
+ {
+ std::vector<int> undef_slaves;
+
+ for (int idx : bucket) {
+ std::vector<int> sat_def_list;
+ for (int idx2 : bucket)
+ if (idx != idx2)
+ sat_def_list.push_back(sat_def[idx2]);
+ if (ez->solve(ez->NOT(sat_def[idx]), ez->expression(ezSAT::OpOr, sat_def_list)))
+ undef_slaves.push_back(idx);
+ }
+
+ if (undef_slaves.size() == bucket.size()) {
+ if (verbose_level >= 1)
+ log("%s Complex undef overlap. None of the signals covers the others.\n", indt);
+ // FIXME: We could try to further shatter a group with complex undef overlaps
+ return;
+ }
+
+ for (int idx : undef_slaves)
+ out_depth[idx] = std::numeric_limits<int>::max();
+
+ if (verbose_level >= 1) {
+ log("%s Found %d equivalent signals:", indt, int(bucket.size()));
+ for (int idx : bucket)
+ log("%s%s%s", idx == bucket.front() ? " " : ", ", out_inverted[idx] ? "~" : "", log_signal(out_bits[idx]));
+ log("\n");
+ }
+
+ int result_idx = -1;
+ for (int idx : bucket) {
+ if (results_map.count(idx) == 0)
+ continue;
+ if (result_idx == -1) {
+ result_idx = results_map.at(idx);
+ continue;
+ }
+ int result_idx2 = results_map.at(idx);
+ results[result_idx].insert(results[result_idx2].begin(), results[result_idx2].end());
+ for (int idx2 : results[result_idx2])
+ results_map[idx2] = result_idx;
+ results[result_idx2].clear();
+ }
+
+ if (result_idx == -1) {
+ result_idx = results.size();
+ results.push_back(std::set<int>());
+ }
+
+ results[result_idx].insert(bucket.begin(), bucket.end());
+ }
+ }
+
+ void analyze(std::vector<std::vector<equiv_bit_t>> &results, int perc)
+ {
+ std::vector<int> bucket;
+ for (size_t i = 0; i < sat_out.size(); i++)
+ bucket.push_back(i);
+
+ std::vector<std::set<int>> results_buf;
+ std::map<int, int> results_map;
+ analyze(results_buf, results_map, bucket, stringf("[%2d%%] %d ", perc, cone_size), "");
+
+ for (auto &r : results_buf)
+ {
+ if (r.size() <= 1)
+ continue;
+
+ if (verbose_level >= 1) {
+ std::vector<RTLIL::SigBit> r_sigbits;
+ for (int idx : r)
+ r_sigbits.push_back(out_bits[idx]);
+ log(" Found group of %d equivalent signals: %s\n", int(r.size()), log_signal(r_sigbits));
+ }
+
+ std::vector<int> undef_slaves;
+
+ for (int idx : r) {
+ std::vector<int> sat_def_list;
+ for (int idx2 : r)
+ if (idx != idx2)
+ sat_def_list.push_back(sat_def[idx2]);
+ if (ez->solve(ez->NOT(sat_def[idx]), ez->expression(ezSAT::OpOr, sat_def_list)))
+ undef_slaves.push_back(idx);
+ }
+
+ if (undef_slaves.size() == bucket.size()) {
+ if (verbose_level >= 1)
+ log(" Complex undef overlap. None of the signals covers the others.\n");
+ // FIXME: We could try to further shatter a group with complex undef overlaps
+ return;
+ }
+
+ for (int idx : undef_slaves)
+ out_depth[idx] = std::numeric_limits<int>::max();
+
+ std::vector<equiv_bit_t> result;
+
+ for (int idx : r) {
+ equiv_bit_t bit;
+ bit.depth = out_depth[idx];
+ bit.inverted = out_inverted[idx];
+ bit.drv = drivers.count(out_bits[idx]) ? drivers.at(out_bits[idx]).first : NULL;
+ bit.bit = out_bits[idx];
+ result.push_back(bit);
+ }
+
+ std::sort(result.begin(), result.end());
+
+ if (result.front().inverted)
+ for (auto &bit : result)
+ bit.inverted = !bit.inverted;
+
+ for (size_t i = 1; i < result.size(); i++) {
+ std::pair<RTLIL::SigBit, RTLIL::SigBit> p(result[0].bit, result[i].bit);
+ if (inv_pairs.count(p) != 0)
+ result.erase(result.begin() + i);
+ }
+
+ if (result.size() > 1)
+ results.push_back(result);
+ }
+ }
+};
+
+struct FreduceWorker
+{
+ RTLIL::Design *design;
+ RTLIL::Module *module;
+
+ SigMap sigmap;
+ drivers_t drivers;
+ std::set<std::pair<RTLIL::SigBit, RTLIL::SigBit>> inv_pairs;
+
+ FreduceWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module), sigmap(module)
+ {
+ }
+
+ bool find_bit_in_cone(std::set<RTLIL::Cell*> &celldone, RTLIL::SigBit needle, RTLIL::SigBit haystack)
+ {
+ if (needle == haystack)
+ return true;
+ if (haystack.wire == NULL || needle.wire == NULL || drivers.count(haystack) == 0)
+ return false;
+
+ std::pair<RTLIL::Cell*, std::set<RTLIL::SigBit>> &drv = drivers.at(haystack);
+
+ if (celldone.count(drv.first))
+ return false;
+ celldone.insert(drv.first);
+
+ for (auto &bit : drv.second)
+ if (find_bit_in_cone(celldone, needle, bit))
+ return true;
+ return false;
+ }
+
+ bool find_bit_in_cone(RTLIL::SigBit needle, RTLIL::SigBit haystack)
+ {
+ std::set<RTLIL::Cell*> celldone;
+ return find_bit_in_cone(celldone, needle, haystack);
+ }
+
+ void dump()
+ {
+ std::string filename = stringf("%s_%s_%05d.il", dump_prefix.c_str(), RTLIL::id2cstr(module->name), reduce_counter);
+ log("%s Writing dump file `%s'.\n", reduce_counter ? " " : "", filename.c_str());
+ Pass::call(design, stringf("dump -outfile %s %s", filename.c_str(), design->selected_active_module.empty() ? module->name.c_str() : ""));
+ }
+
+ int run()
+ {
+ log("Running functional reduction on module %s:\n", RTLIL::id2cstr(module->name));
+
+ CellTypes ct;
+ ct.setup_internals();
+ ct.setup_stdcells();
+
+ int bits_full_total = 0;
+ std::vector<std::set<RTLIL::SigBit>> batches;
+ for (auto &it : module->wires_)
+ if (it.second->port_input) {
+ batches.push_back(sigmap(it.second).to_sigbit_set());
+ bits_full_total += it.second->width;
+ }
+ for (auto &it : module->cells_) {
+ if (ct.cell_known(it.second->type)) {
+ std::set<RTLIL::SigBit> inputs, outputs;
+ for (auto &port : it.second->connections()) {
+ std::vector<RTLIL::SigBit> bits = sigmap(port.second).to_sigbit_vector();
+ if (ct.cell_output(it.second->type, port.first))
+ outputs.insert(bits.begin(), bits.end());
+ else
+ inputs.insert(bits.begin(), bits.end());
+ }
+ std::pair<RTLIL::Cell*, std::set<RTLIL::SigBit>> drv(it.second, inputs);
+ for (auto &bit : outputs)
+ drivers[bit] = drv;
+ batches.push_back(outputs);
+ bits_full_total += outputs.size();
+ }
+ if (inv_mode && it.second->type == "$_NOT_")
+ inv_pairs.insert(std::pair<RTLIL::SigBit, RTLIL::SigBit>(sigmap(it.second->getPort("\\A")), sigmap(it.second->getPort("\\Y"))));
+ }
+
+ int bits_count = 0;
+ int bits_full_count = 0;
+ std::map<std::vector<RTLIL::SigBit>, std::vector<RTLIL::SigBit>> buckets;
+ for (auto &batch : batches)
+ {
+ for (auto &bit : batch)
+ if (bit.wire != NULL && design->selected(module, bit.wire))
+ goto found_selected_wire;
+ bits_full_count += batch.size();
+ continue;
+
+ found_selected_wire:
+ log(" Finding reduced input cone for signal batch %s%c\n",
+ log_signal(batch), verbose_level ? ':' : '.');
+
+ FindReducedInputs infinder(sigmap, drivers);
+ for (auto &bit : batch) {
+ std::vector<RTLIL::SigBit> inputs;
+ infinder.analyze(inputs, bit, 100 * bits_full_count / bits_full_total);
+ buckets[inputs].push_back(bit);
+ bits_full_count++;
+ bits_count++;
+ }
+ }
+ log(" Sorted %d signal bits into %d buckets.\n", bits_count, int(buckets.size()));
+
+ int bucket_count = 0;
+ std::vector<std::vector<equiv_bit_t>> equiv;
+ for (auto &bucket : buckets)
+ {
+ bucket_count++;
+
+ if (bucket.second.size() == 1)
+ continue;
+
+ if (bucket.first.size() == 0) {
+ log(" Finding const values for bucket %s%c\n", log_signal(bucket.second), verbose_level ? ':' : '.');
+ PerformReduction worker(sigmap, drivers, inv_pairs, bucket.second, bucket.first.size());
+ for (size_t idx = 0; idx < bucket.second.size(); idx++)
+ worker.analyze_const(equiv, idx);
+ } else {
+ log(" Trying to shatter bucket %s%c\n", log_signal(bucket.second), verbose_level ? ':' : '.');
+ PerformReduction worker(sigmap, drivers, inv_pairs, bucket.second, bucket.first.size());
+ worker.analyze(equiv, 100 * bucket_count / (buckets.size() + 1));
+ }
+ }
+
+ std::map<RTLIL::SigBit, int> bitusage;
+ module->rewrite_sigspecs(CountBitUsage(sigmap, bitusage));
+
+ if (!dump_prefix.empty())
+ dump();
+
+ log(" Rewiring %d equivalent groups:\n", int(equiv.size()));
+ int rewired_sigbits = 0;
+ for (auto &grp : equiv)
+ {
+ log(" [%05d] Using as master for group: %s\n", ++reduce_counter, log_signal(grp.front().bit));
+
+ RTLIL::SigSpec inv_sig;
+ for (size_t i = 1; i < grp.size(); i++)
+ {
+ if (!design->selected(module, grp[i].bit.wire)) {
+ log(" Skipping not-selected slave: %s\n", log_signal(grp[i].bit));
+ continue;
+ }
+
+ if (grp[i].bit.wire->port_id == 0 && bitusage[grp[i].bit] <= 1) {
+ log(" Skipping unused slave: %s\n", log_signal(grp[i].bit));
+ continue;
+ }
+
+ if (find_bit_in_cone(grp[i].bit, grp.front().bit)) {
+ log(" Skipping dependency of master: %s\n", log_signal(grp[i].bit));
+ continue;
+ }
+
+ log(" Connect slave%s: %s\n", grp[i].inverted ? " using inverter" : "", log_signal(grp[i].bit));
+
+ RTLIL::Cell *drv = drivers.at(grp[i].bit).first;
+ RTLIL::Wire *dummy_wire = module->addWire(NEW_ID);
+ for (auto &port : drv->connections_)
+ if (ct.cell_output(drv->type, port.first))
+ sigmap(port.second).replace(grp[i].bit, dummy_wire, &port.second);
+
+ if (grp[i].inverted)
+ {
+ if (inv_sig.size() == 0)
+ {
+ inv_sig = module->addWire(NEW_ID);
+
+ RTLIL::Cell *inv_cell = module->addCell(NEW_ID, "$_NOT_");
+ inv_cell->setPort("\\A", grp[0].bit);
+ inv_cell->setPort("\\Y", inv_sig);
+ }
+
+ module->connect(RTLIL::SigSig(grp[i].bit, inv_sig));
+ }
+ else
+ module->connect(RTLIL::SigSig(grp[i].bit, grp[0].bit));
+
+ rewired_sigbits++;
+ }
+
+ if (!dump_prefix.empty())
+ dump();
+
+ if (reduce_counter == reduce_stop_at) {
+ log(" Reached limit passed using -stop option. Skipping all further reductions.\n");
+ break;
+ }
+ }
+
+ log(" Rewired a total of %d signal bits in module %s.\n", rewired_sigbits, RTLIL::id2cstr(module->name));
+ return rewired_sigbits;
+ }
+};
+
+struct FreducePass : public Pass {
+ FreducePass() : Pass("freduce", "perform functional reduction") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" freduce [options] [selection]\n");
+ log("\n");
+ log("This pass performs functional reduction in the circuit. I.e. if two nodes are\n");
+ log("equivalent, they are merged to one node and one of the redundant drivers is\n");
+ log("disconnected. A subsequent call to 'clean' will remove the redundant drivers.\n");
+ log("\n");
+ log(" -v, -vv\n");
+ log(" enable verbose or very verbose output\n");
+ log("\n");
+ log(" -inv\n");
+ log(" enable explicit handling of inverted signals\n");
+ log("\n");
+ log(" -stop <n>\n");
+ log(" stop after <n> reduction operations. this is mostly used for\n");
+ log(" debugging the freduce command itself.\n");
+ log("\n");
+ log(" -dump <prefix>\n");
+ log(" dump the design to <prefix>_<module>_<num>.il after each reduction\n");
+ log(" operation. this is mostly used for debugging the freduce command.\n");
+ log("\n");
+ log("This pass is undef-aware, i.e. it considers don't-care values for detecting\n");
+ log("equivalent nodes.\n");
+ log("\n");
+ log("All selected wires are considered for rewiring. The selected cells cover the\n");
+ log("circuit that is analyzed.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ reduce_counter = 0;
+ reduce_stop_at = 0;
+ verbose_level = 0;
+ inv_mode = false;
+ dump_prefix = std::string();
+
+ log_header(design, "Executing FREDUCE pass (perform functional reduction).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-v") {
+ verbose_level = 1;
+ continue;
+ }
+ if (args[argidx] == "-vv") {
+ verbose_level = 2;
+ continue;
+ }
+ if (args[argidx] == "-inv") {
+ inv_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-stop" && argidx+1 < args.size()) {
+ reduce_stop_at = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-dump" && argidx+1 < args.size()) {
+ dump_prefix = args[++argidx];
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ int bitcount = 0;
+ for (auto &mod_it : design->modules_) {
+ RTLIL::Module *module = mod_it.second;
+ if (design->selected(module))
+ bitcount += FreduceWorker(design, module).run();
+ }
+
+ log("Rewired a total of %d signal bits.\n", bitcount);
+ }
+} FreducePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/miter.cc b/passes/sat/miter.cc
new file mode 100644
index 00000000..9e150b60
--- /dev/null
+++ b/passes/sat/miter.cc
@@ -0,0 +1,421 @@
+/*
+ * 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/register.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL::Design *design)
+{
+ bool flag_ignore_gold_x = false;
+ bool flag_make_outputs = false;
+ bool flag_make_outcmp = false;
+ bool flag_make_assert = false;
+ bool flag_flatten = false;
+
+ log_header(design, "Executing MITER pass (creating miter circuit).\n");
+
+ size_t argidx;
+ for (argidx = 2; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-ignore_gold_x") {
+ flag_ignore_gold_x = true;
+ continue;
+ }
+ if (args[argidx] == "-make_outputs") {
+ flag_make_outputs = true;
+ continue;
+ }
+ if (args[argidx] == "-make_outcmp") {
+ flag_make_outcmp = true;
+ continue;
+ }
+ if (args[argidx] == "-make_assert") {
+ flag_make_assert = true;
+ continue;
+ }
+ if (args[argidx] == "-flatten") {
+ flag_flatten = true;
+ continue;
+ }
+ break;
+ }
+ if (argidx+3 != args.size() || args[argidx].substr(0, 1) == "-")
+ that->cmd_error(args, argidx, "command argument error");
+
+ RTLIL::IdString gold_name = RTLIL::escape_id(args[argidx++]);
+ RTLIL::IdString gate_name = RTLIL::escape_id(args[argidx++]);
+ RTLIL::IdString miter_name = RTLIL::escape_id(args[argidx++]);
+
+ if (design->modules_.count(gold_name) == 0)
+ log_cmd_error("Can't find gold module %s!\n", gold_name.c_str());
+ if (design->modules_.count(gate_name) == 0)
+ log_cmd_error("Can't find gate module %s!\n", gate_name.c_str());
+ if (design->modules_.count(miter_name) != 0)
+ log_cmd_error("There is already a module %s!\n", miter_name.c_str());
+
+ RTLIL::Module *gold_module = design->modules_.at(gold_name);
+ RTLIL::Module *gate_module = design->modules_.at(gate_name);
+
+ for (auto &it : gold_module->wires_) {
+ RTLIL::Wire *w1 = it.second, *w2;
+ if (w1->port_id == 0)
+ continue;
+ if (gate_module->wires_.count(it.second->name) == 0)
+ goto match_gold_port_error;
+ w2 = gate_module->wires_.at(it.second->name);
+ if (w1->port_input != w2->port_input)
+ goto match_gold_port_error;
+ if (w1->port_output != w2->port_output)
+ goto match_gold_port_error;
+ if (w1->width != w2->width)
+ goto match_gold_port_error;
+ continue;
+ match_gold_port_error:
+ log_cmd_error("No matching port in gate module was found for %s!\n", it.second->name.c_str());
+ }
+
+ for (auto &it : gate_module->wires_) {
+ RTLIL::Wire *w1 = it.second, *w2;
+ if (w1->port_id == 0)
+ continue;
+ if (gold_module->wires_.count(it.second->name) == 0)
+ goto match_gate_port_error;
+ w2 = gold_module->wires_.at(it.second->name);
+ if (w1->port_input != w2->port_input)
+ goto match_gate_port_error;
+ if (w1->port_output != w2->port_output)
+ goto match_gate_port_error;
+ if (w1->width != w2->width)
+ goto match_gate_port_error;
+ continue;
+ match_gate_port_error:
+ log_cmd_error("No matching port in gold module was found for %s!\n", it.second->name.c_str());
+ }
+
+ log("Creating miter cell \"%s\" with gold cell \"%s\" and gate cell \"%s\".\n", RTLIL::id2cstr(miter_name), RTLIL::id2cstr(gold_name), RTLIL::id2cstr(gate_name));
+
+ RTLIL::Module *miter_module = new RTLIL::Module;
+ miter_module->name = miter_name;
+ design->add(miter_module);
+
+ RTLIL::Cell *gold_cell = miter_module->addCell("\\gold", gold_name);
+ RTLIL::Cell *gate_cell = miter_module->addCell("\\gate", gate_name);
+
+ RTLIL::SigSpec all_conditions;
+
+ for (auto &it : gold_module->wires_)
+ {
+ RTLIL::Wire *w1 = it.second;
+
+ if (w1->port_input)
+ {
+ RTLIL::Wire *w2 = miter_module->addWire("\\in_" + RTLIL::unescape_id(w1->name), w1->width);
+ w2->port_input = true;
+
+ gold_cell->setPort(w1->name, w2);
+ gate_cell->setPort(w1->name, w2);
+ }
+
+ if (w1->port_output)
+ {
+ RTLIL::Wire *w2_gold = miter_module->addWire("\\gold_" + RTLIL::unescape_id(w1->name), w1->width);
+ w2_gold->port_output = flag_make_outputs;
+
+ RTLIL::Wire *w2_gate = miter_module->addWire("\\gate_" + RTLIL::unescape_id(w1->name), w1->width);
+ w2_gate->port_output = flag_make_outputs;
+
+ gold_cell->setPort(w1->name, w2_gold);
+ gate_cell->setPort(w1->name, w2_gate);
+
+ RTLIL::SigSpec this_condition;
+
+ if (flag_ignore_gold_x)
+ {
+ RTLIL::SigSpec gold_x = miter_module->addWire(NEW_ID, w2_gold->width);
+ for (int i = 0; i < w2_gold->width; i++) {
+ RTLIL::Cell *eqx_cell = miter_module->addCell(NEW_ID, "$eqx");
+ eqx_cell->parameters["\\A_WIDTH"] = 1;
+ eqx_cell->parameters["\\B_WIDTH"] = 1;
+ eqx_cell->parameters["\\Y_WIDTH"] = 1;
+ eqx_cell->parameters["\\A_SIGNED"] = 0;
+ eqx_cell->parameters["\\B_SIGNED"] = 0;
+ eqx_cell->setPort("\\A", RTLIL::SigSpec(w2_gold, i));
+ eqx_cell->setPort("\\B", RTLIL::State::Sx);
+ eqx_cell->setPort("\\Y", gold_x.extract(i, 1));
+ }
+
+ RTLIL::SigSpec gold_masked = miter_module->addWire(NEW_ID, w2_gold->width);
+ RTLIL::SigSpec gate_masked = miter_module->addWire(NEW_ID, w2_gate->width);
+
+ RTLIL::Cell *or_gold_cell = miter_module->addCell(NEW_ID, "$or");
+ or_gold_cell->parameters["\\A_WIDTH"] = w2_gold->width;
+ or_gold_cell->parameters["\\B_WIDTH"] = w2_gold->width;
+ or_gold_cell->parameters["\\Y_WIDTH"] = w2_gold->width;
+ or_gold_cell->parameters["\\A_SIGNED"] = 0;
+ or_gold_cell->parameters["\\B_SIGNED"] = 0;
+ or_gold_cell->setPort("\\A", w2_gold);
+ or_gold_cell->setPort("\\B", gold_x);
+ or_gold_cell->setPort("\\Y", gold_masked);
+
+ RTLIL::Cell *or_gate_cell = miter_module->addCell(NEW_ID, "$or");
+ or_gate_cell->parameters["\\A_WIDTH"] = w2_gate->width;
+ or_gate_cell->parameters["\\B_WIDTH"] = w2_gate->width;
+ or_gate_cell->parameters["\\Y_WIDTH"] = w2_gate->width;
+ or_gate_cell->parameters["\\A_SIGNED"] = 0;
+ or_gate_cell->parameters["\\B_SIGNED"] = 0;
+ or_gate_cell->setPort("\\A", w2_gate);
+ or_gate_cell->setPort("\\B", gold_x);
+ or_gate_cell->setPort("\\Y", gate_masked);
+
+ RTLIL::Cell *eq_cell = miter_module->addCell(NEW_ID, "$eqx");
+ eq_cell->parameters["\\A_WIDTH"] = w2_gold->width;
+ eq_cell->parameters["\\B_WIDTH"] = w2_gate->width;
+ eq_cell->parameters["\\Y_WIDTH"] = 1;
+ eq_cell->parameters["\\A_SIGNED"] = 0;
+ eq_cell->parameters["\\B_SIGNED"] = 0;
+ eq_cell->setPort("\\A", gold_masked);
+ eq_cell->setPort("\\B", gate_masked);
+ eq_cell->setPort("\\Y", miter_module->addWire(NEW_ID));
+ this_condition = eq_cell->getPort("\\Y");
+ }
+ else
+ {
+ RTLIL::Cell *eq_cell = miter_module->addCell(NEW_ID, "$eqx");
+ eq_cell->parameters["\\A_WIDTH"] = w2_gold->width;
+ eq_cell->parameters["\\B_WIDTH"] = w2_gate->width;
+ eq_cell->parameters["\\Y_WIDTH"] = 1;
+ eq_cell->parameters["\\A_SIGNED"] = 0;
+ eq_cell->parameters["\\B_SIGNED"] = 0;
+ eq_cell->setPort("\\A", w2_gold);
+ eq_cell->setPort("\\B", w2_gate);
+ eq_cell->setPort("\\Y", miter_module->addWire(NEW_ID));
+ this_condition = eq_cell->getPort("\\Y");
+ }
+
+ if (flag_make_outcmp)
+ {
+ RTLIL::Wire *w_cmp = miter_module->addWire("\\cmp_" + RTLIL::unescape_id(w1->name));
+ w_cmp->port_output = true;
+ miter_module->connect(RTLIL::SigSig(w_cmp, this_condition));
+ }
+
+ all_conditions.append(this_condition);
+ }
+ }
+
+ if (all_conditions.size() != 1) {
+ RTLIL::Cell *reduce_cell = miter_module->addCell(NEW_ID, "$reduce_and");
+ reduce_cell->parameters["\\A_WIDTH"] = all_conditions.size();
+ reduce_cell->parameters["\\Y_WIDTH"] = 1;
+ reduce_cell->parameters["\\A_SIGNED"] = 0;
+ reduce_cell->setPort("\\A", all_conditions);
+ reduce_cell->setPort("\\Y", miter_module->addWire(NEW_ID));
+ all_conditions = reduce_cell->getPort("\\Y");
+ }
+
+ if (flag_make_assert) {
+ RTLIL::Cell *assert_cell = miter_module->addCell(NEW_ID, "$assert");
+ assert_cell->setPort("\\A", all_conditions);
+ assert_cell->setPort("\\EN", RTLIL::SigSpec(1, 1));
+ }
+
+ RTLIL::Wire *w_trigger = miter_module->addWire("\\trigger");
+ w_trigger->port_output = true;
+
+ RTLIL::Cell *not_cell = miter_module->addCell(NEW_ID, "$not");
+ not_cell->parameters["\\A_WIDTH"] = all_conditions.size();
+ not_cell->parameters["\\A_WIDTH"] = all_conditions.size();
+ not_cell->parameters["\\Y_WIDTH"] = w_trigger->width;
+ not_cell->parameters["\\A_SIGNED"] = 0;
+ not_cell->setPort("\\A", all_conditions);
+ not_cell->setPort("\\Y", w_trigger);
+
+ miter_module->fixup_ports();
+
+ if (flag_flatten) {
+ log_push();
+ Pass::call_on_module(design, miter_module, "flatten; opt_expr -keepdc -undriven;;");
+ log_pop();
+ }
+}
+
+void create_miter_assert(struct Pass *that, std::vector<std::string> args, RTLIL::Design *design)
+{
+ bool flag_make_outputs = false;
+ bool flag_flatten = false;
+
+ log_header(design, "Executing MITER pass (creating miter circuit).\n");
+
+ size_t argidx;
+ for (argidx = 2; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-make_outputs") {
+ flag_make_outputs = true;
+ continue;
+ }
+ if (args[argidx] == "-flatten") {
+ flag_flatten = true;
+ continue;
+ }
+ break;
+ }
+ if ((argidx+1 != args.size() && argidx+2 != args.size()) || args[argidx].substr(0, 1) == "-")
+ that->cmd_error(args, argidx, "command argument error");
+
+ IdString module_name = RTLIL::escape_id(args[argidx++]);
+ IdString miter_name = argidx < args.size() ? RTLIL::escape_id(args[argidx++]) : "";
+
+ if (design->modules_.count(module_name) == 0)
+ log_cmd_error("Can't find module %s!\n", module_name.c_str());
+ if (!miter_name.empty() && design->modules_.count(miter_name) != 0)
+ log_cmd_error("There is already a module %s!\n", miter_name.c_str());
+
+ Module *module = design->module(module_name);
+
+ if (!miter_name.empty()) {
+ module = module->clone();
+ module->name = miter_name;
+ design->add(module);
+ }
+
+ if (!flag_make_outputs)
+ for (auto wire : module->wires())
+ wire->port_output = false;
+
+ Wire *trigger = module->addWire("\\trigger");
+ trigger->port_output = true;
+ module->fixup_ports();
+
+ if (flag_flatten) {
+ log_push();
+ Pass::call_on_module(design, module, "flatten;;");
+ log_pop();
+ }
+
+ SigSpec assert_signals, assume_signals;
+ vector<Cell*> cell_list = module->cells();
+ for (auto cell : cell_list)
+ {
+ if (!cell->type.in("$assert", "$assume"))
+ continue;
+
+ SigBit is_active = module->Nex(NEW_ID, cell->getPort("\\A"), State::S1);
+ SigBit is_enabled = module->Eqx(NEW_ID, cell->getPort("\\EN"), State::S1);
+
+ if (cell->type == "$assert") {
+ assert_signals.append(module->And(NEW_ID, is_active, is_enabled));
+ } else {
+ assume_signals.append(module->And(NEW_ID, is_active, is_enabled));
+ }
+
+ module->remove(cell);
+ }
+
+ if (assume_signals.empty())
+ {
+ module->addReduceOr(NEW_ID, assert_signals, trigger);
+ }
+ else
+ {
+ Wire *assume_q = module->addWire(NEW_ID);
+ assume_q->attributes["\\init"] = State::S0;
+ assume_signals.append(assume_q);
+
+ SigSpec assume_nok = module->ReduceOr(NEW_ID, assume_signals);
+ SigSpec assume_ok = module->Not(NEW_ID, assume_nok);
+ module->addFf(NEW_ID, assume_nok, assume_q);
+
+ SigSpec assert_fail = module->ReduceOr(NEW_ID, assert_signals);
+ module->addAnd(NEW_ID, assert_fail, assume_ok, trigger);
+ }
+
+ if (flag_flatten) {
+ log_push();
+ Pass::call_on_module(design, module, "opt_expr -keepdc -undriven;;");
+ log_pop();
+ }
+}
+
+struct MiterPass : public Pass {
+ MiterPass() : Pass("miter", "automatically create a miter circuit") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" miter -equiv [options] gold_name gate_name miter_name\n");
+ log("\n");
+ log("Creates a miter circuit for equivalence checking. The gold- and gate- modules\n");
+ log("must have the same interfaces. The miter circuit will have all inputs of the\n");
+ log("two source modules, prefixed with 'in_'. The miter circuit has a 'trigger'\n");
+ log("output that goes high if an output mismatch between the two source modules is\n");
+ log("detected.\n");
+ log("\n");
+ log(" -ignore_gold_x\n");
+ log(" a undef (x) bit in the gold module output will match any value in\n");
+ log(" the gate module output.\n");
+ log("\n");
+ log(" -make_outputs\n");
+ log(" also route the gold- and gate-outputs to 'gold_*' and 'gate_*' outputs\n");
+ log(" on the miter circuit.\n");
+ log("\n");
+ log(" -make_outcmp\n");
+ log(" also create a cmp_* output for each gold/gate output pair.\n");
+ log("\n");
+ log(" -make_assert\n");
+ log(" also create an 'assert' cell that checks if trigger is always low.\n");
+ log("\n");
+ log(" -flatten\n");
+ log(" call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.\n");
+ log("\n");
+ log("\n");
+ log(" miter -assert [options] module [miter_name]\n");
+ log("\n");
+ log("Creates a miter circuit for property checking. All input ports are kept,\n");
+ log("output ports are discarded. An additional output 'trigger' is created that\n");
+ log("goes high when an assert is violated. Without a miter_name, the existing\n");
+ log("module is modified.\n");
+ log("\n");
+ log(" -make_outputs\n");
+ log(" keep module output ports.\n");
+ log("\n");
+ log(" -flatten\n");
+ log(" call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ if (args.size() > 1 && args[1] == "-equiv") {
+ create_miter_equiv(this, args, design);
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-assert") {
+ create_miter_assert(this, args, design);
+ return;
+ }
+
+ log_cmd_error("Missing mode parameter!\n");
+ }
+} MiterPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc
new file mode 100644
index 00000000..a6ac7afd
--- /dev/null
+++ b/passes/sat/sat.cc
@@ -0,0 +1,1713 @@
+/*
+ * 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.
+ *
+ */
+
+// [[CITE]] Temporal Induction by Incremental SAT Solving
+// Niklas Een and Niklas Sörensson (2003)
+// http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.8161
+
+#include "kernel/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/consteval.h"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include "kernel/satgen.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <algorithm>
+#include <errno.h>
+#include <string.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct SatHelper
+{
+ RTLIL::Design *design;
+ RTLIL::Module *module;
+
+ SigMap sigmap;
+ CellTypes ct;
+
+ ezSatPtr ez;
+ SatGen satgen;
+
+ // additional constraints
+ std::vector<std::pair<std::string, std::string>> sets, prove, prove_x, sets_init;
+ std::map<int, std::vector<std::pair<std::string, std::string>>> sets_at;
+ std::map<int, std::vector<std::string>> unsets_at;
+ bool prove_asserts, set_assumes;
+
+ // undef constraints
+ bool enable_undef, set_init_def, set_init_undef, set_init_zero, ignore_unknown_cells;
+ std::vector<std::string> sets_def, sets_any_undef, sets_all_undef;
+ std::map<int, std::vector<std::string>> sets_def_at, sets_any_undef_at, sets_all_undef_at;
+
+ // model variables
+ std::vector<std::string> shows;
+ SigPool show_signal_pool;
+ SigSet<RTLIL::Cell*> show_drivers;
+ int max_timestep, timeout;
+ bool gotTimeout;
+
+ SatHelper(RTLIL::Design *design, RTLIL::Module *module, bool enable_undef) :
+ design(design), module(module), sigmap(module), ct(design), satgen(ez.get(), &sigmap)
+ {
+ this->enable_undef = enable_undef;
+ satgen.model_undef = enable_undef;
+ set_init_def = false;
+ set_init_undef = false;
+ set_init_zero = false;
+ ignore_unknown_cells = false;
+ max_timestep = -1;
+ timeout = 0;
+ gotTimeout = false;
+ }
+
+ void check_undef_enabled(const RTLIL::SigSpec &sig)
+ {
+ if (enable_undef)
+ return;
+
+ std::vector<RTLIL::SigBit> sigbits = sig.to_sigbit_vector();
+ for (size_t i = 0; i < sigbits.size(); i++)
+ if (sigbits[i].wire == NULL && sigbits[i].data == RTLIL::State::Sx)
+ log_cmd_error("Bit %d of %s is undef but option -enable_undef is missing!\n", int(i), log_signal(sig));
+ }
+
+ void setup(int timestep = -1, bool initstate = false)
+ {
+ if (timestep > 0)
+ log ("\nSetting up time step %d:\n", timestep);
+ else
+ log ("\nSetting up SAT problem:\n");
+
+ if (initstate)
+ satgen.setInitState(timestep);
+
+ if (timestep > max_timestep)
+ max_timestep = timestep;
+
+ RTLIL::SigSpec big_lhs, big_rhs;
+
+ for (auto &s : sets)
+ {
+ RTLIL::SigSpec lhs, rhs;
+
+ if (!RTLIL::SigSpec::parse_sel(lhs, design, module, s.first))
+ log_cmd_error("Failed to parse lhs set expression `%s'.\n", s.first.c_str());
+ if (!RTLIL::SigSpec::parse_rhs(lhs, rhs, module, s.second))
+ log_cmd_error("Failed to parse rhs set expression `%s'.\n", s.second.c_str());
+ show_signal_pool.add(sigmap(lhs));
+ show_signal_pool.add(sigmap(rhs));
+
+ if (lhs.size() != rhs.size())
+ log_cmd_error("Set expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n",
+ s.first.c_str(), log_signal(lhs), lhs.size(), s.second.c_str(), log_signal(rhs), rhs.size());
+
+ log("Import set-constraint: %s = %s\n", log_signal(lhs), log_signal(rhs));
+ big_lhs.remove2(lhs, &big_rhs);
+ big_lhs.append(lhs);
+ big_rhs.append(rhs);
+ }
+
+ for (auto &s : sets_at[timestep])
+ {
+ RTLIL::SigSpec lhs, rhs;
+
+ if (!RTLIL::SigSpec::parse_sel(lhs, design, module, s.first))
+ log_cmd_error("Failed to parse lhs set expression `%s'.\n", s.first.c_str());
+ if (!RTLIL::SigSpec::parse_rhs(lhs, rhs, module, s.second))
+ log_cmd_error("Failed to parse rhs set expression `%s'.\n", s.second.c_str());
+ show_signal_pool.add(sigmap(lhs));
+ show_signal_pool.add(sigmap(rhs));
+
+ if (lhs.size() != rhs.size())
+ log_cmd_error("Set expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n",
+ s.first.c_str(), log_signal(lhs), lhs.size(), s.second.c_str(), log_signal(rhs), rhs.size());
+
+ log("Import set-constraint for this timestep: %s = %s\n", log_signal(lhs), log_signal(rhs));
+ big_lhs.remove2(lhs, &big_rhs);
+ big_lhs.append(lhs);
+ big_rhs.append(rhs);
+ }
+
+ for (auto &s : unsets_at[timestep])
+ {
+ RTLIL::SigSpec lhs;
+
+ if (!RTLIL::SigSpec::parse_sel(lhs, design, module, s))
+ log_cmd_error("Failed to parse lhs set expression `%s'.\n", s.c_str());
+ show_signal_pool.add(sigmap(lhs));
+
+ log("Import unset-constraint for this timestep: %s\n", log_signal(lhs));
+ big_lhs.remove2(lhs, &big_rhs);
+ }
+
+ log("Final constraint equation: %s = %s\n", log_signal(big_lhs), log_signal(big_rhs));
+ check_undef_enabled(big_lhs), check_undef_enabled(big_rhs);
+ ez->assume(satgen.signals_eq(big_lhs, big_rhs, timestep));
+
+ // 0 = sets_def
+ // 1 = sets_any_undef
+ // 2 = sets_all_undef
+ std::set<RTLIL::SigSpec> sets_def_undef[3];
+
+ for (auto &s : sets_def) {
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, s))
+ log_cmd_error("Failed to parse set-def expression `%s'.\n", s.c_str());
+ sets_def_undef[0].insert(sig);
+ }
+
+ for (auto &s : sets_any_undef) {
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, s))
+ log_cmd_error("Failed to parse set-def expression `%s'.\n", s.c_str());
+ sets_def_undef[1].insert(sig);
+ }
+
+ for (auto &s : sets_all_undef) {
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, s))
+ log_cmd_error("Failed to parse set-def expression `%s'.\n", s.c_str());
+ sets_def_undef[2].insert(sig);
+ }
+
+ for (auto &s : sets_def_at[timestep]) {
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, s))
+ log_cmd_error("Failed to parse set-def expression `%s'.\n", s.c_str());
+ sets_def_undef[0].insert(sig);
+ sets_def_undef[1].erase(sig);
+ sets_def_undef[2].erase(sig);
+ }
+
+ for (auto &s : sets_any_undef_at[timestep]) {
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, s))
+ log_cmd_error("Failed to parse set-def expression `%s'.\n", s.c_str());
+ sets_def_undef[0].erase(sig);
+ sets_def_undef[1].insert(sig);
+ sets_def_undef[2].erase(sig);
+ }
+
+ for (auto &s : sets_all_undef_at[timestep]) {
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, s))
+ log_cmd_error("Failed to parse set-def expression `%s'.\n", s.c_str());
+ sets_def_undef[0].erase(sig);
+ sets_def_undef[1].erase(sig);
+ sets_def_undef[2].insert(sig);
+ }
+
+ for (int t = 0; t < 3; t++)
+ for (auto &sig : sets_def_undef[t]) {
+ log("Import %s constraint for this timestep: %s\n", t == 0 ? "def" : t == 1 ? "any_undef" : "all_undef", log_signal(sig));
+ std::vector<int> undef_sig = satgen.importUndefSigSpec(sig, timestep);
+ if (t == 0)
+ ez->assume(ez->NOT(ez->expression(ezSAT::OpOr, undef_sig)));
+ if (t == 1)
+ ez->assume(ez->expression(ezSAT::OpOr, undef_sig));
+ if (t == 2)
+ ez->assume(ez->expression(ezSAT::OpAnd, undef_sig));
+ }
+
+ int import_cell_counter = 0;
+ for (auto cell : module->cells())
+ if (design->selected(module, cell)) {
+ // log("Import cell: %s\n", RTLIL::id2cstr(cell->name));
+ if (satgen.importCell(cell, timestep)) {
+ for (auto &p : cell->connections())
+ if (ct.cell_output(cell->type, p.first))
+ show_drivers.insert(sigmap(p.second), cell);
+ import_cell_counter++;
+ } else if (ignore_unknown_cells)
+ log_warning("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
+ else
+ log_error("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
+ }
+ log("Imported %d cells to SAT database.\n", import_cell_counter);
+
+ if (set_assumes) {
+ RTLIL::SigSpec assumes_a, assumes_en;
+ satgen.getAssumes(assumes_a, assumes_en, timestep);
+ for (int i = 0; i < GetSize(assumes_a); i++)
+ log("Import constraint from assume cell: %s when %s.\n", log_signal(assumes_a[i]), log_signal(assumes_en[i]));
+ ez->assume(satgen.importAssumes(timestep));
+ }
+
+ if (initstate)
+ {
+ RTLIL::SigSpec big_lhs, big_rhs;
+
+ for (auto &it : module->wires_)
+ {
+ if (it.second->attributes.count("\\init") == 0)
+ continue;
+
+ RTLIL::SigSpec lhs = sigmap(it.second);
+ RTLIL::SigSpec rhs = it.second->attributes.at("\\init");
+ log_assert(lhs.size() == rhs.size());
+
+ RTLIL::SigSpec removed_bits;
+ for (int i = 0; i < lhs.size(); i++) {
+ RTLIL::SigSpec bit = lhs.extract(i, 1);
+ if (!satgen.initial_state.check_all(bit)) {
+ removed_bits.append(bit);
+ lhs.remove(i, 1);
+ rhs.remove(i, 1);
+ i--;
+ }
+ }
+
+ if (removed_bits.size())
+ log_warning("ignoring initial value on non-register: %s\n", log_signal(removed_bits));
+
+ if (lhs.size()) {
+ log("Import set-constraint from init attribute: %s = %s\n", log_signal(lhs), log_signal(rhs));
+ big_lhs.remove2(lhs, &big_rhs);
+ big_lhs.append(lhs);
+ big_rhs.append(rhs);
+ }
+ }
+
+ for (auto &s : sets_init)
+ {
+ RTLIL::SigSpec lhs, rhs;
+
+ if (!RTLIL::SigSpec::parse_sel(lhs, design, module, s.first))
+ log_cmd_error("Failed to parse lhs set expression `%s'.\n", s.first.c_str());
+ if (!RTLIL::SigSpec::parse_rhs(lhs, rhs, module, s.second))
+ log_cmd_error("Failed to parse rhs set expression `%s'.\n", s.second.c_str());
+ show_signal_pool.add(sigmap(lhs));
+ show_signal_pool.add(sigmap(rhs));
+
+ if (lhs.size() != rhs.size())
+ log_cmd_error("Set expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n",
+ s.first.c_str(), log_signal(lhs), lhs.size(), s.second.c_str(), log_signal(rhs), rhs.size());
+
+ log("Import init set-constraint: %s = %s\n", log_signal(lhs), log_signal(rhs));
+ big_lhs.remove2(lhs, &big_rhs);
+ big_lhs.append(lhs);
+ big_rhs.append(rhs);
+ }
+
+ if (!satgen.initial_state.check_all(big_lhs)) {
+ RTLIL::SigSpec rem = satgen.initial_state.remove(big_lhs);
+ log_cmd_error("Found -set-init bits that are not part of the initial_state: %s\n", log_signal(rem));
+ }
+
+ if (set_init_def) {
+ RTLIL::SigSpec rem = satgen.initial_state.export_all();
+ std::vector<int> undef_rem = satgen.importUndefSigSpec(rem, 1);
+ ez->assume(ez->NOT(ez->expression(ezSAT::OpOr, undef_rem)));
+ }
+
+ if (set_init_undef) {
+ RTLIL::SigSpec rem = satgen.initial_state.export_all();
+ rem.remove(big_lhs);
+ big_lhs.append(rem);
+ big_rhs.append(RTLIL::SigSpec(RTLIL::State::Sx, rem.size()));
+ }
+
+ if (set_init_zero) {
+ RTLIL::SigSpec rem = satgen.initial_state.export_all();
+ rem.remove(big_lhs);
+ big_lhs.append(rem);
+ big_rhs.append(RTLIL::SigSpec(RTLIL::State::S0, rem.size()));
+ }
+
+ if (big_lhs.size() == 0) {
+ log("No constraints for initial state found.\n\n");
+ return;
+ }
+
+ log("Final init constraint equation: %s = %s\n", log_signal(big_lhs), log_signal(big_rhs));
+ check_undef_enabled(big_lhs), check_undef_enabled(big_rhs);
+ ez->assume(satgen.signals_eq(big_lhs, big_rhs, timestep));
+ }
+ }
+
+ int setup_proof(int timestep = -1)
+ {
+ log_assert(prove.size() || prove_x.size() || prove_asserts);
+
+ RTLIL::SigSpec big_lhs, big_rhs;
+ std::vector<int> prove_bits;
+
+ if (prove.size() > 0)
+ {
+ for (auto &s : prove)
+ {
+ RTLIL::SigSpec lhs, rhs;
+
+ if (!RTLIL::SigSpec::parse_sel(lhs, design, module, s.first))
+ log_cmd_error("Failed to parse lhs proof expression `%s'.\n", s.first.c_str());
+ if (!RTLIL::SigSpec::parse_rhs(lhs, rhs, module, s.second))
+ log_cmd_error("Failed to parse rhs proof expression `%s'.\n", s.second.c_str());
+ show_signal_pool.add(sigmap(lhs));
+ show_signal_pool.add(sigmap(rhs));
+
+ if (lhs.size() != rhs.size())
+ log_cmd_error("Proof expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n",
+ s.first.c_str(), log_signal(lhs), lhs.size(), s.second.c_str(), log_signal(rhs), rhs.size());
+
+ log("Import proof-constraint: %s = %s\n", log_signal(lhs), log_signal(rhs));
+ big_lhs.remove2(lhs, &big_rhs);
+ big_lhs.append(lhs);
+ big_rhs.append(rhs);
+ }
+
+ log("Final proof equation: %s = %s\n", log_signal(big_lhs), log_signal(big_rhs));
+ check_undef_enabled(big_lhs), check_undef_enabled(big_rhs);
+ prove_bits.push_back(satgen.signals_eq(big_lhs, big_rhs, timestep));
+ }
+
+ if (prove_x.size() > 0)
+ {
+ for (auto &s : prove_x)
+ {
+ RTLIL::SigSpec lhs, rhs;
+
+ if (!RTLIL::SigSpec::parse_sel(lhs, design, module, s.first))
+ log_cmd_error("Failed to parse lhs proof-x expression `%s'.\n", s.first.c_str());
+ if (!RTLIL::SigSpec::parse_rhs(lhs, rhs, module, s.second))
+ log_cmd_error("Failed to parse rhs proof-x expression `%s'.\n", s.second.c_str());
+ show_signal_pool.add(sigmap(lhs));
+ show_signal_pool.add(sigmap(rhs));
+
+ if (lhs.size() != rhs.size())
+ log_cmd_error("Proof-x expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n",
+ s.first.c_str(), log_signal(lhs), lhs.size(), s.second.c_str(), log_signal(rhs), rhs.size());
+
+ log("Import proof-x-constraint: %s = %s\n", log_signal(lhs), log_signal(rhs));
+ big_lhs.remove2(lhs, &big_rhs);
+ big_lhs.append(lhs);
+ big_rhs.append(rhs);
+ }
+
+ log("Final proof-x equation: %s = %s\n", log_signal(big_lhs), log_signal(big_rhs));
+
+ std::vector<int> value_lhs = satgen.importDefSigSpec(big_lhs, timestep);
+ std::vector<int> value_rhs = satgen.importDefSigSpec(big_rhs, timestep);
+
+ std::vector<int> undef_lhs = satgen.importUndefSigSpec(big_lhs, timestep);
+ std::vector<int> undef_rhs = satgen.importUndefSigSpec(big_rhs, timestep);
+
+ for (size_t i = 0; i < value_lhs.size(); i++)
+ prove_bits.push_back(ez->OR(undef_lhs.at(i), ez->AND(ez->NOT(undef_rhs.at(i)), ez->NOT(ez->XOR(value_lhs.at(i), value_rhs.at(i))))));
+ }
+
+ if (prove_asserts) {
+ RTLIL::SigSpec asserts_a, asserts_en;
+ satgen.getAsserts(asserts_a, asserts_en, timestep);
+ for (int i = 0; i < GetSize(asserts_a); i++)
+ log("Import proof for assert: %s when %s.\n", log_signal(asserts_a[i]), log_signal(asserts_en[i]));
+ prove_bits.push_back(satgen.importAsserts(timestep));
+ }
+
+ return ez->expression(ezSAT::OpAnd, prove_bits);
+ }
+
+ void force_unique_state(int timestep_from, int timestep_to)
+ {
+ RTLIL::SigSpec state_signals = satgen.initial_state.export_all();
+ for (int i = timestep_from; i < timestep_to; i++)
+ ez->assume(ez->NOT(satgen.signals_eq(state_signals, state_signals, i, timestep_to)));
+ }
+
+ bool solve(const std::vector<int> &assumptions)
+ {
+ log_assert(gotTimeout == false);
+ ez->setSolverTimeout(timeout);
+ bool success = ez->solve(modelExpressions, modelValues, assumptions);
+ if (ez->getSolverTimoutStatus())
+ gotTimeout = true;
+ return success;
+ }
+
+ bool solve(int a = 0, int b = 0, int c = 0, int d = 0, int e = 0, int f = 0)
+ {
+ log_assert(gotTimeout == false);
+ ez->setSolverTimeout(timeout);
+ bool success = ez->solve(modelExpressions, modelValues, a, b, c, d, e, f);
+ if (ez->getSolverTimoutStatus())
+ gotTimeout = true;
+ return success;
+ }
+
+ struct ModelBlockInfo {
+ int timestep, offset, width;
+ std::string description;
+ bool operator < (const ModelBlockInfo &other) const {
+ if (timestep != other.timestep)
+ return timestep < other.timestep;
+ if (description != other.description)
+ return description < other.description;
+ if (offset != other.offset)
+ return offset < other.offset;
+ if (width != other.width)
+ return width < other.width;
+ return false;
+ }
+ };
+
+ std::vector<int> modelExpressions;
+ std::vector<bool> modelValues;
+ std::set<ModelBlockInfo> modelInfo;
+
+ void maximize_undefs()
+ {
+ log_assert(enable_undef);
+ std::vector<bool> backupValues;
+
+ while (1)
+ {
+ std::vector<int> must_undef, maybe_undef;
+
+ for (size_t i = 0; i < modelExpressions.size()/2; i++)
+ if (modelValues.at(modelExpressions.size()/2 + i))
+ must_undef.push_back(modelExpressions.at(modelExpressions.size()/2 + i));
+ else
+ maybe_undef.push_back(modelExpressions.at(modelExpressions.size()/2 + i));
+
+ backupValues.swap(modelValues);
+ if (!solve(ez->expression(ezSAT::OpAnd, must_undef), ez->expression(ezSAT::OpOr, maybe_undef)))
+ break;
+ }
+
+ backupValues.swap(modelValues);
+ }
+
+ void generate_model()
+ {
+ RTLIL::SigSpec modelSig;
+ modelExpressions.clear();
+ modelInfo.clear();
+
+ // Add "show" signals or alternatively the leaves on the input cone on all set and prove signals
+
+ if (shows.size() == 0)
+ {
+ SigPool queued_signals, handled_signals, final_signals;
+ queued_signals = show_signal_pool;
+ while (queued_signals.size() > 0) {
+ RTLIL::SigSpec sig = queued_signals.export_one();
+ queued_signals.del(sig);
+ handled_signals.add(sig);
+ std::set<RTLIL::Cell*> drivers = show_drivers.find(sig);
+ if (drivers.size() == 0) {
+ final_signals.add(sig);
+ } else {
+ for (auto &d : drivers)
+ for (auto &p : d->connections()) {
+ if (d->type == "$dff" && p.first == "\\CLK")
+ continue;
+ if (d->type.substr(0, 6) == "$_DFF_" && p.first == "\\C")
+ continue;
+ queued_signals.add(handled_signals.remove(sigmap(p.second)));
+ }
+ }
+ }
+ modelSig = final_signals.export_all();
+
+ // additionally add all set and prove signals directly
+ // (it improves user confidence if we write the constraints back ;-)
+ modelSig.append(show_signal_pool.export_all());
+ }
+ else
+ {
+ for (auto &s : shows) {
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, s))
+ log_cmd_error("Failed to parse show expression `%s'.\n", s.c_str());
+ log("Import show expression: %s\n", log_signal(sig));
+ modelSig.append(sig);
+ }
+ }
+
+ modelSig.sort_and_unify();
+ // log("Model signals: %s\n", log_signal(modelSig));
+
+ std::vector<int> modelUndefExpressions;
+
+ for (auto &c : modelSig.chunks())
+ if (c.wire != NULL)
+ {
+ ModelBlockInfo info;
+ RTLIL::SigSpec chunksig = c;
+ info.width = chunksig.size();
+ info.description = log_signal(chunksig);
+
+ for (int timestep = -1; timestep <= max_timestep; timestep++)
+ {
+ if ((timestep == -1 && max_timestep > 0) || timestep == 0)
+ continue;
+
+ info.timestep = timestep;
+ info.offset = modelExpressions.size();
+ modelInfo.insert(info);
+
+ std::vector<int> vec = satgen.importSigSpec(chunksig, timestep);
+ modelExpressions.insert(modelExpressions.end(), vec.begin(), vec.end());
+
+ if (enable_undef) {
+ std::vector<int> undef_vec = satgen.importUndefSigSpec(chunksig, timestep);
+ modelUndefExpressions.insert(modelUndefExpressions.end(), undef_vec.begin(), undef_vec.end());
+ }
+ }
+ }
+
+ // Add initial state signals as collected by satgen
+ //
+ modelSig = satgen.initial_state.export_all();
+ for (auto &c : modelSig.chunks())
+ if (c.wire != NULL)
+ {
+ ModelBlockInfo info;
+ RTLIL::SigSpec chunksig = c;
+
+ info.timestep = 0;
+ info.offset = modelExpressions.size();
+ info.width = chunksig.size();
+ info.description = log_signal(chunksig);
+ modelInfo.insert(info);
+
+ std::vector<int> vec = satgen.importSigSpec(chunksig, 1);
+ modelExpressions.insert(modelExpressions.end(), vec.begin(), vec.end());
+
+ if (enable_undef) {
+ std::vector<int> undef_vec = satgen.importUndefSigSpec(chunksig, 1);
+ modelUndefExpressions.insert(modelUndefExpressions.end(), undef_vec.begin(), undef_vec.end());
+ }
+ }
+
+ modelExpressions.insert(modelExpressions.end(), modelUndefExpressions.begin(), modelUndefExpressions.end());
+ }
+
+ void print_model()
+ {
+ int maxModelName = 10;
+ int maxModelWidth = 10;
+
+ for (auto &info : modelInfo) {
+ maxModelName = max(maxModelName, int(info.description.size()));
+ maxModelWidth = max(maxModelWidth, info.width);
+ }
+
+ log("\n");
+
+ int last_timestep = -2;
+ for (auto &info : modelInfo)
+ {
+ RTLIL::Const value;
+ bool found_undef = false;
+
+ for (int i = 0; i < info.width; i++) {
+ value.bits.push_back(modelValues.at(info.offset+i) ? RTLIL::State::S1 : RTLIL::State::S0);
+ if (enable_undef && modelValues.at(modelExpressions.size()/2 + info.offset + i))
+ value.bits.back() = RTLIL::State::Sx, found_undef = true;
+ }
+
+ if (info.timestep != last_timestep) {
+ const char *hline = "---------------------------------------------------------------------------------------------------"
+ "---------------------------------------------------------------------------------------------------"
+ "---------------------------------------------------------------------------------------------------";
+ if (last_timestep == -2) {
+ log(max_timestep > 0 ? " Time " : " ");
+ log("%-*s %11s %9s %*s\n", maxModelName+5, "Signal Name", "Dec", "Hex", maxModelWidth+3, "Bin");
+ }
+ log(max_timestep > 0 ? " ---- " : " ");
+ log("%*.*s %11.11s %9.9s %*.*s\n", maxModelName+5, maxModelName+5,
+ hline, hline, hline, maxModelWidth+3, maxModelWidth+3, hline);
+ last_timestep = info.timestep;
+ }
+
+ if (max_timestep > 0) {
+ if (info.timestep > 0)
+ log(" %4d ", info.timestep);
+ else
+ log(" init ");
+ } else
+ log(" ");
+
+ if (info.width <= 32 && !found_undef)
+ log("%-*s %11d %9x %*s\n", maxModelName+5, info.description.c_str(), value.as_int(), value.as_int(), maxModelWidth+3, value.as_string().c_str());
+ else
+ log("%-*s %11s %9s %*s\n", maxModelName+5, info.description.c_str(), "--", "--", maxModelWidth+3, value.as_string().c_str());
+ }
+
+ if (last_timestep == -2)
+ log(" no model variables selected for display.\n");
+ }
+
+ void dump_model_to_vcd(std::string vcd_file_name)
+ {
+ FILE *f = fopen(vcd_file_name.c_str(), "w");
+ if (!f)
+ log_cmd_error("Can't open output file `%s' for writing: %s\n", vcd_file_name.c_str(), strerror(errno));
+
+ log("Dumping SAT model to VCD file %s\n", vcd_file_name.c_str());
+
+ time_t timestamp;
+ struct tm* now;
+ char stime[128] = {};
+ time(&timestamp);
+ now = localtime(&timestamp);
+ strftime(stime, sizeof(stime), "%c", now);
+
+ std::string module_fname = "unknown";
+ auto apos = module->attributes.find("\\src");
+ if(apos != module->attributes.end())
+ module_fname = module->attributes["\\src"].decode_string();
+
+ fprintf(f, "$date\n");
+ fprintf(f, " %s\n", stime);
+ fprintf(f, "$end\n");
+ fprintf(f, "$version\n");
+ fprintf(f, " Generated by %s\n", yosys_version_str);
+ fprintf(f, "$end\n");
+ fprintf(f, "$comment\n");
+ fprintf(f, " Generated from SAT problem in module %s (declared at %s)\n",
+ module->name.c_str(), module_fname.c_str());
+ fprintf(f, "$end\n");
+
+ // VCD has some limits on internal (non-display) identifier names, so make legal ones
+ std::map<std::string, std::string> vcdnames;
+
+ fprintf(f, "$timescale 1ns\n"); // arbitrary time scale since actual clock period is unknown/unimportant
+ fprintf(f, "$scope module %s $end\n", module->name.c_str());
+ for (auto &info : modelInfo)
+ {
+ if (vcdnames.find(info.description) != vcdnames.end())
+ continue;
+
+ char namebuf[16];
+ snprintf(namebuf, sizeof(namebuf), "v%d", static_cast<int>(vcdnames.size()));
+ vcdnames[info.description] = namebuf;
+
+ // Even display identifiers can't use some special characters
+ std::string legal_desc = info.description.c_str();
+ for (auto &c : legal_desc) {
+ if(c == '$')
+ c = '_';
+ if(c == ':')
+ c = '_';
+ }
+
+ fprintf(f, "$var wire %d %s %s $end\n", info.width, namebuf, legal_desc.c_str());
+
+ // Need to look at first *two* cycles!
+ // We need to put a name on all variables but those without an initialization clause
+ // have no value at timestep 0
+ if(info.timestep > 1)
+ break;
+ }
+ fprintf(f, "$upscope $end\n");
+ fprintf(f, "$enddefinitions $end\n");
+ fprintf(f, "$dumpvars\n");
+
+ static const char bitvals[] = "01xzxx";
+
+ int last_timestep = -2;
+ for (auto &info : modelInfo)
+ {
+ RTLIL::Const value;
+
+ for (int i = 0; i < info.width; i++) {
+ value.bits.push_back(modelValues.at(info.offset+i) ? RTLIL::State::S1 : RTLIL::State::S0);
+ if (enable_undef && modelValues.at(modelExpressions.size()/2 + info.offset + i))
+ value.bits.back() = RTLIL::State::Sx;
+ }
+
+ if (info.timestep != last_timestep) {
+ if(last_timestep == 0)
+ fprintf(f, "$end\n");
+ else
+ fprintf(f, "#%d\n", info.timestep);
+ last_timestep = info.timestep;
+ }
+
+ if(info.width == 1) {
+ fprintf(f, "%c%s\n", bitvals[value.bits[0]], vcdnames[info.description].c_str());
+ } else {
+ fprintf(f, "b");
+ for(int k=info.width-1; k >= 0; k --) //need to flip bit ordering for VCD
+ fprintf(f, "%c", bitvals[value.bits[k]]);
+ fprintf(f, " %s\n", vcdnames[info.description].c_str());
+ }
+ }
+
+ if (last_timestep == -2)
+ log(" no model variables selected for display.\n");
+
+ fclose(f);
+ }
+
+ void dump_model_to_json(std::string json_file_name)
+ {
+ FILE *f = fopen(json_file_name.c_str(), "w");
+ if (!f)
+ log_cmd_error("Can't open output file `%s' for writing: %s\n", json_file_name.c_str(), strerror(errno));
+
+ log("Dumping SAT model to WaveJSON file '%s'.\n", json_file_name.c_str());
+
+ int mintime = 1, maxtime = 0, maxwidth = 0;;
+ dict<string, pair<int, dict<int, Const>>> wavedata;
+
+ for (auto &info : modelInfo)
+ {
+ Const value;
+ for (int i = 0; i < info.width; i++) {
+ value.bits.push_back(modelValues.at(info.offset+i) ? RTLIL::State::S1 : RTLIL::State::S0);
+ if (enable_undef && modelValues.at(modelExpressions.size()/2 + info.offset + i))
+ value.bits.back() = RTLIL::State::Sx;
+ }
+
+ wavedata[info.description].first = info.width;
+ wavedata[info.description].second[info.timestep] = value;
+ mintime = min(mintime, info.timestep);
+ maxtime = max(maxtime, info.timestep);
+ maxwidth = max(maxwidth, info.width);
+ }
+
+ fprintf(f, "{ \"signal\": [");
+ bool fist_wavedata = true;
+ for (auto &wd : wavedata)
+ {
+ fprintf(f, "%s", fist_wavedata ? "\n" : ",\n");
+ fist_wavedata = false;
+
+ vector<string> data;
+ string name = wd.first.c_str();
+ while (name.substr(0, 1) == "\\")
+ name = name.substr(1);
+
+ fprintf(f, " { \"name\": \"%s\", \"wave\": \"", name.c_str());
+ for (int i = mintime; i <= maxtime; i++) {
+ if (wd.second.second.count(i)) {
+ string this_data = wd.second.second[i].as_string();
+ char ch = '=';
+ if (wd.second.first == 1)
+ ch = this_data[0];
+ if (!data.empty() && data.back() == this_data) {
+ fprintf(f, ".");
+ } else {
+ data.push_back(this_data);
+ fprintf(f, "%c", ch);
+ }
+ } else {
+ data.push_back("");
+ fprintf(f, "4");
+ }
+ }
+ if (wd.second.first != 1) {
+ fprintf(f, "\", \"data\": [");
+ for (int i = 0; i < GetSize(data); i++)
+ fprintf(f, "%s\"%s\"", i ? ", " : "", data[i].c_str());
+ fprintf(f, "] }");
+ } else {
+ fprintf(f, "\" }");
+ }
+ }
+ fprintf(f, "\n ],\n");
+ fprintf(f, " \"config\": {\n");
+ fprintf(f, " \"hscale\": %.2f\n", maxwidth / 4.0);
+ fprintf(f, " }\n");
+ fprintf(f, "}\n");
+ fclose(f);
+ }
+
+ void invalidate_model(bool max_undef)
+ {
+ std::vector<int> clause;
+ if (enable_undef) {
+ for (size_t i = 0; i < modelExpressions.size()/2; i++) {
+ int bit = modelExpressions.at(i), bit_undef = modelExpressions.at(modelExpressions.size()/2 + i);
+ bool val = modelValues.at(i), val_undef = modelValues.at(modelExpressions.size()/2 + i);
+ if (!max_undef || !val_undef)
+ clause.push_back(val_undef ? ez->NOT(bit_undef) : val ? ez->NOT(bit) : bit);
+ }
+ } else
+ for (size_t i = 0; i < modelExpressions.size(); i++)
+ clause.push_back(modelValues.at(i) ? ez->NOT(modelExpressions.at(i)) : modelExpressions.at(i));
+ ez->assume(ez->expression(ezSAT::OpOr, clause));
+ }
+};
+
+void print_proof_failed()
+{
+ log("\n");
+ log(" ______ ___ ___ _ _ _ _ \n");
+ log(" (_____ \\ / __) / __) (_) | | | |\n");
+ log(" _____) )___ ___ ___ _| |__ _| |__ _____ _| | _____ __| | |\n");
+ log(" | ____/ ___) _ \\ / _ (_ __) (_ __|____ | | || ___ |/ _ |_|\n");
+ log(" | | | | | |_| | |_| || | | | / ___ | | || ____( (_| |_ \n");
+ log(" |_| |_| \\___/ \\___/ |_| |_| \\_____|_|\\_)_____)\\____|_|\n");
+ log("\n");
+}
+
+void print_timeout()
+{
+ log("\n");
+ log(" _____ _ _ _____ ____ _ _____\n");
+ log(" /__ __\\/ \\/ \\__/|/ __// _ \\/ \\ /\\/__ __\\\n");
+ log(" / \\ | || |\\/||| \\ | / \\|| | || / \\\n");
+ log(" | | | || | ||| /_ | \\_/|| \\_/| | |\n");
+ log(" \\_/ \\_/\\_/ \\|\\____\\\\____/\\____/ \\_/\n");
+ log("\n");
+}
+
+void print_qed()
+{
+ log("\n");
+ log(" /$$$$$$ /$$$$$$$$ /$$$$$$$ \n");
+ log(" /$$__ $$ | $$_____/ | $$__ $$ \n");
+ log(" | $$ \\ $$ | $$ | $$ \\ $$ \n");
+ log(" | $$ | $$ | $$$$$ | $$ | $$ \n");
+ log(" | $$ | $$ | $$__/ | $$ | $$ \n");
+ log(" | $$/$$ $$ | $$ | $$ | $$ \n");
+ log(" | $$$$$$/ /$$| $$$$$$$$ /$$| $$$$$$$//$$\n");
+ log(" \\____ $$$|__/|________/|__/|_______/|__/\n");
+ log(" \\__/ \n");
+ log("\n");
+}
+
+struct SatPass : public Pass {
+ SatPass() : Pass("sat", "solve a SAT problem in the circuit") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" sat [options] [selection]\n");
+ log("\n");
+ log("This command solves a SAT problem defined over the currently selected circuit\n");
+ log("and additional constraints passed as parameters.\n");
+ log("\n");
+ log(" -all\n");
+ log(" show all solutions to the problem (this can grow exponentially, use\n");
+ log(" -max <N> instead to get <N> solutions)\n");
+ log("\n");
+ log(" -max <N>\n");
+ log(" like -all, but limit number of solutions to <N>\n");
+ log("\n");
+ log(" -enable_undef\n");
+ log(" enable modeling of undef value (aka 'x-bits')\n");
+ log(" this option is implied by -set-def, -set-undef et. cetera\n");
+ log("\n");
+ log(" -max_undef\n");
+ log(" maximize the number of undef bits in solutions, giving a better\n");
+ log(" picture of which input bits are actually vital to the solution.\n");
+ log("\n");
+ log(" -set <signal> <value>\n");
+ log(" set the specified signal to the specified value.\n");
+ log("\n");
+ log(" -set-def <signal>\n");
+ log(" add a constraint that all bits of the given signal must be defined\n");
+ log("\n");
+ log(" -set-any-undef <signal>\n");
+ log(" add a constraint that at least one bit of the given signal is undefined\n");
+ log("\n");
+ log(" -set-all-undef <signal>\n");
+ log(" add a constraint that all bits of the given signal are undefined\n");
+ log("\n");
+ log(" -set-def-inputs\n");
+ log(" add -set-def constraints for all module inputs\n");
+ log("\n");
+ log(" -show <signal>\n");
+ log(" show the model for the specified signal. if no -show option is\n");
+ log(" passed then a set of signals to be shown is automatically selected.\n");
+ log("\n");
+ log(" -show-inputs, -show-outputs, -show-ports\n");
+ log(" add all module (input/output) ports to the list of shown signals\n");
+ log("\n");
+ log(" -show-regs, -show-public, -show-all\n");
+ log(" show all registers, show signals with 'public' names, show all signals\n");
+ log("\n");
+ log(" -ignore_div_by_zero\n");
+ log(" ignore all solutions that involve a division by zero\n");
+ log("\n");
+ log(" -ignore_unknown_cells\n");
+ log(" ignore all cells that can not be matched to a SAT model\n");
+ log("\n");
+ log("The following options can be used to set up a sequential problem:\n");
+ log("\n");
+ log(" -seq <N>\n");
+ log(" set up a sequential problem with <N> time steps. The steps will\n");
+ log(" be numbered from 1 to N.\n");
+ log("\n");
+ log(" note: for large <N> it can be significantly faster to use\n");
+ log(" -tempinduct-baseonly -maxsteps <N> instead of -seq <N>.\n");
+ log("\n");
+ log(" -set-at <N> <signal> <value>\n");
+ log(" -unset-at <N> <signal>\n");
+ log(" set or unset the specified signal to the specified value in the\n");
+ log(" given timestep. this has priority over a -set for the same signal.\n");
+ log("\n");
+ log(" -set-assumes\n");
+ log(" set all assumptions provided via $assume cells\n");
+ log("\n");
+ log(" -set-def-at <N> <signal>\n");
+ log(" -set-any-undef-at <N> <signal>\n");
+ log(" -set-all-undef-at <N> <signal>\n");
+ log(" add undef constraints in the given timestep.\n");
+ log("\n");
+ log(" -set-init <signal> <value>\n");
+ log(" set the initial value for the register driving the signal to the value\n");
+ log("\n");
+ log(" -set-init-undef\n");
+ log(" set all initial states (not set using -set-init) to undef\n");
+ log("\n");
+ log(" -set-init-def\n");
+ log(" do not force a value for the initial state but do not allow undef\n");
+ log("\n");
+ log(" -set-init-zero\n");
+ log(" set all initial states (not set using -set-init) to zero\n");
+ log("\n");
+ log(" -dump_vcd <vcd-file-name>\n");
+ log(" dump SAT model (counter example in proof) to VCD file\n");
+ log("\n");
+ log(" -dump_json <json-file-name>\n");
+ log(" dump SAT model (counter example in proof) to a WaveJSON file.\n");
+ log("\n");
+ log(" -dump_cnf <cnf-file-name>\n");
+ log(" dump CNF of SAT problem (in DIMACS format). in temporal induction\n");
+ log(" proofs this is the CNF of the first induction step.\n");
+ log("\n");
+ log("The following additional options can be used to set up a proof. If also -seq\n");
+ log("is passed, a temporal induction proof is performed.\n");
+ log("\n");
+ log(" -tempinduct\n");
+ log(" Perform a temporal induction proof. In a temporal induction proof it is\n");
+ log(" proven that the condition holds forever after the number of time steps\n");
+ log(" specified using -seq.\n");
+ log("\n");
+ log(" -tempinduct-def\n");
+ log(" Perform a temporal induction proof. Assume an initial state with all\n");
+ log(" registers set to defined values for the induction step.\n");
+ log("\n");
+ log(" -tempinduct-baseonly\n");
+ log(" Run only the basecase half of temporal induction (requires -maxsteps)\n");
+ log("\n");
+ log(" -tempinduct-inductonly\n");
+ log(" Run only the induction half of temporal induction\n");
+ log("\n");
+ log(" -tempinduct-skip <N>\n");
+ log(" Skip the first <N> steps of the induction proof.\n");
+ log("\n");
+ log(" note: this will assume that the base case holds for <N> steps.\n");
+ log(" this must be proven independently with \"-tempinduct-baseonly\n");
+ log(" -maxsteps <N>\". Use -initsteps if you just want to set a\n");
+ log(" minimal induction length.\n");
+ log("\n");
+ log(" -prove <signal> <value>\n");
+ log(" Attempt to proof that <signal> is always <value>.\n");
+ log("\n");
+ log(" -prove-x <signal> <value>\n");
+ log(" Like -prove, but an undef (x) bit in the lhs matches any value on\n");
+ log(" the right hand side. Useful for equivalence checking.\n");
+ log("\n");
+ log(" -prove-asserts\n");
+ log(" Prove that all asserts in the design hold.\n");
+ log("\n");
+ log(" -prove-skip <N>\n");
+ log(" Do not enforce the prove-condition for the first <N> time steps.\n");
+ log("\n");
+ log(" -maxsteps <N>\n");
+ log(" Set a maximum length for the induction.\n");
+ log("\n");
+ log(" -initsteps <N>\n");
+ log(" Set initial length for the induction.\n");
+ log(" This will speed up the search of the right induction length\n");
+ log(" for deep induction proofs.\n");
+ log("\n");
+ log(" -stepsize <N>\n");
+ log(" Increase the size of the induction proof in steps of <N>.\n");
+ log(" This will speed up the search of the right induction length\n");
+ log(" for deep induction proofs.\n");
+ log("\n");
+ log(" -timeout <N>\n");
+ log(" Maximum number of seconds a single SAT instance may take.\n");
+ log("\n");
+ log(" -verify\n");
+ log(" Return an error and stop the synthesis script if the proof fails.\n");
+ log("\n");
+ log(" -verify-no-timeout\n");
+ log(" Like -verify but do not return an error for timeouts.\n");
+ log("\n");
+ log(" -falsify\n");
+ log(" Return an error and stop the synthesis script if the proof succeeds.\n");
+ log("\n");
+ log(" -falsify-no-timeout\n");
+ log(" Like -falsify but do not return an error for timeouts.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::vector<std::pair<std::string, std::string>> sets, sets_init, prove, prove_x;
+ std::map<int, std::vector<std::pair<std::string, std::string>>> sets_at;
+ std::map<int, std::vector<std::string>> unsets_at, sets_def_at, sets_any_undef_at, sets_all_undef_at;
+ std::vector<std::string> shows, sets_def, sets_any_undef, sets_all_undef;
+ int loopcount = 0, seq_len = 0, maxsteps = 0, initsteps = 0, timeout = 0, prove_skip = 0;
+ bool verify = false, fail_on_timeout = false, enable_undef = false, set_def_inputs = false;
+ bool ignore_div_by_zero = false, set_init_undef = false, set_init_zero = false, max_undef = false;
+ bool tempinduct = false, prove_asserts = false, show_inputs = false, show_outputs = false;
+ bool show_regs = false, show_public = false, show_all = false;
+ bool ignore_unknown_cells = false, falsify = false, tempinduct_def = false, set_init_def = false;
+ bool tempinduct_baseonly = false, tempinduct_inductonly = false, set_assumes = false;
+ int tempinduct_skip = 0, stepsize = 1;
+ std::string vcd_file_name, json_file_name, cnf_file_name;
+
+ log_header(design, "Executing SAT pass (solving SAT problems in the circuit).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-all") {
+ loopcount = -1;
+ continue;
+ }
+ if (args[argidx] == "-verify") {
+ fail_on_timeout = true;
+ verify = true;
+ continue;
+ }
+ if (args[argidx] == "-verify-no-timeout") {
+ verify = true;
+ continue;
+ }
+ if (args[argidx] == "-falsify") {
+ fail_on_timeout = true;
+ falsify = true;
+ continue;
+ }
+ if (args[argidx] == "-falsify-no-timeout") {
+ falsify = true;
+ continue;
+ }
+ if (args[argidx] == "-timeout" && argidx+1 < args.size()) {
+ timeout = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-max" && argidx+1 < args.size()) {
+ loopcount = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-maxsteps" && argidx+1 < args.size()) {
+ maxsteps = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-initsteps" && argidx+1 < args.size()) {
+ initsteps = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-stepsize" && argidx+1 < args.size()) {
+ stepsize = max(1, atoi(args[++argidx].c_str()));
+ continue;
+ }
+ if (args[argidx] == "-ignore_div_by_zero") {
+ ignore_div_by_zero = true;
+ continue;
+ }
+ if (args[argidx] == "-enable_undef") {
+ enable_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-max_undef") {
+ enable_undef = true;
+ max_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-set-def-inputs") {
+ enable_undef = true;
+ set_def_inputs = true;
+ continue;
+ }
+ if (args[argidx] == "-set" && argidx+2 < args.size()) {
+ std::string lhs = args[++argidx];
+ std::string rhs = args[++argidx];
+ sets.push_back(std::pair<std::string, std::string>(lhs, rhs));
+ continue;
+ }
+ if (args[argidx] == "-set-def" && argidx+1 < args.size()) {
+ sets_def.push_back(args[++argidx]);
+ enable_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-set-any-undef" && argidx+1 < args.size()) {
+ sets_any_undef.push_back(args[++argidx]);
+ enable_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-set-all-undef" && argidx+1 < args.size()) {
+ sets_all_undef.push_back(args[++argidx]);
+ enable_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-set-assumes") {
+ set_assumes = true;
+ continue;
+ }
+ if (args[argidx] == "-tempinduct") {
+ tempinduct = true;
+ continue;
+ }
+ if (args[argidx] == "-tempinduct-def") {
+ tempinduct = true;
+ tempinduct_def = true;
+ continue;
+ }
+ if (args[argidx] == "-tempinduct-baseonly") {
+ tempinduct = true;
+ tempinduct_baseonly = true;
+ continue;
+ }
+ if (args[argidx] == "-tempinduct-inductonly") {
+ tempinduct = true;
+ tempinduct_inductonly = true;
+ continue;
+ }
+ if (args[argidx] == "-tempinduct-skip" && argidx+1 < args.size()) {
+ tempinduct_skip = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-prove" && argidx+2 < args.size()) {
+ std::string lhs = args[++argidx];
+ std::string rhs = args[++argidx];
+ prove.push_back(std::pair<std::string, std::string>(lhs, rhs));
+ continue;
+ }
+ if (args[argidx] == "-prove-x" && argidx+2 < args.size()) {
+ std::string lhs = args[++argidx];
+ std::string rhs = args[++argidx];
+ prove_x.push_back(std::pair<std::string, std::string>(lhs, rhs));
+ enable_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-prove-asserts") {
+ prove_asserts = true;
+ continue;
+ }
+ if (args[argidx] == "-prove-skip" && argidx+1 < args.size()) {
+ prove_skip = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-seq" && argidx+1 < args.size()) {
+ seq_len = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-set-at" && argidx+3 < args.size()) {
+ int timestep = atoi(args[++argidx].c_str());
+ std::string lhs = args[++argidx];
+ std::string rhs = args[++argidx];
+ sets_at[timestep].push_back(std::pair<std::string, std::string>(lhs, rhs));
+ continue;
+ }
+ if (args[argidx] == "-unset-at" && argidx+2 < args.size()) {
+ int timestep = atoi(args[++argidx].c_str());
+ unsets_at[timestep].push_back(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-set-def-at" && argidx+2 < args.size()) {
+ int timestep = atoi(args[++argidx].c_str());
+ sets_def_at[timestep].push_back(args[++argidx]);
+ enable_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-set-any-undef-at" && argidx+2 < args.size()) {
+ int timestep = atoi(args[++argidx].c_str());
+ sets_any_undef_at[timestep].push_back(args[++argidx]);
+ enable_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-set-all-undef-at" && argidx+2 < args.size()) {
+ int timestep = atoi(args[++argidx].c_str());
+ sets_all_undef_at[timestep].push_back(args[++argidx]);
+ enable_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-set-init" && argidx+2 < args.size()) {
+ std::string lhs = args[++argidx];
+ std::string rhs = args[++argidx];
+ sets_init.push_back(std::pair<std::string, std::string>(lhs, rhs));
+ continue;
+ }
+ if (args[argidx] == "-set-init-undef") {
+ set_init_undef = true;
+ enable_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-set-init-def") {
+ set_init_def = true;
+ continue;
+ }
+ if (args[argidx] == "-set-init-zero") {
+ set_init_zero = true;
+ continue;
+ }
+ if (args[argidx] == "-show" && argidx+1 < args.size()) {
+ shows.push_back(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-show-inputs") {
+ show_inputs = true;
+ continue;
+ }
+ if (args[argidx] == "-show-outputs") {
+ show_outputs = true;
+ continue;
+ }
+ if (args[argidx] == "-show-ports") {
+ show_inputs = true;
+ show_outputs = true;
+ continue;
+ }
+ if (args[argidx] == "-show-regs") {
+ show_regs = true;
+ continue;
+ }
+ if (args[argidx] == "-show-public") {
+ show_public = true;
+ continue;
+ }
+ if (args[argidx] == "-show-all") {
+ show_all = true;
+ continue;
+ }
+ if (args[argidx] == "-ignore_unknown_cells") {
+ ignore_unknown_cells = true;
+ continue;
+ }
+ if (args[argidx] == "-dump_vcd" && argidx+1 < args.size()) {
+ vcd_file_name = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-dump_json" && argidx+1 < args.size()) {
+ json_file_name = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-dump_cnf" && argidx+1 < args.size()) {
+ cnf_file_name = args[++argidx];
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ RTLIL::Module *module = NULL;
+ for (auto mod : design->selected_modules()) {
+ if (module)
+ log_cmd_error("Only one module must be selected for the SAT pass! (selected: %s and %s)\n", log_id(module), log_id(mod));
+ module = mod;
+ }
+ if (module == NULL)
+ log_cmd_error("Can't perform SAT on an empty selection!\n");
+
+ if (!prove.size() && !prove_x.size() && !prove_asserts && tempinduct)
+ log_cmd_error("Got -tempinduct but nothing to prove!\n");
+
+ if (prove_skip && tempinduct)
+ log_cmd_error("Options -prove-skip and -tempinduct don't work with each other. Use -seq instead of -prove-skip.\n");
+
+ if (prove_skip >= seq_len && prove_skip > 0)
+ log_cmd_error("The value of -prove-skip must be smaller than the one of -seq.\n");
+
+ if (set_init_undef + set_init_zero + set_init_def > 1)
+ log_cmd_error("The options -set-init-undef, -set-init-def, and -set-init-zero are exclusive!\n");
+
+ if (set_def_inputs) {
+ for (auto &it : module->wires_)
+ if (it.second->port_input)
+ sets_def.push_back(it.second->name.str());
+ }
+
+ if (show_inputs) {
+ for (auto &it : module->wires_)
+ if (it.second->port_input)
+ shows.push_back(it.second->name.str());
+ }
+
+ if (show_outputs) {
+ for (auto &it : module->wires_)
+ if (it.second->port_output)
+ shows.push_back(it.second->name.str());
+ }
+
+ if (show_regs) {
+ pool<Wire*> reg_wires;
+ for (auto cell : module->cells()) {
+ if (cell->type == "$dff" || cell->type.substr(0, 6) == "$_DFF_")
+ for (auto bit : cell->getPort("\\Q"))
+ if (bit.wire)
+ reg_wires.insert(bit.wire);
+ }
+ for (auto wire : reg_wires)
+ shows.push_back(wire->name.str());
+ }
+
+ if (show_public) {
+ for (auto wire : module->wires())
+ if (wire->name[0] == '\\')
+ shows.push_back(wire->name.str());
+ }
+
+ if (show_all) {
+ for (auto wire : module->wires())
+ shows.push_back(wire->name.str());
+ }
+
+ if (tempinduct)
+ {
+ if (loopcount > 0 || max_undef)
+ log_cmd_error("The options -max, -all, and -max_undef are not supported for temporal induction proofs!\n");
+
+ SatHelper basecase(design, module, enable_undef);
+ SatHelper inductstep(design, module, enable_undef);
+
+ basecase.sets = sets;
+ basecase.set_assumes = set_assumes;
+ basecase.prove = prove;
+ basecase.prove_x = prove_x;
+ basecase.prove_asserts = prove_asserts;
+ basecase.sets_at = sets_at;
+ basecase.unsets_at = unsets_at;
+ basecase.shows = shows;
+ basecase.timeout = timeout;
+ basecase.sets_def = sets_def;
+ basecase.sets_any_undef = sets_any_undef;
+ basecase.sets_all_undef = sets_all_undef;
+ basecase.sets_def_at = sets_def_at;
+ basecase.sets_any_undef_at = sets_any_undef_at;
+ basecase.sets_all_undef_at = sets_all_undef_at;
+ basecase.sets_init = sets_init;
+ basecase.set_init_def = set_init_def;
+ basecase.set_init_undef = set_init_undef;
+ basecase.set_init_zero = set_init_zero;
+ basecase.satgen.ignore_div_by_zero = ignore_div_by_zero;
+ basecase.ignore_unknown_cells = ignore_unknown_cells;
+
+ for (int timestep = 1; timestep <= seq_len; timestep++)
+ if (!tempinduct_inductonly)
+ basecase.setup(timestep, timestep == 1);
+
+ inductstep.sets = sets;
+ inductstep.set_assumes = set_assumes;
+ inductstep.prove = prove;
+ inductstep.prove_x = prove_x;
+ inductstep.prove_asserts = prove_asserts;
+ inductstep.shows = shows;
+ inductstep.timeout = timeout;
+ inductstep.sets_def = sets_def;
+ inductstep.sets_any_undef = sets_any_undef;
+ inductstep.sets_all_undef = sets_all_undef;
+ inductstep.satgen.ignore_div_by_zero = ignore_div_by_zero;
+ inductstep.ignore_unknown_cells = ignore_unknown_cells;
+
+ if (!tempinduct_baseonly) {
+ inductstep.setup(1);
+ inductstep.ez->assume(inductstep.setup_proof(1));
+ }
+
+ if (tempinduct_def) {
+ std::vector<int> undef_state = inductstep.satgen.importUndefSigSpec(inductstep.satgen.initial_state.export_all(), 1);
+ inductstep.ez->assume(inductstep.ez->NOT(inductstep.ez->expression(ezSAT::OpOr, undef_state)));
+ }
+
+ for (int inductlen = 1; inductlen <= maxsteps || maxsteps == 0; inductlen++)
+ {
+ log("\n** Trying induction with length %d **\n", inductlen);
+
+ // phase 1: proving base case
+
+ if (!tempinduct_inductonly)
+ {
+ basecase.setup(seq_len + inductlen, seq_len + inductlen == 1);
+ int property = basecase.setup_proof(seq_len + inductlen);
+ basecase.generate_model();
+
+ if (inductlen > 1)
+ basecase.force_unique_state(seq_len + 1, seq_len + inductlen);
+
+ if (tempinduct_skip < inductlen)
+ {
+ log("\n[base case %d] Solving problem with %d variables and %d clauses..\n",
+ inductlen, basecase.ez->numCnfVariables(), basecase.ez->numCnfClauses());
+ log_flush();
+
+ if (basecase.solve(basecase.ez->NOT(property))) {
+ log("SAT temporal induction proof finished - model found for base case: FAIL!\n");
+ print_proof_failed();
+ basecase.print_model();
+ if(!vcd_file_name.empty())
+ basecase.dump_model_to_vcd(vcd_file_name);
+ if(!json_file_name.empty())
+ basecase.dump_model_to_json(json_file_name);
+ goto tip_failed;
+ }
+
+ if (basecase.gotTimeout)
+ goto timeout;
+
+ log("Base case for induction length %d proven.\n", inductlen);
+ }
+ else
+ {
+ log("\n[base case %d] Skipping prove for this step (-tempinduct-skip %d).",
+ inductlen, tempinduct_skip);
+ log("\n[base case %d] Problem size so far: %d variables and %d clauses.\n",
+ inductlen, basecase.ez->numCnfVariables(), basecase.ez->numCnfClauses());
+ }
+ basecase.ez->assume(property);
+ }
+
+ // phase 2: proving induction step
+
+ if (!tempinduct_baseonly)
+ {
+ inductstep.setup(inductlen + 1);
+ int property = inductstep.setup_proof(inductlen + 1);
+ inductstep.generate_model();
+
+ if (inductlen > 1)
+ inductstep.force_unique_state(1, inductlen + 1);
+
+ if (inductlen <= tempinduct_skip || inductlen <= initsteps || inductlen % stepsize != 0)
+ {
+ if (inductlen < tempinduct_skip)
+ log("\n[induction step %d] Skipping prove for this step (-tempinduct-skip %d).",
+ inductlen, tempinduct_skip);
+ if (inductlen < initsteps)
+ log("\n[induction step %d] Skipping prove for this step (-initsteps %d).",
+ inductlen, tempinduct_skip);
+ if (inductlen % stepsize != 0)
+ log("\n[induction step %d] Skipping prove for this step (-stepsize %d).",
+ inductlen, stepsize);
+ log("\n[induction step %d] Problem size so far: %d variables and %d clauses.\n",
+ inductlen, inductstep.ez->numCnfVariables(), inductstep.ez->numCnfClauses());
+ inductstep.ez->assume(property);
+ }
+ else
+ {
+ if (!cnf_file_name.empty())
+ {
+ FILE *f = fopen(cnf_file_name.c_str(), "w");
+ if (!f)
+ log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno));
+
+ log("Dumping CNF to file `%s'.\n", cnf_file_name.c_str());
+ cnf_file_name.clear();
+
+ inductstep.ez->printDIMACS(f, false);
+ fclose(f);
+ }
+
+ log("\n[induction step %d] Solving problem with %d variables and %d clauses..\n",
+ inductlen, inductstep.ez->numCnfVariables(), inductstep.ez->numCnfClauses());
+ log_flush();
+
+ if (!inductstep.solve(inductstep.ez->NOT(property))) {
+ if (inductstep.gotTimeout)
+ goto timeout;
+ log("Induction step proven: SUCCESS!\n");
+ print_qed();
+ goto tip_success;
+ }
+
+ log("Induction step failed. Incrementing induction length.\n");
+ inductstep.ez->assume(property);
+ inductstep.print_model();
+ }
+ }
+ }
+
+ if (tempinduct_baseonly) {
+ log("\nReached maximum number of time steps -> proved base case for %d steps: SUCCESS!\n", maxsteps);
+ goto tip_success;
+ }
+
+ log("\nReached maximum number of time steps -> proof failed.\n");
+ if(!vcd_file_name.empty())
+ inductstep.dump_model_to_vcd(vcd_file_name);
+ if(!json_file_name.empty())
+ inductstep.dump_model_to_json(json_file_name);
+ print_proof_failed();
+
+ tip_failed:
+ if (verify) {
+ log("\n");
+ log_error("Called with -verify and proof did fail!\n");
+ }
+
+ if (0)
+ tip_success:
+ if (falsify) {
+ log("\n");
+ log_error("Called with -falsify and proof did succeed!\n");
+ }
+ }
+ else
+ {
+ if (maxsteps > 0)
+ log_cmd_error("The options -maxsteps is only supported for temporal induction proofs!\n");
+
+ SatHelper sathelper(design, module, enable_undef);
+
+ sathelper.sets = sets;
+ sathelper.set_assumes = set_assumes;
+ sathelper.prove = prove;
+ sathelper.prove_x = prove_x;
+ sathelper.prove_asserts = prove_asserts;
+ sathelper.sets_at = sets_at;
+ sathelper.unsets_at = unsets_at;
+ sathelper.shows = shows;
+ sathelper.timeout = timeout;
+ sathelper.sets_def = sets_def;
+ sathelper.sets_any_undef = sets_any_undef;
+ sathelper.sets_all_undef = sets_all_undef;
+ sathelper.sets_def_at = sets_def_at;
+ sathelper.sets_any_undef_at = sets_any_undef_at;
+ sathelper.sets_all_undef_at = sets_all_undef_at;
+ sathelper.sets_init = sets_init;
+ sathelper.set_init_def = set_init_def;
+ sathelper.set_init_undef = set_init_undef;
+ sathelper.set_init_zero = set_init_zero;
+ sathelper.satgen.ignore_div_by_zero = ignore_div_by_zero;
+ sathelper.ignore_unknown_cells = ignore_unknown_cells;
+
+ if (seq_len == 0) {
+ sathelper.setup();
+ if (sathelper.prove.size() || sathelper.prove_x.size() || sathelper.prove_asserts)
+ sathelper.ez->assume(sathelper.ez->NOT(sathelper.setup_proof()));
+ } else {
+ std::vector<int> prove_bits;
+ for (int timestep = 1; timestep <= seq_len; timestep++) {
+ sathelper.setup(timestep, timestep == 1);
+ if (sathelper.prove.size() || sathelper.prove_x.size() || sathelper.prove_asserts)
+ if (timestep > prove_skip)
+ prove_bits.push_back(sathelper.setup_proof(timestep));
+ }
+ if (sathelper.prove.size() || sathelper.prove_x.size() || sathelper.prove_asserts)
+ sathelper.ez->assume(sathelper.ez->NOT(sathelper.ez->expression(ezSAT::OpAnd, prove_bits)));
+ }
+ sathelper.generate_model();
+
+ if (!cnf_file_name.empty())
+ {
+ FILE *f = fopen(cnf_file_name.c_str(), "w");
+ if (!f)
+ log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno));
+
+ log("Dumping CNF to file `%s'.\n", cnf_file_name.c_str());
+ cnf_file_name.clear();
+
+ sathelper.ez->printDIMACS(f, false);
+ fclose(f);
+ }
+
+ int rerun_counter = 0;
+
+ rerun_solver:
+ log("\nSolving problem with %d variables and %d clauses..\n",
+ sathelper.ez->numCnfVariables(), sathelper.ez->numCnfClauses());
+ log_flush();
+
+ if (sathelper.solve())
+ {
+ if (max_undef) {
+ log("SAT model found. maximizing number of undefs.\n");
+ sathelper.maximize_undefs();
+ }
+
+ if (!prove.size() && !prove_x.size() && !prove_asserts) {
+ log("SAT solving finished - model found:\n");
+ } else {
+ log("SAT proof finished - model found: FAIL!\n");
+ print_proof_failed();
+ }
+
+ sathelper.print_model();
+
+ if(!vcd_file_name.empty())
+ sathelper.dump_model_to_vcd(vcd_file_name);
+ if(!json_file_name.empty())
+ sathelper.dump_model_to_json(json_file_name);
+
+ if (loopcount != 0) {
+ loopcount--, rerun_counter++;
+ sathelper.invalidate_model(max_undef);
+ goto rerun_solver;
+ }
+
+ if (!prove.size() && !prove_x.size() && !prove_asserts) {
+ if (falsify) {
+ log("\n");
+ log_error("Called with -falsify and found a model!\n");
+ }
+ } else {
+ if (verify) {
+ log("\n");
+ log_error("Called with -verify and proof did fail!\n");
+ }
+ }
+ }
+ else
+ {
+ if (sathelper.gotTimeout)
+ goto timeout;
+ if (rerun_counter)
+ log("SAT solving finished - no more models found (after %d distinct solutions).\n", rerun_counter);
+ else if (!prove.size() && !prove_x.size() && !prove_asserts) {
+ log("SAT solving finished - no model found.\n");
+ if (verify) {
+ log("\n");
+ log_error("Called with -verify and found no model!\n");
+ }
+ } else {
+ log("SAT proof finished - no model found: SUCCESS!\n");
+ print_qed();
+ if (falsify) {
+ log("\n");
+ log_error("Called with -falsify and proof did succeed!\n");
+ }
+ }
+ }
+
+ if (!prove.size() && !prove_x.size() && !prove_asserts) {
+ if (falsify && rerun_counter) {
+ log("\n");
+ log_error("Called with -falsify and found a model!\n");
+ }
+ } else {
+ if (verify && rerun_counter) {
+ log("\n");
+ log_error("Called with -verify and proof did fail!\n");
+ }
+ }
+ }
+
+ if (0) {
+ timeout:
+ log("Interrupted SAT solver: TIMEOUT!\n");
+ print_timeout();
+ if (fail_on_timeout)
+ log_error("Called with -verify and proof did time out!\n");
+ }
+ }
+} SatPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/.gitignore b/passes/techmap/.gitignore
new file mode 100644
index 00000000..e6dcc6bc
--- /dev/null
+++ b/passes/techmap/.gitignore
@@ -0,0 +1 @@
+techmap.inc
diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc
new file mode 100644
index 00000000..311a1af9
--- /dev/null
+++ b/passes/techmap/Makefile.inc
@@ -0,0 +1,57 @@
+
+OBJS += passes/techmap/techmap.o
+OBJS += passes/techmap/simplemap.o
+OBJS += passes/techmap/dfflibmap.o
+OBJS += passes/techmap/maccmap.o
+OBJS += passes/techmap/libparse.o
+
+ifeq ($(ENABLE_ABC),1)
+OBJS += passes/techmap/abc.o
+ifneq ($(ABCEXTERNAL),)
+passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
+endif
+endif
+
+ifneq ($(SMALL),1)
+OBJS += passes/techmap/iopadmap.o
+OBJS += passes/techmap/hilomap.o
+OBJS += passes/techmap/extract.o
+OBJS += passes/techmap/alumacc.o
+OBJS += passes/techmap/dff2dffe.o
+OBJS += passes/techmap/dffinit.o
+OBJS += passes/techmap/pmuxtree.o
+OBJS += passes/techmap/muxcover.o
+OBJS += passes/techmap/aigmap.o
+OBJS += passes/techmap/tribuf.o
+OBJS += passes/techmap/lut2mux.o
+OBJS += passes/techmap/nlutmap.o
+OBJS += passes/techmap/dffsr2dff.o
+OBJS += passes/techmap/shregmap.o
+OBJS += passes/techmap/deminout.o
+OBJS += passes/techmap/insbuf.o
+OBJS += passes/techmap/attrmvcp.o
+OBJS += passes/techmap/attrmap.o
+OBJS += passes/techmap/zinit.o
+endif
+
+GENFILES += passes/techmap/techmap.inc
+
+passes/techmap/techmap.inc: techlibs/common/techmap.v
+ $(Q) mkdir -p $(dir $@)
+ $(P) echo "// autogenerated from $<" > $@.new
+ $(Q) echo "static char stdcells_code[] = {" >> $@.new
+ $(Q) od -v -td1 -An $< | $(SED) -e 's/[0-9][0-9]*/&,/g' >> $@.new
+ $(Q) echo "0};" >> $@.new
+ $(Q) mv $@.new $@
+
+passes/techmap/techmap.o: passes/techmap/techmap.inc
+
+ifneq ($(CONFIG),emcc)
+TARGETS += yosys-filterlib$(EXE)
+EXTRA_OBJS += passes/techmap/filterlib.o
+
+yosys-filterlib$(EXE): passes/techmap/filterlib.o
+ $(Q) mkdir -p $(dir $@)
+ $(P) $(LD) -o yosys-filterlib$(EXE) $(LDFLAGS) $^ $(LDLIBS)
+endif
+
diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc
new file mode 100644
index 00000000..cc79296c
--- /dev/null
+++ b/passes/techmap/abc.cc
@@ -0,0 +1,1666 @@
+/*
+ * 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.
+ *
+ */
+
+// [[CITE]] ABC
+// Berkeley Logic Synthesis and Verification Group, ABC: A System for Sequential Synthesis and Verification
+// http://www.eecs.berkeley.edu/~alanmi/abc/
+
+// [[CITE]] Berkeley Logic Interchange Format (BLIF)
+// University of California. Berkeley. July 28, 1992
+// http://www.ece.cmu.edu/~ee760/760docs/blif.pdf
+
+// [[CITE]] Kahn's Topological sorting algorithm
+// Kahn, Arthur B. (1962), "Topological sorting of large networks", Communications of the ACM 5 (11): 558-562, doi:10.1145/368996.369025
+// http://en.wikipedia.org/wiki/Topological_sorting
+
+#define ABC_COMMAND_LIB "strash; dc2; scorr; ifraig; retime -o {D}; strash; dch -f; map {D}"
+#define ABC_COMMAND_CTR "strash; dc2; scorr; ifraig; retime -o {D}; strash; dch -f; map {D}; buffer; upsize {D}; dnsize {D}; stime -p"
+#define ABC_COMMAND_LUT "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; if; mfs"
+#define ABC_COMMAND_SOP "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; cover {I} {P}"
+#define ABC_COMMAND_DFL "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; map"
+
+#define ABC_FAST_COMMAND_LIB "retime -o {D}; map {D}"
+#define ABC_FAST_COMMAND_CTR "retime -o {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p"
+#define ABC_FAST_COMMAND_LUT "retime -o; if"
+#define ABC_FAST_COMMAND_SOP "retime -o; cover -I {I} -P {P}"
+#define ABC_FAST_COMMAND_DFL "retime -o; map"
+
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
+#include "kernel/cost.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <cerrno>
+#include <sstream>
+#include <climits>
+
+#ifndef _WIN32
+# include <unistd.h>
+# include <dirent.h>
+#endif
+
+#include "frontends/blif/blifparse.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+enum class gate_type_t {
+ G_NONE,
+ G_FF,
+ G_BUF,
+ G_NOT,
+ G_AND,
+ G_NAND,
+ G_OR,
+ G_NOR,
+ G_XOR,
+ G_XNOR,
+ G_MUX,
+ G_AOI3,
+ G_OAI3,
+ G_AOI4,
+ G_OAI4
+};
+
+#define G(_name) gate_type_t::G_ ## _name
+
+struct gate_t
+{
+ int id;
+ gate_type_t type;
+ int in1, in2, in3, in4;
+ bool is_port;
+ RTLIL::SigBit bit;
+};
+
+bool map_mux4;
+bool map_mux8;
+bool map_mux16;
+
+bool markgroups;
+int map_autoidx;
+SigMap assign_map;
+RTLIL::Module *module;
+std::vector<gate_t> signal_list;
+std::map<RTLIL::SigBit, int> signal_map;
+pool<std::string> enabled_gates;
+
+bool clk_polarity, en_polarity;
+RTLIL::SigSpec clk_sig, en_sig;
+
+int map_signal(RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, int in2 = -1, int in3 = -1, int in4 = -1)
+{
+ assign_map.apply(bit);
+
+ if (signal_map.count(bit) == 0) {
+ gate_t gate;
+ gate.id = signal_list.size();
+ gate.type = G(NONE);
+ gate.in1 = -1;
+ gate.in2 = -1;
+ gate.in3 = -1;
+ gate.in4 = -1;
+ gate.is_port = false;
+ gate.bit = bit;
+ signal_list.push_back(gate);
+ signal_map[bit] = gate.id;
+ }
+
+ gate_t &gate = signal_list[signal_map[bit]];
+
+ if (gate_type != G(NONE))
+ gate.type = gate_type;
+ if (in1 >= 0)
+ gate.in1 = in1;
+ if (in2 >= 0)
+ gate.in2 = in2;
+ if (in3 >= 0)
+ gate.in3 = in3;
+ if (in4 >= 0)
+ gate.in4 = in4;
+
+ return gate.id;
+}
+
+void mark_port(RTLIL::SigSpec sig)
+{
+ for (auto &bit : assign_map(sig))
+ if (bit.wire != NULL && signal_map.count(bit) > 0)
+ signal_list[signal_map[bit]].is_port = true;
+}
+
+void extract_cell(RTLIL::Cell *cell, bool keepff)
+{
+ if (cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_")
+ {
+ if (clk_polarity != (cell->type == "$_DFF_P_"))
+ return;
+ if (clk_sig != assign_map(cell->getPort("\\C")))
+ return;
+ if (GetSize(en_sig) != 0)
+ return;
+ goto matching_dff;
+ }
+
+ if (cell->type == "$_DFFE_NN_" || cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_")
+ {
+ if (clk_polarity != (cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_"))
+ return;
+ if (en_polarity != (cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PP_"))
+ return;
+ if (clk_sig != assign_map(cell->getPort("\\C")))
+ return;
+ if (en_sig != assign_map(cell->getPort("\\E")))
+ return;
+ goto matching_dff;
+ }
+
+ if (0) {
+ matching_dff:
+ RTLIL::SigSpec sig_d = cell->getPort("\\D");
+ RTLIL::SigSpec sig_q = cell->getPort("\\Q");
+
+ if (keepff)
+ for (auto &c : sig_q.chunks())
+ if (c.wire != NULL)
+ c.wire->attributes["\\keep"] = 1;
+
+ assign_map.apply(sig_d);
+ assign_map.apply(sig_q);
+
+ map_signal(sig_q, G(FF), map_signal(sig_d));
+
+ module->remove(cell);
+ return;
+ }
+
+ if (cell->type.in("$_BUF_", "$_NOT_"))
+ {
+ RTLIL::SigSpec sig_a = cell->getPort("\\A");
+ RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+
+ assign_map.apply(sig_a);
+ assign_map.apply(sig_y);
+
+ map_signal(sig_y, cell->type == "$_BUF_" ? G(BUF) : G(NOT), map_signal(sig_a));
+
+ module->remove(cell);
+ return;
+ }
+
+ if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_"))
+ {
+ RTLIL::SigSpec sig_a = cell->getPort("\\A");
+ RTLIL::SigSpec sig_b = cell->getPort("\\B");
+ RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+
+ assign_map.apply(sig_a);
+ assign_map.apply(sig_b);
+ assign_map.apply(sig_y);
+
+ int mapped_a = map_signal(sig_a);
+ int mapped_b = map_signal(sig_b);
+
+ if (cell->type == "$_AND_")
+ map_signal(sig_y, G(AND), mapped_a, mapped_b);
+ else if (cell->type == "$_NAND_")
+ map_signal(sig_y, G(NAND), mapped_a, mapped_b);
+ else if (cell->type == "$_OR_")
+ map_signal(sig_y, G(OR), mapped_a, mapped_b);
+ else if (cell->type == "$_NOR_")
+ map_signal(sig_y, G(NOR), mapped_a, mapped_b);
+ else if (cell->type == "$_XOR_")
+ map_signal(sig_y, G(XOR), mapped_a, mapped_b);
+ else if (cell->type == "$_XNOR_")
+ map_signal(sig_y, G(XNOR), mapped_a, mapped_b);
+ else
+ log_abort();
+
+ module->remove(cell);
+ return;
+ }
+
+ if (cell->type == "$_MUX_")
+ {
+ RTLIL::SigSpec sig_a = cell->getPort("\\A");
+ RTLIL::SigSpec sig_b = cell->getPort("\\B");
+ RTLIL::SigSpec sig_s = cell->getPort("\\S");
+ RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+
+ assign_map.apply(sig_a);
+ assign_map.apply(sig_b);
+ assign_map.apply(sig_s);
+ assign_map.apply(sig_y);
+
+ int mapped_a = map_signal(sig_a);
+ int mapped_b = map_signal(sig_b);
+ int mapped_s = map_signal(sig_s);
+
+ map_signal(sig_y, G(MUX), mapped_a, mapped_b, mapped_s);
+
+ module->remove(cell);
+ return;
+ }
+
+ if (cell->type.in("$_AOI3_", "$_OAI3_"))
+ {
+ RTLIL::SigSpec sig_a = cell->getPort("\\A");
+ RTLIL::SigSpec sig_b = cell->getPort("\\B");
+ RTLIL::SigSpec sig_c = cell->getPort("\\C");
+ RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+
+ assign_map.apply(sig_a);
+ assign_map.apply(sig_b);
+ assign_map.apply(sig_c);
+ assign_map.apply(sig_y);
+
+ int mapped_a = map_signal(sig_a);
+ int mapped_b = map_signal(sig_b);
+ int mapped_c = map_signal(sig_c);
+
+ map_signal(sig_y, cell->type == "$_AOI3_" ? G(AOI3) : G(OAI3), mapped_a, mapped_b, mapped_c);
+
+ module->remove(cell);
+ return;
+ }
+
+ if (cell->type.in("$_AOI4_", "$_OAI4_"))
+ {
+ RTLIL::SigSpec sig_a = cell->getPort("\\A");
+ RTLIL::SigSpec sig_b = cell->getPort("\\B");
+ RTLIL::SigSpec sig_c = cell->getPort("\\C");
+ RTLIL::SigSpec sig_d = cell->getPort("\\D");
+ RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+
+ assign_map.apply(sig_a);
+ assign_map.apply(sig_b);
+ assign_map.apply(sig_c);
+ assign_map.apply(sig_d);
+ assign_map.apply(sig_y);
+
+ int mapped_a = map_signal(sig_a);
+ int mapped_b = map_signal(sig_b);
+ int mapped_c = map_signal(sig_c);
+ int mapped_d = map_signal(sig_d);
+
+ map_signal(sig_y, cell->type == "$_AOI4_" ? G(AOI4) : G(OAI4), mapped_a, mapped_b, mapped_c, mapped_d);
+
+ module->remove(cell);
+ return;
+ }
+}
+
+std::string remap_name(RTLIL::IdString abc_name)
+{
+ std::stringstream sstr;
+ sstr << "$abc$" << map_autoidx << "$" << abc_name.substr(1);
+ return sstr.str();
+}
+
+void dump_loop_graph(FILE *f, int &nr, std::map<int, std::set<int>> &edges, std::set<int> &workpool, std::vector<int> &in_counts)
+{
+ if (f == NULL)
+ return;
+
+ log("Dumping loop state graph to slide %d.\n", ++nr);
+
+ fprintf(f, "digraph \"slide%d\" {\n", nr);
+ fprintf(f, " label=\"slide%d\";\n", nr);
+ fprintf(f, " rankdir=\"TD\";\n");
+
+ std::set<int> nodes;
+ for (auto &e : edges) {
+ nodes.insert(e.first);
+ for (auto n : e.second)
+ nodes.insert(n);
+ }
+
+ for (auto n : nodes)
+ fprintf(f, " 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, "}\n");
+}
+
+void handle_loops()
+{
+ // http://en.wikipedia.org/wiki/Topological_sorting
+ // (Kahn, Arthur B. (1962), "Topological sorting of large networks")
+
+ std::map<int, std::set<int>> edges;
+ std::vector<int> in_edges_count(signal_list.size());
+ std::set<int> workpool;
+
+ FILE *dot_f = NULL;
+ int dot_nr = 0;
+
+ // uncomment for troubleshooting the loop detection code
+ // dot_f = fopen("test.dot", "w");
+
+ for (auto &g : signal_list) {
+ if (g.type == G(NONE) || g.type == G(FF)) {
+ workpool.insert(g.id);
+ } else {
+ if (g.in1 >= 0) {
+ edges[g.in1].insert(g.id);
+ in_edges_count[g.id]++;
+ }
+ if (g.in2 >= 0 && g.in2 != g.in1) {
+ edges[g.in2].insert(g.id);
+ in_edges_count[g.id]++;
+ }
+ if (g.in3 >= 0 && g.in3 != g.in2 && g.in3 != g.in1) {
+ edges[g.in3].insert(g.id);
+ in_edges_count[g.id]++;
+ }
+ if (g.in4 >= 0 && g.in4 != g.in3 && g.in4 != g.in2 && g.in4 != g.in1) {
+ edges[g.in4].insert(g.id);
+ in_edges_count[g.id]++;
+ }
+ }
+ }
+
+ dump_loop_graph(dot_f, dot_nr, edges, workpool, in_edges_count);
+
+ while (workpool.size() > 0)
+ {
+ int id = *workpool.begin();
+ workpool.erase(id);
+
+ // log("Removing non-loop node %d from graph: %s\n", id, log_signal(signal_list[id].bit));
+
+ for (int id2 : edges[id]) {
+ log_assert(in_edges_count[id2] > 0);
+ if (--in_edges_count[id2] == 0)
+ workpool.insert(id2);
+ }
+ edges.erase(id);
+
+ dump_loop_graph(dot_f, dot_nr, edges, workpool, in_edges_count);
+
+ while (workpool.size() == 0)
+ {
+ if (edges.size() == 0)
+ break;
+
+ int id1 = edges.begin()->first;
+
+ for (auto &edge_it : edges) {
+ int id2 = edge_it.first;
+ RTLIL::Wire *w1 = signal_list[id1].bit.wire;
+ RTLIL::Wire *w2 = signal_list[id2].bit.wire;
+ if (w1 == NULL)
+ id1 = id2;
+ else if (w2 == NULL)
+ continue;
+ else if (w1->name[0] == '$' && w2->name[0] == '\\')
+ id1 = id2;
+ else if (w1->name[0] == '\\' && w2->name[0] == '$')
+ continue;
+ else if (edges[id1].size() < edges[id2].size())
+ id1 = id2;
+ else if (edges[id1].size() > edges[id2].size())
+ continue;
+ else if (w2->name.str() < w1->name.str())
+ id1 = id2;
+ }
+
+ if (edges[id1].size() == 0) {
+ edges.erase(id1);
+ continue;
+ }
+
+ log_assert(signal_list[id1].bit.wire != NULL);
+
+ std::stringstream sstr;
+ sstr << "$abcloop$" << (autoidx++);
+ RTLIL::Wire *wire = module->addWire(sstr.str());
+
+ bool first_line = true;
+ for (int id2 : edges[id1]) {
+ if (first_line)
+ log("Breaking loop using new signal %s: %s -> %s\n", log_signal(RTLIL::SigSpec(wire)),
+ log_signal(signal_list[id1].bit), log_signal(signal_list[id2].bit));
+ else
+ log(" %*s %s -> %s\n", int(strlen(log_signal(RTLIL::SigSpec(wire)))), "",
+ log_signal(signal_list[id1].bit), log_signal(signal_list[id2].bit));
+ first_line = false;
+ }
+
+ int id3 = map_signal(RTLIL::SigSpec(wire));
+ signal_list[id1].is_port = true;
+ signal_list[id3].is_port = true;
+ log_assert(id3 == int(in_edges_count.size()));
+ in_edges_count.push_back(0);
+ workpool.insert(id3);
+
+ for (int id2 : edges[id1]) {
+ if (signal_list[id2].in1 == id1)
+ signal_list[id2].in1 = id3;
+ if (signal_list[id2].in2 == id1)
+ signal_list[id2].in2 = id3;
+ if (signal_list[id2].in3 == id1)
+ signal_list[id2].in3 = id3;
+ if (signal_list[id2].in4 == id1)
+ signal_list[id2].in4 = id3;
+ }
+ edges[id1].swap(edges[id3]);
+
+ module->connect(RTLIL::SigSig(signal_list[id3].bit, signal_list[id1].bit));
+ dump_loop_graph(dot_f, dot_nr, edges, workpool, in_edges_count);
+ }
+ }
+
+ if (dot_f != NULL)
+ fclose(dot_f);
+}
+
+std::string add_echos_to_abc_cmd(std::string str)
+{
+ std::string new_str, token;
+ for (size_t i = 0; i < str.size(); i++) {
+ token += str[i];
+ if (str[i] == ';') {
+ while (i+1 < str.size() && str[i+1] == ' ')
+ i++;
+ new_str += "echo + " + token + " " + token + " ";
+ token.clear();
+ }
+ }
+
+ if (!token.empty()) {
+ if (!new_str.empty())
+ new_str += "echo + " + token + "; ";
+ new_str += token;
+ }
+
+ return new_str;
+}
+
+std::string fold_abc_cmd(std::string str)
+{
+ std::string token, new_str = " ";
+ int char_counter = 10;
+
+ for (size_t i = 0; i <= str.size(); i++) {
+ if (i < str.size())
+ token += str[i];
+ if (i == str.size() || str[i] == ';') {
+ if (char_counter + token.size() > 75)
+ new_str += "\n ", char_counter = 14;
+ new_str += token, char_counter += token.size();
+ token.clear();
+ }
+ }
+
+ return new_str;
+}
+
+std::string replace_tempdir(std::string text, std::string tempdir_name, bool show_tempdir)
+{
+ if (show_tempdir)
+ return text;
+
+ while (1) {
+ size_t pos = text.find(tempdir_name);
+ if (pos == std::string::npos)
+ break;
+ text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(tempdir_name));
+ }
+
+ std::string selfdir_name = proc_self_dirname();
+ while (1) {
+ size_t pos = text.find(selfdir_name);
+ if (pos == std::string::npos)
+ break;
+ text = text.substr(0, pos) + "<yosys-exe-dir>/" + text.substr(pos + GetSize(selfdir_name));
+ }
+
+ return text;
+}
+
+struct abc_output_filter
+{
+ bool got_cr;
+ int escape_seq_state;
+ std::string linebuf;
+ std::string tempdir_name;
+ bool show_tempdir;
+
+ abc_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir)
+ {
+ got_cr = false;
+ escape_seq_state = 0;
+ }
+
+ void next_char(char ch)
+ {
+ if (escape_seq_state == 0 && ch == '\033') {
+ escape_seq_state = 1;
+ return;
+ }
+ if (escape_seq_state == 1) {
+ escape_seq_state = ch == '[' ? 2 : 0;
+ return;
+ }
+ if (escape_seq_state == 2) {
+ if ((ch < '0' || '9' < ch) && ch != ';')
+ escape_seq_state = 0;
+ return;
+ }
+ escape_seq_state = 0;
+ if (ch == '\r') {
+ got_cr = true;
+ return;
+ }
+ if (ch == '\n') {
+ log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir).c_str());
+ got_cr = false, linebuf.clear();
+ return;
+ }
+ if (got_cr)
+ got_cr = false, linebuf.clear();
+ linebuf += ch;
+ }
+
+ void next_line(const std::string &line)
+ {
+ for (char ch : line)
+ next_char(ch);
+ }
+};
+
+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, bool fast_mode,
+ const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode)
+{
+ module = current_module;
+ map_autoidx = autoidx++;
+
+ signal_map.clear();
+ signal_list.clear();
+
+ if (clk_str != "$")
+ {
+ assign_map.set(module);
+
+ clk_polarity = true;
+ clk_sig = RTLIL::SigSpec();
+
+ en_polarity = true;
+ en_sig = RTLIL::SigSpec();
+ }
+
+ std::string tempdir_name = "/tmp/yosys-abc-XXXXXX";
+ if (!cleanup)
+ tempdir_name[0] = tempdir_name[4] = '_';
+ tempdir_name = make_temp_dir(tempdir_name);
+ log_header(design, "Extracting gate netlist of module `%s' to `%s/input.blif'..\n",
+ module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str());
+
+ std::string abc_script = stringf("read_blif %s/input.blif; ", tempdir_name.c_str());
+
+ if (!liberty_file.empty()) {
+ abc_script += stringf("read_lib -w %s; ", liberty_file.c_str());
+ if (!constr_file.empty())
+ abc_script += stringf("read_constr -v %s; ", constr_file.c_str());
+ } else
+ if (!lut_costs.empty())
+ abc_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str());
+ else
+ abc_script += stringf("read_library %s/stdcells.genlib; ", tempdir_name.c_str());
+
+ if (!script_file.empty()) {
+ if (script_file[0] == '+') {
+ for (size_t i = 1; i < script_file.size(); i++)
+ if (script_file[i] == '\'')
+ abc_script += "'\\''";
+ else if (script_file[i] == ',')
+ abc_script += " ";
+ else
+ abc_script += script_file[i];
+ } else
+ abc_script += stringf("source %s", script_file.c_str());
+ } else if (!lut_costs.empty()) {
+ bool all_luts_cost_same = true;
+ for (int this_cost : lut_costs)
+ if (this_cost != lut_costs.front())
+ all_luts_cost_same = false;
+ abc_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT;
+ if (all_luts_cost_same && !fast_mode)
+ abc_script += "; lutpack";
+ } else if (!liberty_file.empty())
+ abc_script += constr_file.empty() ? (fast_mode ? ABC_FAST_COMMAND_LIB : ABC_COMMAND_LIB) : (fast_mode ? ABC_FAST_COMMAND_CTR : ABC_COMMAND_CTR);
+ else if (sop_mode)
+ abc_script += fast_mode ? ABC_FAST_COMMAND_SOP : ABC_COMMAND_SOP;
+ else
+ abc_script += fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL;
+
+ for (size_t pos = abc_script.find("{D}"); pos != std::string::npos; pos = abc_script.find("{D}", pos))
+ abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3);
+
+ for (size_t pos = abc_script.find("{I}"); pos != std::string::npos; pos = abc_script.find("{D}", pos))
+ abc_script = abc_script.substr(0, pos) + sop_inputs + abc_script.substr(pos+3);
+
+ for (size_t pos = abc_script.find("{P}"); pos != std::string::npos; pos = abc_script.find("{D}", pos))
+ abc_script = abc_script.substr(0, pos) + sop_products + abc_script.substr(pos+3);
+
+ abc_script += stringf("; write_blif %s/output.blif", tempdir_name.c_str());
+ abc_script = add_echos_to_abc_cmd(abc_script);
+
+ for (size_t i = 0; i+1 < abc_script.size(); i++)
+ if (abc_script[i] == ';' && abc_script[i+1] == ' ')
+ abc_script[i+1] = '\n';
+
+ FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt");
+ fprintf(f, "%s\n", abc_script.c_str());
+ fclose(f);
+
+ if (!clk_str.empty() && clk_str != "$")
+ {
+ if (clk_str.find(',') != std::string::npos) {
+ int pos = clk_str.find(',');
+ std::string en_str = clk_str.substr(pos+1);
+ clk_str = clk_str.substr(0, pos);
+ if (en_str[0] == '!') {
+ en_polarity = false;
+ en_str = en_str.substr(1);
+ }
+ if (module->wires_.count(RTLIL::escape_id(en_str)) != 0)
+ en_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(en_str)), 0));
+ }
+ if (clk_str[0] == '!') {
+ clk_polarity = false;
+ clk_str = clk_str.substr(1);
+ }
+ if (module->wires_.count(RTLIL::escape_id(clk_str)) != 0)
+ clk_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(clk_str)), 0));
+ }
+
+ if (dff_mode && clk_sig.empty())
+ log_error("Clock domain %s not found.\n", clk_str.c_str());
+
+ if (dff_mode || !clk_str.empty())
+ {
+ if (clk_sig.size() == 0)
+ log("No%s clock domain found. Not extracting any FF cells.\n", clk_str.empty() ? "" : " matching");
+ else {
+ log("Found%s %s clock domain: %s", clk_str.empty() ? "" : " matching", clk_polarity ? "posedge" : "negedge", log_signal(clk_sig));
+ if (en_sig.size() != 0)
+ log(", enabled by %s%s", en_polarity ? "" : "!", log_signal(en_sig));
+ log("\n");
+ }
+ }
+
+ for (auto c : cells)
+ extract_cell(c, keepff);
+
+ for (auto &wire_it : module->wires_) {
+ if (wire_it.second->port_id > 0 || wire_it.second->get_bool_attribute("\\keep"))
+ mark_port(RTLIL::SigSpec(wire_it.second));
+ }
+
+ for (auto &cell_it : module->cells_)
+ for (auto &port_it : cell_it.second->connections())
+ mark_port(port_it.second);
+
+ if (clk_sig.size() != 0)
+ mark_port(clk_sig);
+
+ if (en_sig.size() != 0)
+ mark_port(en_sig);
+
+ handle_loops();
+
+ std::string buffer = stringf("%s/input.blif", tempdir_name.c_str());
+ f = fopen(buffer.c_str(), "wt");
+ if (f == NULL)
+ log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno));
+
+ fprintf(f, ".model netlist\n");
+
+ int count_input = 0;
+ fprintf(f, ".inputs");
+ for (auto &si : signal_list) {
+ if (!si.is_port || si.type != G(NONE))
+ continue;
+ fprintf(f, " n%d", si.id);
+ count_input++;
+ }
+ if (count_input == 0)
+ fprintf(f, " dummy_input\n");
+ fprintf(f, "\n");
+
+ int count_output = 0;
+ fprintf(f, ".outputs");
+ for (auto &si : signal_list) {
+ if (!si.is_port || si.type == G(NONE))
+ continue;
+ fprintf(f, " n%d", si.id);
+ count_output++;
+ }
+ fprintf(f, "\n");
+
+ for (auto &si : signal_list)
+ fprintf(f, "# 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);
+ if (si.bit == RTLIL::State::S1)
+ fprintf(f, "1\n");
+ }
+ }
+
+ 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, "1 1\n");
+ } else if (si.type == G(NOT)) {
+ fprintf(f, ".names n%d 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, "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, "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, "-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, "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, "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, "00 1\n");
+ fprintf(f, "11 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, "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, "-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, "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, "-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, "00-- 1\n");
+ fprintf(f, "--00 1\n");
+ } else if (si.type == G(FF)) {
+ fprintf(f, ".latch n%d n%d\n", si.in1, si.id);
+ } else if (si.type != G(NONE))
+ log_abort();
+ if (si.type != G(NONE))
+ count_gates++;
+ }
+
+ fprintf(f, ".end\n");
+ fclose(f);
+
+ 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");
+
+ buffer = stringf("%s/stdcells.genlib", tempdir_name.c_str());
+ f = fopen(buffer.c_str(), "wt");
+ if (f == NULL)
+ log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno));
+ fprintf(f, "GATE ZERO 1 Y=CONST0;\n");
+ fprintf(f, "GATE ONE 1 Y=CONST1;\n");
+ fprintf(f, "GATE BUF %d Y=A; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_BUF_"));
+ fprintf(f, "GATE NOT %d Y=!A; PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOT_"));
+ if (enabled_gates.empty() || enabled_gates.count("AND"))
+ fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_AND_"));
+ if (enabled_gates.empty() || enabled_gates.count("NAND"))
+ fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NAND_"));
+ if (enabled_gates.empty() || enabled_gates.count("OR"))
+ fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_OR_"));
+ if (enabled_gates.empty() || enabled_gates.count("NOR"))
+ fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOR_"));
+ if (enabled_gates.empty() || enabled_gates.count("XOR"))
+ fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XOR_"));
+ if (enabled_gates.empty() || enabled_gates.count("XNOR"))
+ fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XNOR_"));
+ if (enabled_gates.empty() || enabled_gates.count("AOI3"))
+ fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI3_"));
+ if (enabled_gates.empty() || enabled_gates.count("OAI3"))
+ fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI3_"));
+ if (enabled_gates.empty() || enabled_gates.count("AOI4"))
+ fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI4_"));
+ if (enabled_gates.empty() || enabled_gates.count("OAI4"))
+ fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI4_"));
+ if (enabled_gates.empty() || enabled_gates.count("MUX"))
+ fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_MUX_"));
+ if (map_mux4)
+ fprintf(f, "GATE MUX4 %d Y=(!S*!T*A)+(S*!T*B)+(!S*T*C)+(S*T*D); PIN * UNKNOWN 1 999 1 0 1 0\n", 2*get_cell_cost("$_MUX_"));
+ if (map_mux8)
+ fprintf(f, "GATE MUX8 %d Y=(!S*!T*!U*A)+(S*!T*!U*B)+(!S*T*!U*C)+(S*T*!U*D)+(!S*!T*U*E)+(S*!T*U*F)+(!S*T*U*G)+(S*T*U*H); PIN * UNKNOWN 1 999 1 0 1 0\n", 4*get_cell_cost("$_MUX_"));
+ if (map_mux16)
+ fprintf(f, "GATE MUX16 %d Y=(!S*!T*!U*!V*A)+(S*!T*!U*!V*B)+(!S*T*!U*!V*C)+(S*T*!U*!V*D)+(!S*!T*U*!V*E)+(S*!T*U*!V*F)+(!S*T*U*!V*G)+(S*T*U*!V*H)+(!S*!T*!U*V*I)+(S*!T*!U*V*J)+(!S*T*!U*V*K)+(S*T*!U*V*L)+(!S*!T*U*V*M)+(S*!T*U*V*N)+(!S*T*U*V*O)+(S*T*U*V*P); PIN * UNKNOWN 1 999 1 0 1 0\n", 8*get_cell_cost("$_MUX_"));
+ fclose(f);
+
+ if (!lut_costs.empty()) {
+ buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str());
+ f = fopen(buffer.c_str(), "wt");
+ if (f == NULL)
+ log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno));
+ for (int i = 0; i < GetSize(lut_costs); i++)
+ fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i));
+ fclose(f);
+ }
+
+ buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str());
+ log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str());
+
+ abc_output_filter filt(tempdir_name, show_tempdir);
+ int ret = run_command(buffer, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1));
+ if (ret != 0)
+ log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret);
+
+ buffer = stringf("%s/%s", tempdir_name.c_str(), "output.blif");
+ std::ifstream ifs;
+ ifs.open(buffer);
+ if (ifs.fail())
+ log_error("Can't open ABC output file `%s'.\n", buffer.c_str());
+
+ bool builtin_lib = liberty_file.empty();
+ RTLIL::Design *mapped_design = new RTLIL::Design;
+ parse_blif(mapped_design, ifs, builtin_lib ? "\\DFF" : "\\_dff_", false, sop_mode);
+
+ ifs.close();
+
+ log_header(design, "Re-integrating ABC results.\n");
+ RTLIL::Module *mapped_mod = mapped_design->modules_["\\netlist"];
+ if (mapped_mod == NULL)
+ 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));
+ if (markgroups) wire->attributes["\\abcgroup"] = map_autoidx;
+ design->select(module, wire);
+ }
+
+ std::map<std::string, int> cell_stats;
+ for (auto c : mapped_mod->cells())
+ {
+ if (builtin_lib)
+ {
+ cell_stats[RTLIL::unescape_id(c->type)]++;
+ if (c->type == "\\ZERO" || c->type == "\\ONE") {
+ RTLIL::SigSig conn;
+ conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]);
+ conn.second = RTLIL::SigSpec(c->type == "\\ZERO" ? 0 : 1, 1);
+ module->connect(conn);
+ continue;
+ }
+ if (c->type == "\\BUF") {
+ RTLIL::SigSig conn;
+ conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]);
+ conn.second = RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]);
+ module->connect(conn);
+ continue;
+ }
+ if (c->type == "\\NOT") {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_NOT_");
+ if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
+ cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
+ cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
+ design->select(module, cell);
+ continue;
+ }
+ if (c->type == "\\AND" || c->type == "\\OR" || c->type == "\\XOR" || c->type == "\\NAND" || c->type == "\\NOR" || c->type == "\\XNOR") {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_");
+ if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
+ cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
+ cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
+ cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
+ design->select(module, cell);
+ continue;
+ }
+ if (c->type == "\\MUX") {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX_");
+ if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
+ cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
+ cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
+ cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)]));
+ cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
+ design->select(module, cell);
+ continue;
+ }
+ if (c->type == "\\MUX4") {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX4_");
+ if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
+ cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
+ cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
+ cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)]));
+ cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
+ cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)]));
+ cell->setPort("\\T", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\T").as_wire()->name)]));
+ cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
+ design->select(module, cell);
+ continue;
+ }
+ if (c->type == "\\MUX8") {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX8_");
+ if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
+ cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
+ cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
+ cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)]));
+ cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
+ cell->setPort("\\E", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\E").as_wire()->name)]));
+ cell->setPort("\\F", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\F").as_wire()->name)]));
+ cell->setPort("\\G", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\G").as_wire()->name)]));
+ cell->setPort("\\H", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\H").as_wire()->name)]));
+ cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)]));
+ cell->setPort("\\T", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\T").as_wire()->name)]));
+ cell->setPort("\\U", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\U").as_wire()->name)]));
+ cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
+ design->select(module, cell);
+ continue;
+ }
+ if (c->type == "\\MUX16") {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX16_");
+ if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
+ cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
+ cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
+ cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)]));
+ cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
+ cell->setPort("\\E", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\E").as_wire()->name)]));
+ cell->setPort("\\F", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\F").as_wire()->name)]));
+ cell->setPort("\\G", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\G").as_wire()->name)]));
+ cell->setPort("\\H", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\H").as_wire()->name)]));
+ cell->setPort("\\I", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\I").as_wire()->name)]));
+ cell->setPort("\\J", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\J").as_wire()->name)]));
+ cell->setPort("\\K", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\K").as_wire()->name)]));
+ cell->setPort("\\L", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\L").as_wire()->name)]));
+ cell->setPort("\\M", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\M").as_wire()->name)]));
+ cell->setPort("\\N", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\N").as_wire()->name)]));
+ cell->setPort("\\O", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\O").as_wire()->name)]));
+ cell->setPort("\\P", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\P").as_wire()->name)]));
+ cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)]));
+ cell->setPort("\\T", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\T").as_wire()->name)]));
+ cell->setPort("\\U", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\U").as_wire()->name)]));
+ cell->setPort("\\V", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\V").as_wire()->name)]));
+ cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
+ design->select(module, cell);
+ continue;
+ }
+ if (c->type == "\\AOI3" || c->type == "\\OAI3") {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_");
+ if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
+ cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
+ cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
+ cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)]));
+ cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
+ design->select(module, cell);
+ continue;
+ }
+ if (c->type == "\\AOI4" || c->type == "\\OAI4") {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_");
+ if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
+ cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
+ cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
+ cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)]));
+ cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
+ cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
+ design->select(module, cell);
+ continue;
+ }
+ if (c->type == "\\DFF") {
+ log_assert(clk_sig.size() == 1);
+ RTLIL::Cell *cell;
+ if (en_sig.size() == 0) {
+ cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_");
+ } else {
+ log_assert(en_sig.size() == 1);
+ cell = module->addCell(remap_name(c->name), stringf("$_DFFE_%c%c_", clk_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N'));
+ cell->setPort("\\E", en_sig);
+ }
+ if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
+ cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
+ cell->setPort("\\Q", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Q").as_wire()->name)]));
+ cell->setPort("\\C", clk_sig);
+ design->select(module, cell);
+ continue;
+ }
+ }
+
+ cell_stats[RTLIL::unescape_id(c->type)]++;
+
+ if (c->type == "\\_const0_" || c->type == "\\_const1_") {
+ RTLIL::SigSig conn;
+ conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->connections().begin()->second.as_wire()->name)]);
+ conn.second = RTLIL::SigSpec(c->type == "\\_const0_" ? 0 : 1, 1);
+ module->connect(conn);
+ continue;
+ }
+
+ if (c->type == "\\_dff_") {
+ log_assert(clk_sig.size() == 1);
+ RTLIL::Cell *cell;
+ if (en_sig.size() == 0) {
+ cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_");
+ } else {
+ log_assert(en_sig.size() == 1);
+ cell = module->addCell(remap_name(c->name), stringf("$_DFFE_%c%c_", clk_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N'));
+ cell->setPort("\\E", en_sig);
+ }
+ if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
+ cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
+ cell->setPort("\\Q", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Q").as_wire()->name)]));
+ cell->setPort("\\C", clk_sig);
+ design->select(module, cell);
+ continue;
+ }
+
+ if (c->type == "$lut" && GetSize(c->getPort("\\A")) == 1 && c->getParam("\\LUT").as_int() == 2) {
+ SigSpec my_a = module->wires_[remap_name(c->getPort("\\A").as_wire()->name)];
+ SigSpec my_y = module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)];
+ module->connect(my_y, my_a);
+ continue;
+ }
+
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), c->type);
+ if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
+ cell->parameters = c->parameters;
+ for (auto &conn : c->connections()) {
+ RTLIL::SigSpec newsig;
+ for (auto &c : conn.second.chunks()) {
+ if (c.width == 0)
+ continue;
+ log_assert(c.width == 1);
+ newsig.append(module->wires_[remap_name(c.wire->name)]);
+ }
+ cell->setPort(conn.first, newsig);
+ }
+ design->select(module, cell);
+ }
+
+ for (auto conn : mapped_mod->connections()) {
+ if (!conn.first.is_fully_const())
+ conn.first = RTLIL::SigSpec(module->wires_[remap_name(conn.first.as_wire()->name)]);
+ if (!conn.second.is_fully_const())
+ conn.second = RTLIL::SigSpec(module->wires_[remap_name(conn.second.as_wire()->name)]);
+ module->connect(conn);
+ }
+
+ for (auto &it : cell_stats)
+ log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second);
+ int in_wires = 0, out_wires = 0;
+ for (auto &si : signal_list)
+ if (si.is_port) {
+ char buffer[100];
+ snprintf(buffer, 100, "\\n%d", si.id);
+ RTLIL::SigSig conn;
+ if (si.type != G(NONE)) {
+ conn.first = si.bit;
+ conn.second = RTLIL::SigSpec(module->wires_[remap_name(buffer)]);
+ out_wires++;
+ } else {
+ conn.first = RTLIL::SigSpec(module->wires_[remap_name(buffer)]);
+ conn.second = si.bit;
+ in_wires++;
+ }
+ module->connect(conn);
+ }
+ log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires);
+ log("ABC RESULTS: input signals: %8d\n", in_wires);
+ log("ABC RESULTS: output signals: %8d\n", out_wires);
+
+ delete mapped_design;
+ }
+ else
+ {
+ log("Don't call ABC as there is nothing to map.\n");
+ }
+
+ if (cleanup)
+ {
+ log("Removing temp directory.\n");
+ remove_directory(tempdir_name);
+ }
+
+ log_pop();
+}
+
+struct AbcPass : public Pass {
+ AbcPass() : Pass("abc", "use ABC for technology mapping") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" abc [options] [selection]\n");
+ log("\n");
+ log("This pass uses the ABC tool [1] for technology mapping of yosys's internal gate\n");
+ log("library to a target architecture.\n");
+ log("\n");
+ log(" -exe <command>\n");
+#ifdef ABCEXTERNAL
+ log(" use the specified command instead of \"" ABCEXTERNAL "\" to execute ABC.\n");
+#else
+ log(" use the specified command instead of \"<yosys-bindir>/yosys-abc\" to execute ABC.\n");
+#endif
+ log(" This can e.g. be used to call a specific version of ABC or a wrapper.\n");
+ log("\n");
+ log(" -script <file>\n");
+ log(" use the specified ABC script file instead of the default script.\n");
+ log("\n");
+ log(" if <file> starts with a plus sign (+), then the rest of the filename\n");
+ log(" string is interpreted as the command string to be passed to ABC. The\n");
+ log(" leading plus sign is removed and all commas (,) in the string are\n");
+ log(" replaced with blanks before the string is passed to ABC.\n");
+ log("\n");
+ log(" if no -script parameter is given, the following scripts are used:\n");
+ log("\n");
+ log(" for -liberty without -constr:\n");
+ log("%s\n", fold_abc_cmd(ABC_COMMAND_LIB).c_str());
+ log("\n");
+ log(" for -liberty with -constr:\n");
+ log("%s\n", fold_abc_cmd(ABC_COMMAND_CTR).c_str());
+ log("\n");
+ log(" for -lut/-luts (only one LUT size):\n");
+ log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT "; lutpack").c_str());
+ log("\n");
+ log(" for -lut/-luts (different LUT sizes):\n");
+ log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT).c_str());
+ log("\n");
+ log(" for -sop:\n");
+ log("%s\n", fold_abc_cmd(ABC_COMMAND_SOP).c_str());
+ log("\n");
+ log(" otherwise:\n");
+ log("%s\n", fold_abc_cmd(ABC_COMMAND_DFL).c_str());
+ log("\n");
+ log(" -fast\n");
+ log(" use different default scripts that are slightly faster (at the cost\n");
+ log(" of output quality):\n");
+ log("\n");
+ log(" for -liberty without -constr:\n");
+ log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_LIB).c_str());
+ log("\n");
+ log(" for -liberty with -constr:\n");
+ log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_CTR).c_str());
+ log("\n");
+ log(" for -lut/-luts:\n");
+ log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_LUT).c_str());
+ log("\n");
+ log(" for -sop:\n");
+ log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_SOP).c_str());
+ log("\n");
+ log(" otherwise:\n");
+ log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_DFL).c_str());
+ log("\n");
+ log(" -liberty <file>\n");
+ log(" generate netlists for the specified cell library (using the liberty\n");
+ log(" file format).\n");
+ log("\n");
+ log(" -constr <file>\n");
+ log(" pass this file with timing constraints to ABC. use with -liberty.\n");
+ log("\n");
+ log(" a constr file contains two lines:\n");
+ log(" set_driving_cell <cell_name>\n");
+ log(" set_load <floating_point_number>\n");
+ log("\n");
+ log(" the set_driving_cell statement defines which cell type is assumed to\n");
+ log(" drive the primary inputs and the set_load statement sets the load in\n");
+ log(" femtofarads for each primary output.\n");
+ log("\n");
+ log(" -D <picoseconds>\n");
+ log(" set delay target. the string {D} in the default scripts above is\n");
+ log(" replaced by this option when used, and an empty string otherwise.\n");
+ log("\n");
+ log(" -I <num>\n");
+ log(" maximum number of SOP inputs.\n");
+ log(" (replaces {I} in the default scripts above)\n");
+ log("\n");
+ log(" -P <num>\n");
+ log(" maximum number of SOP products.\n");
+ log(" (replaces {P} in the default scripts above)\n");
+ log("\n");
+ log(" -lut <width>\n");
+ log(" generate netlist using luts of (max) the specified width.\n");
+ log("\n");
+ log(" -lut <w1>:<w2>\n");
+ log(" generate netlist using luts of (max) the specified width <w2>. All\n");
+ log(" luts with width <= <w1> have constant cost. for luts larger than <w1>\n");
+ log(" the area cost doubles with each additional input bit. the delay cost\n");
+ log(" is still constant for all lut widths.\n");
+ log("\n");
+ log(" -luts <cost1>,<cost2>,<cost3>,<sizeN>:<cost4-N>,..\n");
+ log(" generate netlist using luts. Use the specified costs for luts with 1,\n");
+ log(" 2, 3, .. inputs.\n");
+ log("\n");
+ log(" -sop\n");
+ log(" map to sum-of-product cells and inverters\n");
+ log("\n");
+ // log(" -mux4, -mux8, -mux16\n");
+ // log(" try to extract 4-input, 8-input, and/or 16-input muxes\n");
+ // log(" (ignored when used with -liberty or -lut)\n");
+ // log("\n");
+ log(" -g type1,type2,...\n");
+ log(" Map the the specified list of gate types. Supported gates types are:\n");
+ log(" AND, NAND, OR, NOR, XOR, XNOR, MUX, AOI3, OAI3, AOI4, OAI4.\n");
+ log(" (The NOT gate is always added to this list automatically.)\n");
+ log("\n");
+ log(" -dff\n");
+ log(" also pass $_DFF_?_ and $_DFFE_??_ cells through ABC. modules with many\n");
+ log(" clock domains are automatically partitioned in clock domains and each\n");
+ log(" domain is passed through ABC independently.\n");
+ log("\n");
+ log(" -clk [!]<clock-signal-name>[,[!]<enable-signal-name>]\n");
+ log(" use only the specified clock domain. this is like -dff, but only FF\n");
+ log(" cells that belong to the specified clock domain are used.\n");
+ log("\n");
+ log(" -keepff\n");
+ log(" set the \"keep\" attribute on flip-flop output wires. (and thus preserve\n");
+ log(" them, for example for equivalence checking.)\n");
+ log("\n");
+ log(" -nocleanup\n");
+ log(" when this option is used, the temporary files created by this pass\n");
+ log(" are not removed. this is useful for debugging.\n");
+ log("\n");
+ log(" -showtmp\n");
+ log(" print the temp dir name in log. usually this is suppressed so that the\n");
+ log(" command output is identical across runs.\n");
+ log("\n");
+ log(" -markgroups\n");
+ log(" set a 'abcgroup' attribute on all objects created by ABC. The value of\n");
+ 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("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");
+ log("This pass does not operate on modules with unprocessed processes in it.\n");
+ log("(I.e. the 'proc' pass should be used first to convert processes to netlists.)\n");
+ log("\n");
+ log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing ABC pass (technology mapping using ABC).\n");
+ log_push();
+
+#ifdef ABCEXTERNAL
+ std::string exe_file = ABCEXTERNAL;
+#else
+ std::string exe_file = proc_self_dirname() + "yosys-abc";
+#endif
+ std::string script_file, liberty_file, constr_file, clk_str;
+ std::string delay_target, sop_inputs, sop_products;
+ bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true;
+ bool show_tempdir = false, sop_mode = false;
+ vector<int> lut_costs;
+ markgroups = false;
+
+ map_mux4 = false;
+ map_mux8 = false;
+ map_mux16 = false;
+ enabled_gates.clear();
+
+#ifdef _WIN32
+#ifndef ABCEXTERNAL
+ if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe"))
+ exe_file = proc_self_dirname() + "..\\yosys-abc";
+#endif
+#endif
+
+ size_t argidx;
+ char pwd [PATH_MAX];
+ if (!getcwd(pwd, sizeof(pwd))) {
+ log_cmd_error("getcwd failed: %s\n", strerror(errno));
+ log_abort();
+ }
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-exe" && argidx+1 < args.size()) {
+ exe_file = args[++argidx];
+ continue;
+ }
+ if (arg == "-script" && argidx+1 < args.size()) {
+ script_file = args[++argidx];
+ if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+')
+ script_file = std::string(pwd) + "/" + script_file;
+ continue;
+ }
+ if (arg == "-liberty" && argidx+1 < args.size()) {
+ liberty_file = args[++argidx];
+ if (!liberty_file.empty() && !is_absolute_path(liberty_file))
+ liberty_file = std::string(pwd) + "/" + liberty_file;
+ continue;
+ }
+ if (arg == "-constr" && argidx+1 < args.size()) {
+ constr_file = args[++argidx];
+ if (!constr_file.empty() && !is_absolute_path(constr_file))
+ constr_file = std::string(pwd) + "/" + constr_file;
+ continue;
+ }
+ if (arg == "-D" && argidx+1 < args.size()) {
+ delay_target = "-D " + args[++argidx];
+ continue;
+ }
+ if (arg == "-I" && argidx+1 < args.size()) {
+ sop_inputs = "-I " + args[++argidx];
+ continue;
+ }
+ if (arg == "-P" && argidx+1 < args.size()) {
+ sop_products = "-P " + args[++argidx];
+ continue;
+ }
+ if (arg == "-lut" && argidx+1 < args.size()) {
+ string arg = args[++argidx];
+ size_t pos = arg.find_first_of(':');
+ int lut_mode = 0, lut_mode2 = 0;
+ if (pos != string::npos) {
+ lut_mode = atoi(arg.substr(0, pos).c_str());
+ lut_mode2 = atoi(arg.substr(pos+1).c_str());
+ } else {
+ lut_mode = atoi(arg.c_str());
+ lut_mode2 = lut_mode;
+ }
+ lut_costs.clear();
+ for (int i = 0; i < lut_mode; i++)
+ lut_costs.push_back(1);
+ for (int i = lut_mode; i < lut_mode2; i++)
+ lut_costs.push_back(2 << (i - lut_mode));
+ continue;
+ }
+ if (arg == "-luts" && argidx+1 < args.size()) {
+ lut_costs.clear();
+ for (auto &tok : split_tokens(args[++argidx], ",")) {
+ auto parts = split_tokens(tok, ":");
+ if (GetSize(parts) == 0 && !lut_costs.empty())
+ lut_costs.push_back(lut_costs.back());
+ else if (GetSize(parts) == 1)
+ lut_costs.push_back(atoi(parts.at(0).c_str()));
+ else if (GetSize(parts) == 2)
+ while (GetSize(lut_costs) < atoi(parts.at(0).c_str()))
+ lut_costs.push_back(atoi(parts.at(1).c_str()));
+ else
+ log_cmd_error("Invalid -luts syntax.\n");
+ }
+ continue;
+ }
+ if (arg == "-sop") {
+ sop_mode = true;
+ continue;
+ }
+ if (arg == "-mux4") {
+ map_mux4 = true;
+ continue;
+ }
+ if (arg == "-mux8") {
+ map_mux8 = true;
+ continue;
+ }
+ if (arg == "-mux16") {
+ map_mux16 = true;
+ continue;
+ }
+ if (arg == "-g" && argidx+1 < args.size()) {
+ for (auto g : split_tokens(args[++argidx], ",")) {
+ if (g == "AND") goto ok_gate;
+ if (g == "NAND") goto ok_gate;
+ if (g == "OR") goto ok_gate;
+ if (g == "NOR") goto ok_gate;
+ if (g == "XOR") goto ok_gate;
+ if (g == "XNOR") goto ok_gate;
+ if (g == "MUX") goto ok_gate;
+ if (g == "AOI3") goto ok_gate;
+ if (g == "OAI3") goto ok_gate;
+ if (g == "AOI4") goto ok_gate;
+ if (g == "OAI4") goto ok_gate;
+ cmd_error(args, argidx, stringf("Unsupported gate type: %s", g.c_str()));
+ ok_gate:
+ enabled_gates.insert(g);
+ }
+ continue;
+ }
+ if (arg == "-fast") {
+ fast_mode = true;
+ continue;
+ }
+ if (arg == "-dff") {
+ dff_mode = true;
+ continue;
+ }
+ if (arg == "-clk" && argidx+1 < args.size()) {
+ clk_str = args[++argidx];
+ dff_mode = true;
+ continue;
+ }
+ if (arg == "-keepff") {
+ keepff = true;
+ continue;
+ }
+ if (arg == "-nocleanup") {
+ cleanup = false;
+ continue;
+ }
+ if (arg == "-showtmp") {
+ show_tempdir = true;
+ continue;
+ }
+ if (arg == "-markgroups") {
+ markgroups = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!lut_costs.empty() && !liberty_file.empty())
+ log_cmd_error("Got -lut and -liberty! This two options are exclusive.\n");
+ if (!constr_file.empty() && liberty_file.empty())
+ log_cmd_error("Got -constr but no -liberty!\n");
+
+ for (auto mod : design->selected_modules())
+ if (mod->processes.size() > 0)
+ log("Skipping module %s as it contains processes.\n", log_id(mod));
+ else 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, fast_mode, mod->selected_cells(), show_tempdir, sop_mode);
+ else
+ {
+ assign_map.set(mod);
+ CellTypes ct(design);
+
+ std::vector<RTLIL::Cell*> all_cells = mod->selected_cells();
+ std::set<RTLIL::Cell*> unassigned_cells(all_cells.begin(), all_cells.end());
+
+ std::set<RTLIL::Cell*> expand_queue, next_expand_queue;
+ std::set<RTLIL::Cell*> expand_queue_up, next_expand_queue_up;
+ std::set<RTLIL::Cell*> expand_queue_down, next_expand_queue_down;
+
+ typedef tuple<bool, RTLIL::SigSpec, bool, RTLIL::SigSpec> clkdomain_t;
+ std::map<clkdomain_t, std::vector<RTLIL::Cell*>> assigned_cells;
+ std::map<RTLIL::Cell*, clkdomain_t> assigned_cells_reverse;
+
+ std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_bit, cell_to_bit_up, cell_to_bit_down;
+ std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> bit_to_cell, bit_to_cell_up, bit_to_cell_down;
+
+ for (auto cell : all_cells)
+ {
+ clkdomain_t key;
+
+ for (auto &conn : cell->connections())
+ for (auto bit : conn.second) {
+ bit = assign_map(bit);
+ if (bit.wire != nullptr) {
+ cell_to_bit[cell].insert(bit);
+ bit_to_cell[bit].insert(cell);
+ if (ct.cell_input(cell->type, conn.first)) {
+ cell_to_bit_up[cell].insert(bit);
+ bit_to_cell_down[bit].insert(cell);
+ }
+ if (ct.cell_output(cell->type, conn.first)) {
+ cell_to_bit_down[cell].insert(bit);
+ bit_to_cell_up[bit].insert(cell);
+ }
+ }
+ }
+
+ if (cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_")
+ {
+ key = clkdomain_t(cell->type == "$_DFF_P_", assign_map(cell->getPort("\\C")), true, RTLIL::SigSpec());
+ }
+ else
+ if (cell->type == "$_DFFE_NN_" || cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_")
+ {
+ bool this_clk_pol = cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_";
+ bool this_en_pol = cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PP_";
+ key = clkdomain_t(this_clk_pol, assign_map(cell->getPort("\\C")), this_en_pol, assign_map(cell->getPort("\\E")));
+ }
+ else
+ continue;
+
+ unassigned_cells.erase(cell);
+ expand_queue.insert(cell);
+ expand_queue_up.insert(cell);
+ expand_queue_down.insert(cell);
+
+ assigned_cells[key].push_back(cell);
+ assigned_cells_reverse[cell] = key;
+ }
+
+ while (!expand_queue_up.empty() || !expand_queue_down.empty())
+ {
+ if (!expand_queue_up.empty())
+ {
+ RTLIL::Cell *cell = *expand_queue_up.begin();
+ clkdomain_t key = assigned_cells_reverse.at(cell);
+ expand_queue_up.erase(cell);
+
+ for (auto bit : cell_to_bit_up[cell])
+ for (auto c : bit_to_cell_up[bit])
+ if (unassigned_cells.count(c)) {
+ unassigned_cells.erase(c);
+ next_expand_queue_up.insert(c);
+ assigned_cells[key].push_back(c);
+ assigned_cells_reverse[c] = key;
+ expand_queue.insert(c);
+ }
+ }
+
+ if (!expand_queue_down.empty())
+ {
+ RTLIL::Cell *cell = *expand_queue_down.begin();
+ clkdomain_t key = assigned_cells_reverse.at(cell);
+ expand_queue_down.erase(cell);
+
+ for (auto bit : cell_to_bit_down[cell])
+ for (auto c : bit_to_cell_down[bit])
+ if (unassigned_cells.count(c)) {
+ unassigned_cells.erase(c);
+ next_expand_queue_up.insert(c);
+ assigned_cells[key].push_back(c);
+ assigned_cells_reverse[c] = key;
+ expand_queue.insert(c);
+ }
+ }
+
+ if (expand_queue_up.empty() && expand_queue_down.empty()) {
+ expand_queue_up.swap(next_expand_queue_up);
+ expand_queue_down.swap(next_expand_queue_down);
+ }
+ }
+
+ while (!expand_queue.empty())
+ {
+ RTLIL::Cell *cell = *expand_queue.begin();
+ clkdomain_t key = assigned_cells_reverse.at(cell);
+ expand_queue.erase(cell);
+
+ for (auto bit : cell_to_bit.at(cell)) {
+ for (auto c : bit_to_cell[bit])
+ if (unassigned_cells.count(c)) {
+ unassigned_cells.erase(c);
+ next_expand_queue.insert(c);
+ assigned_cells[key].push_back(c);
+ assigned_cells_reverse[c] = key;
+ }
+ bit_to_cell[bit].clear();
+ }
+
+ if (expand_queue.empty())
+ expand_queue.swap(next_expand_queue);
+ }
+
+ clkdomain_t key(true, RTLIL::SigSpec(), true, RTLIL::SigSpec());
+ for (auto cell : unassigned_cells) {
+ assigned_cells[key].push_back(cell);
+ assigned_cells_reverse[cell] = key;
+ }
+
+ log_header(design, "Summary of detected clock domains:\n");
+ for (auto &it : assigned_cells)
+ log(" %d cells in clk=%s%s, en=%s%s\n", GetSize(it.second),
+ std::get<0>(it.first) ? "" : "!", log_signal(std::get<1>(it.first)),
+ std::get<2>(it.first) ? "" : "!", log_signal(std::get<3>(it.first)));
+
+ for (auto &it : assigned_cells) {
+ clk_polarity = std::get<0>(it.first);
+ clk_sig = assign_map(std::get<1>(it.first));
+ 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, fast_mode, it.second, show_tempdir, sop_mode);
+ assign_map.set(mod);
+ }
+ }
+
+ assign_map.clear();
+ signal_list.clear();
+ signal_map.clear();
+
+ log_pop();
+ }
+} AbcPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/aigmap.cc b/passes/techmap/aigmap.cc
new file mode 100644
index 00000000..b9ac7ade
--- /dev/null
+++ b/passes/techmap/aigmap.cc
@@ -0,0 +1,149 @@
+/*
+ * 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/cellaigs.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct AigmapPass : public Pass {
+ AigmapPass() : Pass("aigmap", "map logic to and-inverter-graph circuit") { }
+ virtual void help()
+ {
+ log("\n");
+ log(" aigmap [options] [selection]\n");
+ log("\n");
+ log("Replace all logic cells with circuits made of only $_AND_ and\n");
+ log("$_NOT_ cells.\n");
+ log("\n");
+ log(" -nand\n");
+ log(" Enable creation of $_NAND_ cells\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool nand_mode = false;
+
+ log_header(design, "Executing AIGMAP pass (map logic to AIG).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-nand") {
+ nand_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ vector<Cell*> replaced_cells;
+ int not_replaced_count = 0;
+ dict<IdString, int> stat_replaced;
+ dict<IdString, int> stat_not_replaced;
+ int orig_num_cells = GetSize(module->cells());
+
+ for (auto cell : module->selected_cells())
+ {
+ Aig aig(cell);
+
+ if (cell->type == "$_AND_" || cell->type == "$_NOT_")
+ aig.name.clear();
+
+ if (nand_mode && cell->type == "$_NAND_")
+ aig.name.clear();
+
+ if (aig.name.empty()) {
+ not_replaced_count++;
+ stat_not_replaced[cell->type]++;
+ continue;
+ }
+
+ vector<SigBit> sigs;
+ dict<pair<int, int>, SigBit> and_cache;
+
+ for (int node_idx = 0; node_idx < GetSize(aig.nodes); node_idx++)
+ {
+ SigBit bit;
+ auto &node = aig.nodes[node_idx];
+
+ if (node.portbit >= 0) {
+ bit = cell->getPort(node.portname)[node.portbit];
+ } else if (node.left_parent < 0 && node.right_parent < 0) {
+ bit = node.inverter ? State::S1 : State::S0;
+ goto skip_inverter;
+ } else {
+ SigBit A = sigs.at(node.left_parent);
+ SigBit B = sigs.at(node.right_parent);
+ if (nand_mode && node.inverter) {
+ bit = module->NandGate(NEW_ID, A, B);
+ goto skip_inverter;
+ } else {
+ pair<int, int> key(node.left_parent, node.right_parent);
+ if (and_cache.count(key))
+ bit = and_cache.at(key);
+ else
+ bit = module->AndGate(NEW_ID, A, B);
+ }
+ }
+
+ if (node.inverter)
+ bit = module->NotGate(NEW_ID, bit);
+
+ skip_inverter:
+ for (auto &op : node.outports)
+ module->connect(cell->getPort(op.first)[op.second], bit);
+
+ sigs.push_back(bit);
+ }
+
+ replaced_cells.push_back(cell);
+ stat_replaced[cell->type]++;
+ }
+
+ if (not_replaced_count == 0 && replaced_cells.empty())
+ continue;
+
+ log("Module %s: replaced %d cells with %d new cells, skipped %d cells.\n", log_id(module),
+ GetSize(replaced_cells), GetSize(module->cells()) - orig_num_cells, not_replaced_count);
+
+ if (!stat_replaced.empty()) {
+ stat_replaced.sort();
+ log(" replaced %d cell types:\n", GetSize(stat_replaced));
+ for (auto &it : stat_replaced)
+ log("%8d %s\n", it.second, log_id(it.first));
+ }
+
+ if (!stat_not_replaced.empty()) {
+ stat_not_replaced.sort();
+ log(" not replaced %d cell types:\n", GetSize(stat_not_replaced));
+ for (auto &it : stat_not_replaced)
+ log("%8d %s\n", it.second, log_id(it.first));
+ }
+
+ for (auto cell : replaced_cells)
+ module->remove(cell);
+ }
+ }
+} AigmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc
new file mode 100644
index 00000000..9f6dd02d
--- /dev/null
+++ b/passes/techmap/alumacc.cc
@@ -0,0 +1,567 @@
+/*
+ * 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/macc.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct AlumaccWorker
+{
+ RTLIL::Module *module;
+ SigMap sigmap;
+
+ struct maccnode_t {
+ Macc macc;
+ RTLIL::Cell *cell;
+ RTLIL::SigSpec y;
+ int users;
+ };
+
+ struct alunode_t
+ {
+ std::vector<RTLIL::Cell*> cells;
+ RTLIL::SigSpec a, b, c, y;
+ std::vector<tuple<bool, bool, bool, bool, RTLIL::SigSpec>> cmp;
+ bool is_signed, invert_b;
+
+ RTLIL::Cell *alu_cell;
+ RTLIL::SigSpec cached_lt, cached_gt, cached_eq, cached_ne;
+ RTLIL::SigSpec cached_cf, cached_of, cached_sf;
+
+ RTLIL::SigSpec get_lt() {
+ if (GetSize(cached_lt) == 0)
+ cached_lt = is_signed ? alu_cell->module->Xor(NEW_ID, get_of(), get_sf()) : get_cf();
+ return cached_lt;
+ }
+
+ RTLIL::SigSpec get_gt() {
+ if (GetSize(cached_gt) == 0)
+ cached_gt = alu_cell->module->Not(NEW_ID, alu_cell->module->Or(NEW_ID, get_lt(), get_eq()));
+ return cached_gt;
+ }
+
+ RTLIL::SigSpec get_eq() {
+ if (GetSize(cached_eq) == 0)
+ cached_eq = alu_cell->module->ReduceAnd(NEW_ID, alu_cell->getPort("\\X"));
+ return cached_eq;
+ }
+
+ RTLIL::SigSpec get_ne() {
+ if (GetSize(cached_ne) == 0)
+ cached_ne = alu_cell->module->Not(NEW_ID, get_eq());
+ return cached_ne;
+ }
+
+ RTLIL::SigSpec get_cf() {
+ if (GetSize(cached_cf) == 0) {
+ cached_cf = alu_cell->getPort("\\CO");
+ log_assert(GetSize(cached_cf) >= 1);
+ cached_cf = alu_cell->module->Not(NEW_ID, cached_cf[GetSize(cached_cf)-1]);
+ }
+ return cached_cf;
+ }
+
+ RTLIL::SigSpec get_of() {
+ if (GetSize(cached_of) == 0) {
+ cached_of = {alu_cell->getPort("\\CO"), alu_cell->getPort("\\CI")};
+ log_assert(GetSize(cached_of) >= 2);
+ cached_of = alu_cell->module->Xor(NEW_ID, cached_of[GetSize(cached_of)-1], cached_of[GetSize(cached_of)-2]);
+ }
+ return cached_of;
+ }
+
+ RTLIL::SigSpec get_sf() {
+ if (GetSize(cached_sf) == 0) {
+ cached_sf = alu_cell->getPort("\\Y");
+ cached_sf = cached_sf[GetSize(cached_sf)-1];
+ }
+ return cached_sf;
+ }
+ };
+
+ dict<RTLIL::SigBit, int> bit_users;
+ dict<RTLIL::SigSpec, maccnode_t*> sig_macc;
+ dict<RTLIL::SigSig, pool<alunode_t*, hash_ptr_ops>> sig_alu;
+ int macc_counter, alu_counter;
+
+ AlumaccWorker(RTLIL::Module *module) : module(module), sigmap(module)
+ {
+ macc_counter = 0;
+ alu_counter = 0;
+ }
+
+ void count_bit_users()
+ {
+ for (auto port : module->ports)
+ for (auto bit : sigmap(module->wire(port)))
+ bit_users[bit]++;
+
+ for (auto cell : module->cells())
+ for (auto &conn : cell->connections())
+ for (auto bit : sigmap(conn.second))
+ bit_users[bit]++;
+ }
+
+ void extract_macc()
+ {
+ for (auto cell : module->selected_cells())
+ {
+ if (!cell->type.in("$pos", "$neg", "$add", "$sub", "$mul"))
+ continue;
+
+ log(" creating $macc model for %s (%s).\n", log_id(cell), log_id(cell->type));
+
+ maccnode_t *n = new maccnode_t;
+ Macc::port_t new_port;
+
+ n->cell = cell;
+ n->y = sigmap(cell->getPort("\\Y"));
+ n->users = 0;
+
+ for (auto bit : n->y)
+ n->users = max(n->users, bit_users.at(bit) - 1);
+
+ if (cell->type.in("$pos", "$neg"))
+ {
+ new_port.in_a = sigmap(cell->getPort("\\A"));
+ new_port.is_signed = cell->getParam("\\A_SIGNED").as_bool();
+ new_port.do_subtract = cell->type == "$neg";
+ n->macc.ports.push_back(new_port);
+ }
+
+ if (cell->type.in("$add", "$sub"))
+ {
+ new_port.in_a = sigmap(cell->getPort("\\A"));
+ new_port.is_signed = cell->getParam("\\A_SIGNED").as_bool();
+ new_port.do_subtract = false;
+ n->macc.ports.push_back(new_port);
+
+ new_port.in_a = sigmap(cell->getPort("\\B"));
+ new_port.is_signed = cell->getParam("\\B_SIGNED").as_bool();
+ new_port.do_subtract = cell->type == "$sub";
+ n->macc.ports.push_back(new_port);
+ }
+
+ if (cell->type.in("$mul"))
+ {
+ new_port.in_a = sigmap(cell->getPort("\\A"));
+ new_port.in_b = sigmap(cell->getPort("\\B"));
+ new_port.is_signed = cell->getParam("\\A_SIGNED").as_bool();
+ new_port.do_subtract = false;
+ n->macc.ports.push_back(new_port);
+ }
+
+ log_assert(sig_macc.count(n->y) == 0);
+ sig_macc[n->y] = n;
+ }
+ }
+
+ static bool macc_may_overflow(Macc &macc, int width, bool is_signed)
+ {
+ std::vector<int> port_sizes;
+
+ for (auto &port : macc.ports) {
+ if (port.is_signed != is_signed)
+ return true;
+ if (!port.is_signed && port.do_subtract)
+ return true;
+ if (GetSize(port.in_b))
+ port_sizes.push_back(GetSize(port.in_a) + GetSize(port.in_b));
+ else
+ port_sizes.push_back(GetSize(port.in_a));
+ }
+
+ std::sort(port_sizes.begin(), port_sizes.end());
+
+ int acc_sum = 0, acc_shift = 0;
+ for (int sz : port_sizes) {
+ while ((sz - acc_shift) > 20) {
+ if (acc_sum & 1)
+ acc_sum++;
+ acc_sum = acc_sum >> 1;
+ acc_shift++;
+ }
+ acc_sum += (1 << (sz - acc_shift)) - 1;
+ }
+
+ while (acc_sum) {
+ acc_sum = acc_sum >> 1;
+ acc_shift++;
+ }
+
+ return acc_shift > width;
+ }
+
+ void merge_macc()
+ {
+ while (1)
+ {
+ pool<maccnode_t*, hash_ptr_ops> delete_nodes;
+
+ for (auto &it : sig_macc)
+ {
+ auto n = it.second;
+
+ if (delete_nodes.count(n))
+ continue;
+
+ for (int i = 0; i < GetSize(n->macc.ports); i++)
+ {
+ auto &port = n->macc.ports[i];
+
+ if (GetSize(port.in_b) > 0 || sig_macc.count(port.in_a) == 0)
+ continue;
+
+ auto other_n = sig_macc.at(port.in_a);
+
+ if (other_n->users > 1)
+ continue;
+
+ if (GetSize(other_n->y) != GetSize(n->y) && macc_may_overflow(other_n->macc, GetSize(other_n->y), port.is_signed))
+ continue;
+
+ log(" merging $macc model for %s into %s.\n", log_id(other_n->cell), log_id(n->cell));
+
+ bool do_subtract = port.do_subtract;
+ for (int j = 0; j < GetSize(other_n->macc.ports); j++) {
+ if (do_subtract)
+ other_n->macc.ports[j].do_subtract = !other_n->macc.ports[j].do_subtract;
+ if (j == 0)
+ n->macc.ports[i--] = other_n->macc.ports[j];
+ else
+ n->macc.ports.push_back(other_n->macc.ports[j]);
+ }
+
+ delete_nodes.insert(other_n);
+ }
+ }
+
+ if (delete_nodes.empty())
+ break;
+
+ for (auto n : delete_nodes) {
+ sig_macc.erase(n->y);
+ delete n;
+ }
+ }
+ }
+
+ void macc_to_alu()
+ {
+ pool<maccnode_t*, hash_ptr_ops> delete_nodes;
+
+ for (auto &it : sig_macc)
+ {
+ auto n = it.second;
+ RTLIL::SigSpec A, B, C = n->macc.bit_ports;
+ bool a_signed = false, b_signed = false;
+ bool subtract_b = false;
+ alunode_t *alunode;
+
+ for (auto &port : n->macc.ports)
+ if (GetSize(port.in_b) > 0) {
+ goto next_macc;
+ } else if (GetSize(port.in_a) == 1 && !port.is_signed && !port.do_subtract) {
+ C.append(port.in_a);
+ } else if (GetSize(A) || port.do_subtract) {
+ if (GetSize(B))
+ goto next_macc;
+ B = port.in_a;
+ b_signed = port.is_signed;
+ subtract_b = port.do_subtract;
+ } else {
+ if (GetSize(A))
+ goto next_macc;
+ A = port.in_a;
+ a_signed = port.is_signed;
+ }
+
+ if (!a_signed || !b_signed) {
+ if (GetSize(A) == GetSize(n->y))
+ a_signed = false;
+ if (GetSize(B) == GetSize(n->y))
+ b_signed = false;
+ if (a_signed != b_signed)
+ goto next_macc;
+ }
+
+ if (GetSize(A) == 0 && GetSize(C) > 0) {
+ A = C[0];
+ C.remove(0);
+ }
+
+ if (GetSize(B) == 0 && GetSize(C) > 0) {
+ B = C[0];
+ C.remove(0);
+ }
+
+ if (subtract_b)
+ C.append(RTLIL::S1);
+
+ if (GetSize(C) > 1)
+ goto next_macc;
+
+ if (!subtract_b && B < A && GetSize(B))
+ std::swap(A, B);
+
+ log(" creating $alu model for $macc %s.\n", log_id(n->cell));
+
+ alunode = new alunode_t;
+ alunode->cells.push_back(n->cell);
+ alunode->is_signed = a_signed;
+ alunode->invert_b = subtract_b;
+
+ alunode->a = A;
+ alunode->b = B;
+ alunode->c = C;
+ alunode->y = n->y;
+
+ sig_alu[RTLIL::SigSig(A, B)].insert(alunode);
+ delete_nodes.insert(n);
+ next_macc:;
+ }
+
+ for (auto n : delete_nodes) {
+ sig_macc.erase(n->y);
+ delete n;
+ }
+ }
+
+ void replace_macc()
+ {
+ for (auto &it : sig_macc)
+ {
+ auto n = it.second;
+ auto cell = module->addCell(NEW_ID, "$macc");
+ macc_counter++;
+
+ log(" creating $macc cell for %s: %s\n", log_id(n->cell), log_id(cell));
+
+ n->macc.optimize(GetSize(n->y));
+ n->macc.to_cell(cell);
+ cell->setPort("\\Y", n->y);
+ cell->fixup_parameters();
+ module->remove(n->cell);
+ delete n;
+ }
+
+ sig_macc.clear();
+ }
+
+ void extract_cmp_alu()
+ {
+ std::vector<RTLIL::Cell*> lge_cells, eq_cells;
+
+ for (auto cell : module->selected_cells())
+ {
+ if (cell->type.in("$lt", "$le", "$ge", "$gt"))
+ lge_cells.push_back(cell);
+ if (cell->type.in("$eq", "$eqx", "$ne", "$nex"))
+ eq_cells.push_back(cell);
+ }
+
+ for (auto cell : lge_cells)
+ {
+ log(" creating $alu model for %s (%s):", log_id(cell), log_id(cell->type));
+
+ bool cmp_less = cell->type.in("$lt", "$le");
+ bool cmp_equal = cell->type.in("$le", "$ge");
+ bool is_signed = cell->getParam("\\A_SIGNED").as_bool();
+
+ RTLIL::SigSpec A = sigmap(cell->getPort("\\A"));
+ RTLIL::SigSpec B = sigmap(cell->getPort("\\B"));
+ RTLIL::SigSpec Y = sigmap(cell->getPort("\\Y"));
+
+ if (B < A && GetSize(B)) {
+ cmp_less = !cmp_less;
+ std::swap(A, B);
+ }
+
+ alunode_t *n = nullptr;
+
+ for (auto node : sig_alu[RTLIL::SigSig(A, B)])
+ if (node->is_signed == is_signed && node->invert_b && node->c == RTLIL::S1) {
+ n = node;
+ break;
+ }
+
+ if (n == nullptr) {
+ n = new alunode_t;
+ n->a = A;
+ n->b = B;
+ n->c = RTLIL::S1;
+ n->y = module->addWire(NEW_ID, max(GetSize(A), GetSize(B)));
+ n->is_signed = is_signed;
+ n->invert_b = true;
+ sig_alu[RTLIL::SigSig(A, B)].insert(n);
+ log(" new $alu\n");
+ } else {
+ log(" merged with %s.\n", log_id(n->cells.front()));
+ }
+
+ n->cells.push_back(cell);
+ n->cmp.push_back(std::make_tuple(cmp_less, !cmp_less, cmp_equal, false, Y));
+ }
+
+ for (auto cell : eq_cells)
+ {
+ bool cmp_equal = cell->type.in("$eq", "$eqx");
+ bool is_signed = cell->getParam("\\A_SIGNED").as_bool();
+
+ RTLIL::SigSpec A = sigmap(cell->getPort("\\A"));
+ RTLIL::SigSpec B = sigmap(cell->getPort("\\B"));
+ RTLIL::SigSpec Y = sigmap(cell->getPort("\\Y"));
+
+ if (B < A && GetSize(B))
+ std::swap(A, B);
+
+ alunode_t *n = nullptr;
+
+ for (auto node : sig_alu[RTLIL::SigSig(A, B)])
+ if (node->is_signed == is_signed && node->invert_b && node->c == RTLIL::S1) {
+ n = node;
+ break;
+ }
+
+ if (n != nullptr) {
+ log(" creating $alu model for %s (%s): merged with %s.\n", log_id(cell), log_id(cell->type), log_id(n->cells.front()));
+ n->cells.push_back(cell);
+ n->cmp.push_back(std::make_tuple(false, false, cmp_equal, !cmp_equal, Y));
+ }
+ }
+ }
+
+ void replace_alu()
+ {
+ for (auto &it1 : sig_alu)
+ for (auto n : it1.second)
+ {
+ if (GetSize(n->b) == 0 && GetSize(n->c) == 0 && GetSize(n->cmp) == 0)
+ {
+ n->alu_cell = module->addPos(NEW_ID, n->a, n->y, n->is_signed);
+
+ log(" creating $pos cell for ");
+ for (int i = 0; i < GetSize(n->cells); i++)
+ log("%s%s", i ? ", ": "", log_id(n->cells[i]));
+ log(": %s\n", log_id(n->alu_cell));
+
+ goto delete_node;
+ }
+
+ n->alu_cell = module->addCell(NEW_ID, "$alu");
+ alu_counter++;
+
+ log(" creating $alu cell for ");
+ for (int i = 0; i < GetSize(n->cells); i++)
+ log("%s%s", i ? ", ": "", log_id(n->cells[i]));
+ log(": %s\n", log_id(n->alu_cell));
+
+ n->alu_cell->setPort("\\A", n->a);
+ n->alu_cell->setPort("\\B", n->b);
+ n->alu_cell->setPort("\\CI", GetSize(n->c) ? n->c : RTLIL::S0);
+ n->alu_cell->setPort("\\BI", n->invert_b ? RTLIL::S1 : RTLIL::S0);
+ n->alu_cell->setPort("\\Y", n->y);
+ n->alu_cell->setPort("\\X", module->addWire(NEW_ID, GetSize(n->y)));
+ n->alu_cell->setPort("\\CO", module->addWire(NEW_ID, GetSize(n->y)));
+ n->alu_cell->fixup_parameters(n->is_signed, n->is_signed);
+
+ for (auto &it : n->cmp)
+ {
+ bool cmp_lt = std::get<0>(it);
+ bool cmp_gt = std::get<1>(it);
+ bool cmp_eq = std::get<2>(it);
+ bool cmp_ne = std::get<3>(it);
+ RTLIL::SigSpec cmp_y = std::get<4>(it);
+
+ RTLIL::SigSpec sig;
+ if (cmp_lt) sig.append(n->get_lt());
+ if (cmp_gt) sig.append(n->get_gt());
+ if (cmp_eq) sig.append(n->get_eq());
+ if (cmp_ne) sig.append(n->get_ne());
+
+ if (GetSize(sig) > 1)
+ sig = module->ReduceOr(NEW_ID, sig);
+
+ sig.extend_u0(GetSize(cmp_y));
+ module->connect(cmp_y, sig);
+ }
+
+ delete_node:
+ for (auto c : n->cells)
+ module->remove(c);
+ delete n;
+ }
+
+ sig_alu.clear();
+ }
+
+ void run()
+ {
+ log("Extracting $alu and $macc cells in module %s:\n", log_id(module));
+
+ count_bit_users();
+ extract_macc();
+ merge_macc();
+ macc_to_alu();
+ replace_macc();
+ extract_cmp_alu();
+ replace_alu();
+
+ log(" created %d $alu and %d $macc cells.\n", alu_counter, macc_counter);
+ }
+};
+
+struct AlumaccPass : public Pass {
+ AlumaccPass() : Pass("alumacc", "extract ALU and MACC cells") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" alumacc [selection]\n");
+ log("\n");
+ log("This pass translates arithmetic operations like $add, $mul, $lt, etc. to $alu\n");
+ log("and $macc cells.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing ALUMACC pass (create $alu and $macc cells).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ // if (args[argidx] == "-foobar") {
+ // foobar_mode = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto mod : design->selected_modules())
+ if (!mod->has_processes_warn()) {
+ AlumaccWorker worker(mod);
+ worker.run();
+ }
+ }
+} AlumaccPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/attrmap.cc b/passes/techmap/attrmap.cc
new file mode 100644
index 00000000..dec81d21
--- /dev/null
+++ b/passes/techmap/attrmap.cc
@@ -0,0 +1,269 @@
+/*
+ * 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
+
+Const make_value(string &value)
+{
+ if (GetSize(value) >= 2 && value.front() == '"' && value.back() == '"')
+ return Const(value.substr(1, GetSize(value)-2));
+
+ SigSpec sig;
+ SigSpec::parse(sig, nullptr, value);
+ return sig.as_const();
+}
+
+bool string_compare_nocase(const string &str1, const string &str2)
+{
+ if (str1.size() != str2.size())
+ return false;
+
+ for (size_t i = 0; i < str1.size(); i++)
+ {
+ char ch1 = str1[i], ch2 = str2[i];
+ if ('a' <= ch1 && ch1 <= 'z')
+ ch1 -= 'a' - 'A';
+ if ('a' <= ch2 && ch2 <= 'z')
+ ch2 -= 'a' - 'A';
+ if (ch1 != ch2)
+ return false;
+ }
+
+ return true;
+}
+
+bool match_name(string &name, IdString &id, bool ignore_case=false)
+{
+ string str1 = RTLIL::escape_id(name);
+ string str2 = id.str();
+
+ if (ignore_case)
+ return string_compare_nocase(str1, str2);
+
+ return str1 == str2;
+}
+
+bool match_value(string &value, Const &val, bool ignore_case=false)
+{
+ if (ignore_case && ((val.flags & RTLIL::CONST_FLAG_STRING) != 0) && GetSize(value) && value.front() == '"' && value.back() == '"') {
+ string str1 = value.substr(1, GetSize(value)-2);
+ string str2 = val.decode_string();
+ return string_compare_nocase(str1, str2);
+ }
+
+ return make_value(value) == val;
+}
+
+struct AttrmapAction {
+ virtual ~AttrmapAction() { }
+ virtual bool apply(IdString &id, Const &val) = 0;
+};
+
+struct AttrmapTocase : AttrmapAction {
+ string name;
+ virtual bool apply(IdString &id, Const&) {
+ if (match_name(name, id, true))
+ id = RTLIL::escape_id(name);
+ return true;
+ }
+};
+
+struct AttrmapRename : AttrmapAction {
+ string old_name, new_name;
+ virtual bool apply(IdString &id, Const&) {
+ if (match_name(old_name, id))
+ id = RTLIL::escape_id(new_name);
+ return true;
+ }
+};
+
+struct AttrmapMap : AttrmapAction {
+ bool imap;
+ string old_name, new_name;
+ string old_value, new_value;
+ virtual bool apply(IdString &id, Const &val) {
+ if (match_name(old_name, id) && match_value(old_value, val, true)) {
+ id = RTLIL::escape_id(new_name);
+ val = make_value(new_value);
+ }
+ return true;
+ }
+};
+
+struct AttrmapRemove : AttrmapAction {
+ string name, value;
+ virtual bool apply(IdString &id, Const &val) {
+ return !(match_name(name, id) && match_value(value, val));
+ }
+};
+
+void attrmap_apply(string objname, vector<std::unique_ptr<AttrmapAction>> &actions, dict<RTLIL::IdString, RTLIL::Const> &attributes)
+{
+ dict<RTLIL::IdString, RTLIL::Const> new_attributes;
+
+ for (auto attr : attributes)
+ {
+ auto new_attr = attr;
+ for (auto &action : actions)
+ if (!action->apply(new_attr.first, new_attr.second))
+ goto delete_this_attr;
+
+ if (new_attr != attr)
+ log("Changed attribute on %s: %s=%s -> %s=%s\n", objname.c_str(),
+ log_id(attr.first), log_const(attr.second), log_id(new_attr.first), log_const(new_attr.second));
+
+ new_attributes[new_attr.first] = new_attr.second;
+
+ if (0)
+ delete_this_attr:
+ log("Removed attribute on %s: %s=%s\n", objname.c_str(), log_id(attr.first), log_const(attr.second));
+ }
+
+ attributes.swap(new_attributes);
+}
+
+struct AttrmapPass : public Pass {
+ AttrmapPass() : Pass("attrmap", "renaming attributes") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" attrmap [options] [selection]\n");
+ log("\n");
+ log("This command renames attributes and/or mapps key/value pairs to\n");
+ log("other key/value pairs.\n");
+ log("\n");
+ log(" -tocase <name>\n");
+ log(" Match attribute names case-insensitively and set it to the specified\n");
+ log(" name.\n");
+ log("\n");
+ log(" -rename <old_name> <new_name>\n");
+ log(" Rename attributes as specified\n");
+ log("\n");
+ log(" -map <old_name>=<old_value> <new_name>=<new_value>\n");
+ log(" Map key/value pairs as indicated.\n");
+ log("\n");
+ log(" -imap <old_name>=<old_value> <new_name>=<new_value>\n");
+ log(" Like -map, but use case-insensitive match for <old_value> when\n");
+ log(" it is a string value.\n");
+ log("\n");
+ log(" -remove <name>=<value>\n");
+ log(" Remove attributes matching this pattern.\n");
+ log("\n");
+ log(" -modattr\n");
+ log(" Operate on module attributes instead of attributes on wires and cells.\n");
+ log("\n");
+ log("For example, mapping Xilinx-style \"keep\" attributes to Yosys-style:\n");
+ log("\n");
+ log(" attrmap -tocase keep -imap keep=\"true\" keep=1 \\\n");
+ log(" -imap keep=\"false\" keep=0 -remove keep=0\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing ATTRMAP pass (move or copy attributes).\n");
+
+ bool modattr_mode = false;
+ vector<std::unique_ptr<AttrmapAction>> actions;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-tocase" && argidx+1 < args.size()) {
+ auto action = new AttrmapTocase;
+ action->name = args[++argidx];
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ continue;
+ }
+ if (arg == "-rename" && argidx+2 < args.size()) {
+ auto action = new AttrmapRename;
+ action->old_name = args[++argidx];
+ action->new_name = args[++argidx];
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ continue;
+ }
+ if ((arg == "-map" || arg == "-imap") && argidx+2 < args.size()) {
+ string arg1 = args[++argidx];
+ string arg2 = args[++argidx];
+ string val1, val2;
+ size_t p = arg1.find("=");
+ if (p != string::npos) {
+ val1 = arg1.substr(p+1);
+ arg1 = arg1.substr(0, p);
+ }
+ p = arg2.find("=");
+ if (p != string::npos) {
+ val2 = arg2.substr(p+1);
+ arg2 = arg2.substr(0, p);
+ }
+ auto action = new AttrmapMap;
+ action->imap = (arg == "-map");
+ action->old_name = arg1;
+ action->new_name = arg2;
+ action->old_value = val1;
+ action->new_value = val2;
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ continue;
+ }
+ if (arg == "-remove" && argidx+1 < args.size()) {
+ string arg1 = args[++argidx], val1;
+ size_t p = arg1.find("=");
+ if (p != string::npos) {
+ val1 = arg1.substr(p+1);
+ arg1 = arg1.substr(0, p);
+ }
+ auto action = new AttrmapRemove;
+ action->name = arg1;
+ action->value = val1;
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ continue;
+ }
+ if (arg == "-modattr") {
+ modattr_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (modattr_mode)
+ {
+ for (auto module : design->selected_whole_modules())
+ attrmap_apply(stringf("%s", log_id(module)), actions, module->attributes);
+ }
+ else
+ {
+ for (auto module : design->selected_modules())
+ {
+ for (auto wire : module->selected_wires())
+ attrmap_apply(stringf("%s.%s", log_id(module), log_id(wire)), actions, wire->attributes);
+
+ for (auto cell : module->selected_cells())
+ attrmap_apply(stringf("%s.%s", log_id(module), log_id(cell)), actions, cell->attributes);
+ }
+ }
+ }
+} AttrmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/attrmvcp.cc b/passes/techmap/attrmvcp.cc
new file mode 100644
index 00000000..1537def0
--- /dev/null
+++ b/passes/techmap/attrmvcp.cc
@@ -0,0 +1,141 @@
+/*
+ * 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 AttrmvcpPass : public Pass {
+ AttrmvcpPass() : Pass("attrmvcp", "move or copy attributes from wires to driving cells") { }
+ virtual void help()
+ {
+ log("\n");
+ log(" attrmvcp [options] [selection]\n");
+ log("\n");
+ log("Move or copy attributes on wires to the cells driving them.\n");
+ log("\n");
+ log(" -copy\n");
+ log(" By default, attributes are moved. This will only add\n");
+ log(" the attribute to the cell, without removing it from\n");
+ log(" the wire.\n");
+ log("\n");
+ log(" -purge\n");
+ log(" If no selected cell consumes the attribute, then it is\n");
+ log(" left on the wire by default. This option will cause the\n");
+ log(" attribute to be removed from the wire, even if no selected\n");
+ log(" cell takes it.\n");
+ log("\n");
+ log(" -driven\n");
+ log(" By default, attriburtes are moved to the cell driving the\n");
+ log(" wire. With this option set it will be moved to the cell\n");
+ log(" driven by the wire instead.\n");
+ log("\n");
+ log(" -attr <attrname>\n");
+ log(" Move or copy this attribute. This option can be used\n");
+ log(" multiple times.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing ATTRMVCP pass (move or copy attributes).\n");
+
+ bool copy_mode = false;
+ bool driven_mode = false;
+ bool purge_mode = false;
+ pool<IdString> attrnames;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-copy") {
+ copy_mode = true;
+ continue;
+ }
+ if (arg == "-driven") {
+ driven_mode = true;
+ continue;
+ }
+ if (arg == "-purge") {
+ purge_mode = true;
+ continue;
+ }
+ if (arg == "-attr" && argidx+1 < args.size()) {
+ attrnames.insert(RTLIL::escape_id(args[++argidx]));
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ dict<SigBit, pool<Cell*>> net2cells;
+ SigMap sigmap(module);
+
+ for (auto cell : module->selected_cells())
+ for (auto &conn : cell->connections())
+ {
+ if (driven_mode) {
+ if (cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ net2cells[bit].insert(cell);
+ } else {
+ if (cell->output(conn.first))
+ for (auto bit : sigmap(conn.second))
+ net2cells[bit].insert(cell);
+ }
+ }
+
+ for (auto wire : module->selected_wires())
+ {
+ dict<IdString, Const> new_attributes;
+
+ for (auto attr : wire->attributes)
+ {
+ bool did_something = false;
+
+ if (!attrnames.count(attr.first)) {
+ new_attributes[attr.first] = attr.second;
+ continue;
+ }
+
+ for (auto bit : sigmap(wire))
+ if (net2cells.count(bit))
+ for (auto cell : net2cells.at(bit)) {
+ log("Moving attribute %s=%s from %s.%s to %s.%s.\n", log_id(attr.first), log_const(attr.second),
+ log_id(module), log_id(wire), log_id(module), log_id(cell));
+ cell->attributes[attr.first] = attr.second;
+ did_something = true;
+ }
+
+ if (!purge_mode && !did_something)
+ new_attributes[attr.first] = attr.second;
+ }
+
+ if (!copy_mode)
+ wire->attributes.swap(new_attributes);
+ }
+ }
+ }
+} AttrmvcpPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/deminout.cc b/passes/techmap/deminout.cc
new file mode 100644
index 00000000..ed4e4576
--- /dev/null
+++ b/passes/techmap/deminout.cc
@@ -0,0 +1,116 @@
+/*
+ * 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 DeminoutPass : public Pass {
+ DeminoutPass() : Pass("deminout", "demote inout ports to input or output") { }
+ virtual void help()
+ {
+ log("\n");
+ log(" deminout [options] [selection]\n");
+ log("\n");
+ log("\"Demote\" inout ports to input or output ports, if possible.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing DEMINOUT pass (demote inout ports to input or output).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-bits") {
+ // flag_bits = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ bool keep_running = true;
+
+ while (keep_running)
+ {
+ keep_running = false;
+
+ for (auto module : design->selected_modules())
+ {
+ SigMap sigmap(module);
+ pool<SigBit> bits_written, bits_used, bits_inout;
+ dict<SigBit, int> bits_numports;
+
+ for (auto wire : module->wires())
+ if (wire->port_id)
+ for (auto bit : sigmap(wire))
+ bits_numports[bit]++;
+
+ for (auto cell : module->cells())
+ for (auto &conn : cell->connections())
+ {
+ bool cellport_out = cell->output(conn.first) || !cell->known();
+ bool cellport_in = cell->input(conn.first) || !cell->known();
+
+ if (cellport_out && cellport_in)
+ for (auto bit : sigmap(conn.second))
+ bits_inout.insert(bit);
+
+ if (cellport_out)
+ for (auto bit : sigmap(conn.second))
+ bits_written.insert(bit);
+
+ if (cellport_in)
+ for (auto bit : sigmap(conn.second))
+ bits_used.insert(bit);
+ }
+
+ for (auto wire : module->selected_wires())
+ if (wire->port_input && wire->port_output)
+ {
+ bool new_input = false;
+ bool new_output = false;
+
+ for (auto bit : sigmap(wire))
+ {
+ if (bits_numports[bit] > 1 || bits_inout.count(bit))
+ new_input = true, new_output = true;
+
+ if (bits_written.count(bit))
+ new_output = true;
+ else if (bits_used.count(bit))
+ new_input = true;
+ }
+
+ if (new_input != new_output) {
+ log("Demoting inout port %s.%s to %s.\n", log_id(module), log_id(wire), new_input ? "input" : "output");
+ wire->port_input = new_input;
+ wire->port_output = new_output;
+ keep_running = true;
+ }
+ }
+ }
+ }
+ }
+} DeminoutPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/dff2dffe.cc b/passes/techmap/dff2dffe.cc
new file mode 100644
index 00000000..1b8920bb
--- /dev/null
+++ b/passes/techmap/dff2dffe.cc
@@ -0,0 +1,369 @@
+/*
+ * 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"
+#include "passes/techmap/simplemap.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct Dff2dffeWorker
+{
+ const dict<IdString, IdString> &direct_dict;
+
+ RTLIL::Module *module;
+ SigMap sigmap;
+ CellTypes ct;
+
+ typedef std::pair<RTLIL::Cell*, int> cell_int_t;
+ std::map<RTLIL::SigBit, cell_int_t> bit2mux;
+ std::vector<RTLIL::Cell*> dff_cells;
+ std::map<RTLIL::SigBit, int> bitusers;
+
+ typedef std::map<RTLIL::SigBit, bool> pattern_t;
+ typedef std::set<pattern_t> patterns_t;
+
+
+ Dff2dffeWorker(RTLIL::Module *module, const dict<IdString, IdString> &direct_dict) :
+ direct_dict(direct_dict), module(module), sigmap(module), ct(module->design)
+ {
+ for (auto wire : module->wires()) {
+ if (wire->port_output)
+ for (auto bit : sigmap(wire))
+ bitusers[bit]++;
+ }
+
+ for (auto cell : module->cells()) {
+ if (cell->type == "$mux" || cell->type == "$pmux" || cell->type == "$_MUX_") {
+ RTLIL::SigSpec sig_y = sigmap(cell->getPort("\\Y"));
+ for (int i = 0; i < GetSize(sig_y); i++)
+ bit2mux[sig_y[i]] = cell_int_t(cell, i);
+ }
+ if (direct_dict.empty()) {
+ if (cell->type == "$dff" || cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_")
+ dff_cells.push_back(cell);
+ } else {
+ if (direct_dict.count(cell->type))
+ dff_cells.push_back(cell);
+ }
+ for (auto conn : cell->connections()) {
+ if (ct.cell_output(cell->type, conn.first))
+ continue;
+ for (auto bit : sigmap(conn.second))
+ bitusers[bit]++;
+ }
+ }
+ }
+
+ patterns_t find_muxtree_feedback_patterns(RTLIL::SigBit d, RTLIL::SigBit q, pattern_t path)
+ {
+ patterns_t ret;
+
+ if (d == q) {
+ ret.insert(path);
+ return ret;
+ }
+
+ if (bit2mux.count(d) == 0 || bitusers[d] > 1)
+ return ret;
+
+ cell_int_t mux_cell_int = bit2mux.at(d);
+ RTLIL::SigSpec sig_a = sigmap(mux_cell_int.first->getPort("\\A"));
+ RTLIL::SigSpec sig_b = sigmap(mux_cell_int.first->getPort("\\B"));
+ RTLIL::SigSpec sig_s = sigmap(mux_cell_int.first->getPort("\\S"));
+ int width = GetSize(sig_a), index = mux_cell_int.second;
+
+ for (int i = 0; i < GetSize(sig_s); i++)
+ if (path.count(sig_s[i]) && path.at(sig_s[i]))
+ {
+ ret = find_muxtree_feedback_patterns(sig_b[i*width + index], q, path);
+
+ if (sig_b[i*width + index] == q) {
+ RTLIL::SigSpec s = mux_cell_int.first->getPort("\\B");
+ s[i*width + index] = RTLIL::Sx;
+ mux_cell_int.first->setPort("\\B", s);
+ }
+
+ return ret;
+ }
+
+ pattern_t path_else = path;
+
+ for (int i = 0; i < GetSize(sig_s); i++)
+ {
+ if (path.count(sig_s[i]))
+ continue;
+
+ pattern_t path_this = path;
+ path_else[sig_s[i]] = false;
+ path_this[sig_s[i]] = true;
+
+ for (auto &pat : find_muxtree_feedback_patterns(sig_b[i*width + index], q, path_this))
+ ret.insert(pat);
+
+ if (sig_b[i*width + index] == q) {
+ RTLIL::SigSpec s = mux_cell_int.first->getPort("\\B");
+ s[i*width + index] = RTLIL::Sx;
+ mux_cell_int.first->setPort("\\B", s);
+ }
+ }
+
+ for (auto &pat : find_muxtree_feedback_patterns(sig_a[index], q, path_else))
+ ret.insert(pat);
+
+ if (sig_a[index] == q) {
+ RTLIL::SigSpec s = mux_cell_int.first->getPort("\\A");
+ s[index] = RTLIL::Sx;
+ mux_cell_int.first->setPort("\\A", s);
+ }
+
+ return ret;
+ }
+
+ void simplify_patterns(patterns_t&)
+ {
+ // TBD
+ }
+
+ RTLIL::SigSpec make_patterns_logic(patterns_t patterns, bool make_gates)
+ {
+ RTLIL::SigSpec or_input;
+
+ for (auto pat : patterns)
+ {
+ RTLIL::SigSpec s1, s2;
+ for (auto it : pat) {
+ s1.append(it.first);
+ s2.append(it.second);
+ }
+
+ RTLIL::SigSpec y = module->addWire(NEW_ID);
+ RTLIL::Cell *c = module->addNe(NEW_ID, s1, s2, y);
+
+ if (make_gates) {
+ simplemap(module, c);
+ module->remove(c);
+ }
+
+ or_input.append(y);
+ }
+
+ if (GetSize(or_input) == 0)
+ return RTLIL::S1;
+
+ if (GetSize(or_input) == 1)
+ return or_input;
+
+ RTLIL::SigSpec y = module->addWire(NEW_ID);
+ RTLIL::Cell *c = module->addReduceAnd(NEW_ID, or_input, y);
+
+ if (make_gates) {
+ simplemap(module, c);
+ module->remove(c);
+ }
+
+ return y;
+ }
+
+ void handle_dff_cell(RTLIL::Cell *dff_cell)
+ {
+ RTLIL::SigSpec sig_d = sigmap(dff_cell->getPort("\\D"));
+ RTLIL::SigSpec sig_q = sigmap(dff_cell->getPort("\\Q"));
+
+ std::map<patterns_t, std::set<int>> grouped_patterns;
+ std::set<int> remaining_indices;
+
+ for (int i = 0 ; i < GetSize(sig_d); i++) {
+ patterns_t patterns = find_muxtree_feedback_patterns(sig_d[i], sig_q[i], pattern_t());
+ if (!patterns.empty()) {
+ simplify_patterns(patterns);
+ grouped_patterns[patterns].insert(i);
+ } else
+ remaining_indices.insert(i);
+ }
+
+ for (auto &it : grouped_patterns) {
+ RTLIL::SigSpec new_sig_d, new_sig_q;
+ for (int i : it.second) {
+ new_sig_d.append(sig_d[i]);
+ new_sig_q.append(sig_q[i]);
+ }
+ if (!direct_dict.empty()) {
+ log(" converting %s cell %s to %s for %s -> %s.\n", log_id(dff_cell->type), log_id(dff_cell), log_id(direct_dict.at(dff_cell->type)), log_signal(new_sig_d), log_signal(new_sig_q));
+ dff_cell->setPort("\\E", make_patterns_logic(it.first, true));
+ dff_cell->type = direct_dict.at(dff_cell->type);
+ } else
+ if (dff_cell->type == "$dff") {
+ RTLIL::Cell *new_cell = module->addDffe(NEW_ID, dff_cell->getPort("\\CLK"), make_patterns_logic(it.first, false),
+ new_sig_d, new_sig_q, dff_cell->getParam("\\CLK_POLARITY").as_bool(), true);
+ log(" created $dffe cell %s for %s -> %s.\n", log_id(new_cell), log_signal(new_sig_d), log_signal(new_sig_q));
+ } else {
+ RTLIL::Cell *new_cell = module->addDffeGate(NEW_ID, dff_cell->getPort("\\C"), make_patterns_logic(it.first, true),
+ new_sig_d, new_sig_q, dff_cell->type == "$_DFF_P_", true);
+ log(" created %s cell %s for %s -> %s.\n", log_id(new_cell->type), log_id(new_cell), log_signal(new_sig_d), log_signal(new_sig_q));
+ }
+ }
+
+ if (!direct_dict.empty())
+ return;
+
+ if (remaining_indices.empty()) {
+ log(" removing now obsolete cell %s.\n", log_id(dff_cell));
+ module->remove(dff_cell);
+ } else if (GetSize(remaining_indices) != GetSize(sig_d)) {
+ log(" removing %d now obsolete bits from cell %s.\n", GetSize(sig_d) - GetSize(remaining_indices), log_id(dff_cell));
+ RTLIL::SigSpec new_sig_d, new_sig_q;
+ for (int i : remaining_indices) {
+ new_sig_d.append(sig_d[i]);
+ new_sig_q.append(sig_q[i]);
+ }
+ dff_cell->setPort("\\D", new_sig_d);
+ dff_cell->setPort("\\Q", new_sig_q);
+ dff_cell->setParam("\\WIDTH", GetSize(remaining_indices));
+ }
+ }
+
+ void run()
+ {
+ log("Transforming FF to FF+Enable cells in module %s:\n", log_id(module));
+ for (auto dff_cell : dff_cells) {
+ // log("Handling candidate %s:\n", log_id(dff_cell));
+ handle_dff_cell(dff_cell);
+ }
+ }
+};
+
+struct Dff2dffePass : public Pass {
+ Dff2dffePass() : Pass("dff2dffe", "transform $dff cells to $dffe cells") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" dff2dffe [options] [selection]\n");
+ log("\n");
+ log("This pass transforms $dff cells driven by a tree of multiplexers with one or\n");
+ log("more feedback paths to $dffe cells. It also works on gate-level cells such as\n");
+ log("$_DFF_P_, $_DFF_N_ and $_MUX_.\n");
+ log("\n");
+ log(" -unmap\n");
+ 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(" -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");
+ log(" <external_gate_type> is the cell type name for a cell with an\n");
+ log(" identical interface to the <internal_gate_type>, except it\n");
+ log(" also has an high-active enable port 'E'.\n");
+ log(" Usually <external_gate_type> is an intermediate cell type\n");
+ log(" that is then translated to the final type using 'techmap'.\n");
+ log("\n");
+ log(" -direct-match <pattern>\n");
+ log(" like -direct for all DFF cell types matching the expression.\n");
+ log(" this will use $__DFFE_* as <external_gate_type> matching the\n");
+ log(" internal gate type $_DFF_*_, except for $_DFF_[NP]_, which is\n");
+ log(" converted to $_DFFE_[NP]_.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing DFF2DFFE pass (transform $dff to $dffe where applicable).\n");
+
+ bool unmap_mode = false;
+ dict<IdString, IdString> direct_dict;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-unmap") {
+ unmap_mode = true;
+ 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]);
+ direct_dict[direct_from] = direct_to;
+ continue;
+ }
+ if (args[argidx] == "-direct-match" && argidx + 1 < args.size()) {
+ bool found_match = false;
+ const char *pattern = args[++argidx].c_str();
+ if (patmatch(pattern, "$_DFF_P_" )) found_match = true, direct_dict["$_DFF_P_" ] = "$_DFFE_PP_";
+ if (patmatch(pattern, "$_DFF_N_" )) found_match = true, direct_dict["$_DFF_N_" ] = "$_DFFE_NP_";
+ if (patmatch(pattern, "$_DFF_NN0_")) found_match = true, direct_dict["$_DFF_NN0_"] = "$__DFFE_NN0";
+ if (patmatch(pattern, "$_DFF_NN1_")) found_match = true, direct_dict["$_DFF_NN1_"] = "$__DFFE_NN1";
+ if (patmatch(pattern, "$_DFF_NP0_")) found_match = true, direct_dict["$_DFF_NP0_"] = "$__DFFE_NP0";
+ if (patmatch(pattern, "$_DFF_NP1_")) found_match = true, direct_dict["$_DFF_NP1_"] = "$__DFFE_NP1";
+ if (patmatch(pattern, "$_DFF_PN0_")) found_match = true, direct_dict["$_DFF_PN0_"] = "$__DFFE_PN0";
+ if (patmatch(pattern, "$_DFF_PN1_")) found_match = true, direct_dict["$_DFF_PN1_"] = "$__DFFE_PN1";
+ if (patmatch(pattern, "$_DFF_PP0_")) found_match = true, direct_dict["$_DFF_PP0_"] = "$__DFFE_PP0";
+ if (patmatch(pattern, "$_DFF_PP1_")) found_match = true, direct_dict["$_DFF_PP1_"] = "$__DFFE_PP1";
+ if (!found_match)
+ log_cmd_error("No cell types matched pattern '%s'.\n", pattern);
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!direct_dict.empty()) {
+ log("Selected cell types for direct conversion:\n");
+ for (auto &it : direct_dict)
+ log(" %s -> %s\n", log_id(it.first), log_id(it.second));
+ }
+
+ for (auto mod : design->selected_modules())
+ if (!mod->has_processes_warn())
+ {
+ if (unmap_mode) {
+ for (auto cell : mod->selected_cells()) {
+ if (cell->type == "$dffe") {
+ 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())
+ mod->addMux(NEW_ID, cell->getPort("\\Q"), cell->getPort("\\D"), cell->getPort("\\EN"), tmp);
+ else
+ mod->addMux(NEW_ID, cell->getPort("\\D"), cell->getPort("\\Q"), cell->getPort("\\EN"), tmp);
+ mod->remove(cell);
+ continue;
+ }
+ if (cell->type.substr(0, 7) == "$_DFFE_") {
+ 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);
+ mod->addDff(NEW_ID, cell->getPort("\\C"), tmp, cell->getPort("\\Q"), clk_pol);
+ if (en_pol)
+ mod->addMux(NEW_ID, cell->getPort("\\Q"), cell->getPort("\\D"), cell->getPort("\\E"), tmp);
+ else
+ mod->addMux(NEW_ID, cell->getPort("\\D"), cell->getPort("\\Q"), cell->getPort("\\E"), tmp);
+ mod->remove(cell);
+ continue;
+ }
+ }
+ continue;
+ }
+
+ Dff2dffeWorker worker(mod, direct_dict);
+ worker.run();
+ }
+ }
+} Dff2dffePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc
new file mode 100644
index 00000000..d737b342
--- /dev/null
+++ b/passes/techmap/dffinit.cc
@@ -0,0 +1,135 @@
+/*
+ * 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 DffinitPass : public Pass {
+ DffinitPass() : Pass("dffinit", "set INIT param on FF cells") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" dffinit [options] [selection]\n");
+ log("\n");
+ log("This pass sets an FF cell parameter to the the initial value of the net it\n");
+ log("drives. (This is primarily used in FPGA flows.)\n");
+ log("\n");
+ log(" -ff <cell_name> <output_port> <init_param>\n");
+ log(" operate on the specified cell type. this option can be used\n");
+ log(" multiple times.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing DFFINIT pass (set INIT param on FF cells).\n");
+
+ dict<IdString, dict<IdString, IdString>> ff_types;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-ff" && argidx+3 < args.size()) {
+ IdString cell_name = RTLIL::escape_id(args[++argidx]);
+ IdString output_port = RTLIL::escape_id(args[++argidx]);
+ IdString init_param = RTLIL::escape_id(args[++argidx]);
+ ff_types[cell_name][output_port] = init_param;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ SigMap sigmap(module);
+ dict<SigBit, State> init_bits;
+ pool<SigBit> cleanup_bits;
+ pool<SigBit> used_bits;
+
+ for (auto wire : module->selected_wires()) {
+ if (wire->attributes.count("\\init")) {
+ Const value = wire->attributes.at("\\init");
+ for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++)
+ init_bits[sigmap(SigBit(wire, i))] = value[i];
+ }
+ if (wire->port_output)
+ for (auto bit : sigmap(wire))
+ used_bits.insert(bit);
+ }
+
+ for (auto cell : module->selected_cells())
+ {
+ for (auto it : cell->connections())
+ if (!cell->known() || cell->input(it.first))
+ for (auto bit : sigmap(it.second))
+ used_bits.insert(bit);
+
+ if (ff_types.count(cell->type) == 0)
+ continue;
+
+ for (auto &it : ff_types[cell->type])
+ {
+ if (!cell->hasPort(it.first))
+ continue;
+
+ SigSpec sig = sigmap(cell->getPort(it.first));
+ Const value;
+
+ if (cell->hasParam(it.second))
+ value = cell->getParam(it.second);
+
+ for (int i = 0; i < GetSize(sig); i++) {
+ if (init_bits.count(sig[i]) == 0)
+ continue;
+ while (GetSize(value.bits) <= i)
+ value.bits.push_back(State::S0);
+ value.bits[i] = init_bits.at(sig[i]);
+ cleanup_bits.insert(sig[i]);
+ }
+
+ log("Setting %s.%s.%s (port=%s, net=%s) to %s.\n", log_id(module), log_id(cell), log_id(it.second),
+ log_id(it.first), log_signal(sig), log_signal(value));
+ cell->setParam(it.second, value);
+ }
+ }
+
+ for (auto wire : module->selected_wires())
+ if (wire->attributes.count("\\init")) {
+ Const &value = wire->attributes.at("\\init");
+ bool do_cleanup = true;
+ for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) {
+ SigBit bit = sigmap(SigBit(wire, i));
+ if (cleanup_bits.count(bit) || !used_bits.count(bit))
+ value[i] = State::Sx;
+ else if (value[i] != State::Sx)
+ do_cleanup = false;
+ }
+ if (do_cleanup) {
+ log("Removing init attribute from wire %s.%s.\n", log_id(module), log_id(wire));
+ wire->attributes.erase("\\init");
+ }
+ }
+ }
+ }
+} DffinitPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc
new file mode 100644
index 00000000..c8104fb7
--- /dev/null
+++ b/passes/techmap/dfflibmap.cc
@@ -0,0 +1,654 @@
+/*
+ * 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 "libparse.h"
+#include <string.h>
+#include <errno.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct cell_mapping {
+ std::string cell_name;
+ std::map<std::string, char> ports;
+};
+static std::map<RTLIL::IdString, cell_mapping> cell_mappings;
+
+static void logmap(std::string dff)
+{
+ if (cell_mappings.count(dff) == 0) {
+ log(" unmapped dff cell: %s\n", dff.c_str());
+ } else {
+ log(" %s %s (", cell_mappings[dff].cell_name.c_str(), dff.substr(1).c_str());
+ bool first = true;
+ for (auto &port : cell_mappings[dff].ports) {
+ char arg[3] = { port.second, 0, 0 };
+ if ('a' <= arg[0] && arg[0] <= 'z')
+ arg[1] = arg[0] - ('a' - 'A'), arg[0] = '~';
+ else
+ arg[1] = arg[0], arg[0] = ' ';
+ log("%s.%s(%s)", first ? "" : ", ", port.first.c_str(), arg);
+ first = false;
+ }
+ log(");\n");
+ }
+}
+
+static void logmap_all()
+{
+ logmap("$_DFF_N_");
+ logmap("$_DFF_P_");
+
+ logmap("$_DFF_NN0_");
+ logmap("$_DFF_NN1_");
+ logmap("$_DFF_NP0_");
+ logmap("$_DFF_NP1_");
+ logmap("$_DFF_PN0_");
+ logmap("$_DFF_PN1_");
+ logmap("$_DFF_PP0_");
+ logmap("$_DFF_PP1_");
+
+ logmap("$_DFFSR_NNN_");
+ logmap("$_DFFSR_NNP_");
+ logmap("$_DFFSR_NPN_");
+ logmap("$_DFFSR_NPP_");
+ logmap("$_DFFSR_PNN_");
+ logmap("$_DFFSR_PNP_");
+ logmap("$_DFFSR_PPN_");
+ logmap("$_DFFSR_PPP_");
+}
+
+static bool parse_pin(LibertyAst *cell, LibertyAst *attr, std::string &pin_name, bool &pin_pol)
+{
+ if (cell == NULL || attr == NULL || attr->value.empty())
+ return false;
+
+ std::string value = attr->value;
+
+ for (size_t pos = value.find_first_of("\" \t()"); pos != std::string::npos; pos = value.find_first_of("\" \t()"))
+ value.erase(pos, 1);
+
+ if (value[value.size()-1] == '\'') {
+ pin_name = value.substr(0, value.size()-1);
+ pin_pol = false;
+ } else if (value[0] == '!') {
+ pin_name = value.substr(1, value.size()-1);
+ pin_pol = false;
+ } else {
+ pin_name = value;
+ pin_pol = true;
+ }
+
+ for (auto child : cell->children)
+ if (child->id == "pin" && child->args.size() == 1 && child->args[0] == pin_name)
+ return true;
+ return false;
+}
+
+static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval, bool prepare_mode)
+{
+ LibertyAst *best_cell = NULL;
+ std::map<std::string, char> best_cell_ports;
+ int best_cell_pins = 0;
+ bool best_cell_noninv = false;
+ double best_cell_area = 0;
+
+ if (ast->id != "library")
+ log_error("Format error in liberty file.\n");
+
+ for (auto cell : ast->children)
+ {
+ if (cell->id != "cell" || cell->args.size() != 1)
+ continue;
+
+ LibertyAst *dn = cell->find("dont_use");
+ if (dn != NULL && dn->value == "true")
+ continue;
+
+ LibertyAst *ff = cell->find("ff");
+ if (ff == NULL)
+ continue;
+
+ std::string cell_clk_pin, cell_rst_pin, cell_next_pin;
+ bool cell_clk_pol, cell_rst_pol, cell_next_pol;
+
+ if (!parse_pin(cell, ff->find("clocked_on"), cell_clk_pin, cell_clk_pol) || cell_clk_pol != clkpol)
+ continue;
+ if (!parse_pin(cell, ff->find("next_state"), cell_next_pin, cell_next_pol))
+ continue;
+ if (has_reset && rstval == false) {
+ if (!parse_pin(cell, ff->find("clear"), cell_rst_pin, cell_rst_pol) || cell_rst_pol != rstpol)
+ continue;
+ }
+ if (has_reset && rstval == true) {
+ if (!parse_pin(cell, ff->find("preset"), cell_rst_pin, cell_rst_pol) || cell_rst_pol != rstpol)
+ continue;
+ }
+
+ std::map<std::string, char> this_cell_ports;
+ this_cell_ports[cell_clk_pin] = 'C';
+ if (has_reset)
+ this_cell_ports[cell_rst_pin] = 'R';
+ this_cell_ports[cell_next_pin] = 'D';
+
+ double area = 0;
+ LibertyAst *ar = cell->find("area");
+ if (ar != NULL && !ar->value.empty())
+ area = atof(ar->value.c_str());
+
+ int num_pins = 0;
+ bool found_output = false;
+ bool found_noninv_output = false;
+ for (auto pin : cell->children)
+ {
+ if (pin->id != "pin" || pin->args.size() != 1)
+ continue;
+
+ LibertyAst *dir = pin->find("direction");
+ if (dir == NULL || dir->value == "internal")
+ continue;
+ num_pins++;
+
+ if (dir->value == "input" && this_cell_ports.count(pin->args[0]) == 0)
+ goto continue_cell_loop;
+
+ LibertyAst *func = pin->find("function");
+ if (dir->value == "output" && func != NULL) {
+ std::string value = func->value;
+ for (size_t pos = value.find_first_of("\" \t"); pos != std::string::npos; pos = value.find_first_of("\" \t"))
+ value.erase(pos, 1);
+ if (value == ff->args[0]) {
+ this_cell_ports[pin->args[0]] = cell_next_pol ? 'Q' : 'q';
+ if (cell_next_pol)
+ found_noninv_output = true;
+ found_output = true;
+ } else
+ if (value == ff->args[1]) {
+ this_cell_ports[pin->args[0]] = cell_next_pol ? 'q' : 'Q';
+ if (!cell_next_pol)
+ found_noninv_output = true;
+ found_output = true;
+ }
+ }
+
+ if (this_cell_ports.count(pin->args[0]) == 0)
+ this_cell_ports[pin->args[0]] = 0;
+ }
+
+ if (!found_output || (best_cell != NULL && (num_pins > best_cell_pins || (best_cell_noninv && !found_noninv_output))))
+ continue;
+
+ if (best_cell != NULL && num_pins == best_cell_pins && area > best_cell_area)
+ continue;
+
+ best_cell = cell;
+ best_cell_pins = num_pins;
+ best_cell_area = area;
+ best_cell_noninv = found_noninv_output;
+ best_cell_ports.swap(this_cell_ports);
+ continue_cell_loop:;
+ }
+
+ if (best_cell != NULL) {
+ log(" cell %s (%sinv, pins=%d, area=%.2f) is a direct match for cell type %s.\n",
+ best_cell->args[0].c_str(), best_cell_noninv ? "non" : "", best_cell_pins, best_cell_area, cell_type.c_str());
+ if (prepare_mode) {
+ cell_mappings[cell_type].cell_name = cell_type;
+ cell_mappings[cell_type].ports["C"] = 'C';
+ if (has_reset)
+ cell_mappings[cell_type].ports["R"] = 'R';
+ cell_mappings[cell_type].ports["D"] = 'D';
+ cell_mappings[cell_type].ports["Q"] = 'Q';
+ } else {
+ cell_mappings[cell_type].cell_name = best_cell->args[0];
+ cell_mappings[cell_type].ports = best_cell_ports;
+ }
+ }
+}
+
+static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bool setpol, bool clrpol, bool prepare_mode)
+{
+ LibertyAst *best_cell = NULL;
+ std::map<std::string, char> best_cell_ports;
+ int best_cell_pins = 0;
+ bool best_cell_noninv = false;
+ double best_cell_area = 0;
+
+ if (ast->id != "library")
+ log_error("Format error in liberty file.\n");
+
+ for (auto cell : ast->children)
+ {
+ if (cell->id != "cell" || cell->args.size() != 1)
+ continue;
+
+ LibertyAst *ff = cell->find("ff");
+ if (ff == NULL)
+ continue;
+
+ std::string cell_clk_pin, cell_set_pin, cell_clr_pin, cell_next_pin;
+ bool cell_clk_pol, cell_set_pol, cell_clr_pol, cell_next_pol;
+
+ if (!parse_pin(cell, ff->find("clocked_on"), cell_clk_pin, cell_clk_pol) || cell_clk_pol != clkpol)
+ continue;
+ if (!parse_pin(cell, ff->find("next_state"), cell_next_pin, cell_next_pol))
+ continue;
+ if (!parse_pin(cell, ff->find("preset"), cell_set_pin, cell_set_pol) || cell_set_pol != setpol)
+ continue;
+ if (!parse_pin(cell, ff->find("clear"), cell_clr_pin, cell_clr_pol) || cell_clr_pol != clrpol)
+ continue;
+
+ std::map<std::string, char> this_cell_ports;
+ this_cell_ports[cell_clk_pin] = 'C';
+ this_cell_ports[cell_set_pin] = 'S';
+ this_cell_ports[cell_clr_pin] = 'R';
+ this_cell_ports[cell_next_pin] = 'D';
+
+ double area = 0;
+ LibertyAst *ar = cell->find("area");
+ if (ar != NULL && !ar->value.empty())
+ area = atof(ar->value.c_str());
+
+ int num_pins = 0;
+ bool found_output = false;
+ bool found_noninv_output = false;
+ for (auto pin : cell->children)
+ {
+ if (pin->id != "pin" || pin->args.size() != 1)
+ continue;
+
+ LibertyAst *dir = pin->find("direction");
+ if (dir == NULL || dir->value == "internal")
+ continue;
+ num_pins++;
+
+ if (dir->value == "input" && this_cell_ports.count(pin->args[0]) == 0)
+ goto continue_cell_loop;
+
+ LibertyAst *func = pin->find("function");
+ if (dir->value == "output" && func != NULL) {
+ std::string value = func->value;
+ for (size_t pos = value.find_first_of("\" \t"); pos != std::string::npos; pos = value.find_first_of("\" \t"))
+ value.erase(pos, 1);
+ if (value == ff->args[0]) {
+ this_cell_ports[pin->args[0]] = cell_next_pol ? 'Q' : 'q';
+ if (cell_next_pol)
+ found_noninv_output = true;
+ found_output = true;
+ } else
+ if (value == ff->args[1]) {
+ this_cell_ports[pin->args[0]] = cell_next_pol ? 'q' : 'Q';
+ if (!cell_next_pol)
+ found_noninv_output = true;
+ found_output = true;
+ }
+ }
+
+ if (this_cell_ports.count(pin->args[0]) == 0)
+ this_cell_ports[pin->args[0]] = 0;
+ }
+
+ if (!found_output || (best_cell != NULL && (num_pins > best_cell_pins || (best_cell_noninv && !found_noninv_output))))
+ continue;
+
+ if (best_cell != NULL && num_pins == best_cell_pins && area > best_cell_area)
+ continue;
+
+ best_cell = cell;
+ best_cell_pins = num_pins;
+ best_cell_area = area;
+ best_cell_noninv = found_noninv_output;
+ best_cell_ports.swap(this_cell_ports);
+ continue_cell_loop:;
+ }
+
+ if (best_cell != NULL) {
+ log(" cell %s (%sinv, pins=%d, area=%.2f) is a direct match for cell type %s.\n",
+ best_cell->args[0].c_str(), best_cell_noninv ? "non" : "", best_cell_pins, best_cell_area, cell_type.c_str());
+ if (prepare_mode) {
+ cell_mappings[cell_type].cell_name = cell_type;
+ cell_mappings[cell_type].ports["C"] = 'C';
+ cell_mappings[cell_type].ports["S"] = 'S';
+ cell_mappings[cell_type].ports["R"] = 'R';
+ cell_mappings[cell_type].ports["D"] = 'D';
+ cell_mappings[cell_type].ports["Q"] = 'Q';
+ } else {
+ cell_mappings[cell_type].cell_name = best_cell->args[0];
+ cell_mappings[cell_type].ports = best_cell_ports;
+ }
+ }
+}
+
+static bool expand_cellmap_worker(std::string from, std::string to, std::string inv)
+{
+ if (cell_mappings.count(to) > 0)
+ return false;
+
+ log(" create mapping for %s from mapping for %s.\n", to.c_str(), from.c_str());
+ cell_mappings[to].cell_name = cell_mappings[from].cell_name;
+ cell_mappings[to].ports = cell_mappings[from].ports;
+
+ for (auto &it : cell_mappings[to].ports) {
+ char cmp_ch = it.second;
+ if ('a' <= cmp_ch && cmp_ch <= 'z')
+ cmp_ch -= 'a' - 'A';
+ if (inv.find(cmp_ch) == std::string::npos)
+ continue;
+ if ('a' <= it.second && it.second <= 'z')
+ it.second -= 'a' - 'A';
+ else if ('A' <= it.second && it.second <= 'Z')
+ it.second += 'a' - 'A';
+ }
+ return true;
+}
+
+static bool expand_cellmap(std::string pattern, std::string inv)
+{
+ std::vector<std::pair<std::string, std::string>> from_to_list;
+ bool return_status = false;
+
+ for (auto &it : cell_mappings) {
+ std::string from = it.first.str(), to = it.first.str();
+ if (from.size() != pattern.size())
+ continue;
+ for (size_t i = 0; i < from.size(); i++) {
+ if (pattern[i] == '*') {
+ to[i] = from[i] == 'P' ? 'N' :
+ from[i] == 'N' ? 'P' :
+ from[i] == '1' ? '0' :
+ from[i] == '0' ? '1' : '*';
+ } else
+ if (pattern[i] != '?' && pattern[i] != from[i])
+ goto pattern_failed;
+ }
+ from_to_list.push_back(std::pair<std::string, std::string>(from, to));
+ pattern_failed:;
+ }
+
+ for (auto &it : from_to_list)
+ return_status = return_status || expand_cellmap_worker(it.first, it.second, inv);
+ return return_status;
+}
+
+static void map_sr_to_arst(const char *from, const char *to)
+{
+ if (!cell_mappings.count(from) || cell_mappings.count(to) > 0)
+ return;
+
+ char from_clk_pol YS_ATTRIBUTE(unused) = from[8];
+ char from_set_pol = from[9];
+ char from_clr_pol = from[10];
+ char to_clk_pol YS_ATTRIBUTE(unused) = to[6];
+ char to_rst_pol YS_ATTRIBUTE(unused) = to[7];
+ char to_rst_val = to[8];
+
+ log_assert(from_clk_pol == to_clk_pol);
+ log_assert(to_rst_pol == from_set_pol && to_rst_pol == from_clr_pol);
+
+ log(" create mapping for %s from mapping for %s.\n", to, from);
+ cell_mappings[to].cell_name = cell_mappings[from].cell_name;
+ cell_mappings[to].ports = cell_mappings[from].ports;
+
+ for (auto &it : cell_mappings[to].ports)
+ {
+ bool is_set_pin = it.second == 'S' || it.second == 's';
+ bool is_clr_pin = it.second == 'R' || it.second == 'r';
+
+ if (!is_set_pin && !is_clr_pin)
+ continue;
+
+ if ((to_rst_val == '0' && is_set_pin) || (to_rst_val == '1' && is_clr_pin))
+ {
+ // this is the unused set/clr pin -- deactivate it
+ if (is_set_pin)
+ it.second = (from_set_pol == 'P') == (it.second == 'S') ? '0' : '1';
+ else
+ it.second = (from_clr_pol == 'P') == (it.second == 'R') ? '0' : '1';
+ }
+ else
+ {
+ // this is the used set/clr pin -- rename it to 'reset'
+ if (it.second == 'S')
+ it.second = 'R';
+ if (it.second == 's')
+ it.second = 'r';
+ }
+ }
+}
+
+static void map_adff_to_dff(const char *from, const char *to)
+{
+ if (!cell_mappings.count(from) || cell_mappings.count(to) > 0)
+ return;
+
+ char from_clk_pol YS_ATTRIBUTE(unused) = from[6];
+ char from_rst_pol = from[7];
+ char to_clk_pol YS_ATTRIBUTE(unused) = to[6];
+
+ log_assert(from_clk_pol == to_clk_pol);
+
+ log(" create mapping for %s from mapping for %s.\n", to, from);
+ cell_mappings[to].cell_name = cell_mappings[from].cell_name;
+ cell_mappings[to].ports = cell_mappings[from].ports;
+
+ for (auto &it : cell_mappings[to].ports) {
+ if (it.second == 'S' || it.second == 'R')
+ it.second = from_rst_pol == 'P' ? '0' : '1';
+ if (it.second == 's' || it.second == 'r')
+ it.second = from_rst_pol == 'P' ? '1' : '0';
+ }
+}
+
+static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare_mode)
+{
+ log("Mapping DFF cells in module `%s':\n", module->name.c_str());
+
+ dict<SigBit, pool<Cell*>> notmap;
+ SigMap sigmap(module);
+
+ std::vector<RTLIL::Cell*> cell_list;
+ for (auto &it : module->cells_) {
+ if (design->selected(module, it.second) && cell_mappings.count(it.second->type) > 0)
+ cell_list.push_back(it.second);
+ if (it.second->type == "$_NOT_")
+ notmap[sigmap(it.second->getPort("\\A"))].insert(it.second);
+ }
+
+ std::map<std::string, int> stats;
+ for (auto cell : cell_list)
+ {
+ auto cell_type = cell->type;
+ auto cell_name = cell->name;
+ auto cell_connections = cell->connections();
+ module->remove(cell);
+
+ cell_mapping &cm = cell_mappings[cell_type];
+ RTLIL::Cell *new_cell = module->addCell(cell_name, prepare_mode ? cm.cell_name : "\\" + cm.cell_name);
+
+ bool has_q = false, has_qn = false;
+ for (auto &port : cm.ports) {
+ if (port.second == 'Q') has_q = true;
+ if (port.second == 'q') has_qn = true;
+ }
+
+ for (auto &port : cm.ports) {
+ RTLIL::SigSpec sig;
+ if ('A' <= port.second && port.second <= 'Z') {
+ sig = cell_connections[std::string("\\") + port.second];
+ } else
+ if (port.second == 'q') {
+ RTLIL::SigSpec old_sig = cell_connections[std::string("\\") + char(port.second - ('a' - 'A'))];
+ sig = module->addWire(NEW_ID, GetSize(old_sig));
+ if (has_q && has_qn) {
+ for (auto &it : notmap[sigmap(old_sig)]) {
+ module->connect(it->getPort("\\Y"), sig);
+ it->setPort("\\Y", module->addWire(NEW_ID, GetSize(old_sig)));
+ }
+ } else {
+ module->addNotGate(NEW_ID, sig, old_sig);
+ }
+ } else
+ if ('a' <= port.second && port.second <= 'z') {
+ sig = cell_connections[std::string("\\") + char(port.second - ('a' - 'A'))];
+ sig = module->NotGate(NEW_ID, sig);
+ } else
+ if (port.second == '0' || port.second == '1') {
+ sig = RTLIL::SigSpec(port.second == '0' ? 0 : 1, 1);
+ } else
+ if (port.second == 0) {
+ sig = module->addWire(NEW_ID);
+ } else
+ log_abort();
+ new_cell->setPort("\\" + port.first, sig);
+ }
+
+ stats[stringf(" mapped %%d %s cells to %s cells.\n", cell_type.c_str(), new_cell->type.c_str())]++;
+ }
+
+ for (auto &stat: stats)
+ log(stat.first.c_str(), stat.second);
+}
+
+struct DfflibmapPass : public Pass {
+ DfflibmapPass() : Pass("dfflibmap", "technology mapping of flip-flops") { }
+ virtual void help()
+ {
+ log("\n");
+ log(" dfflibmap [-prepare] -liberty <file> [selection]\n");
+ log("\n");
+ log("Map internal flip-flop cells to the flip-flop cells in the technology\n");
+ log("library specified in the given liberty file.\n");
+ log("\n");
+ log("This pass may add inverters as needed. Therefore it is recommended to\n");
+ log("first run this pass and then map the logic paths to the target technology.\n");
+ log("\n");
+ log("When called with -prepare, this command will convert the internal FF cells\n");
+ log("to the internal cell types that best match the cells found in the given\n");
+ log("liberty file.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n");
+
+ std::string liberty_file;
+ bool prepare_mode = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-liberty" && argidx+1 < args.size()) {
+ liberty_file = args[++argidx];
+ rewrite_filename(liberty_file);
+ continue;
+ }
+ if (arg == "-prepare") {
+ prepare_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (liberty_file.empty())
+ log_cmd_error("Missing `-liberty liberty_file' option!\n");
+
+ std::ifstream f;
+ f.open(liberty_file.c_str());
+ if (f.fail())
+ log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno));
+ LibertyParser libparser(f);
+ f.close();
+
+ find_cell(libparser.ast, "$_DFF_N_", false, false, false, false, prepare_mode);
+ find_cell(libparser.ast, "$_DFF_P_", true, false, false, false, prepare_mode);
+
+ find_cell(libparser.ast, "$_DFF_NN0_", false, true, false, false, prepare_mode);
+ find_cell(libparser.ast, "$_DFF_NN1_", false, true, false, true, prepare_mode);
+ find_cell(libparser.ast, "$_DFF_NP0_", false, true, true, false, prepare_mode);
+ find_cell(libparser.ast, "$_DFF_NP1_", false, true, true, true, prepare_mode);
+ find_cell(libparser.ast, "$_DFF_PN0_", true, true, false, false, prepare_mode);
+ find_cell(libparser.ast, "$_DFF_PN1_", true, true, false, true, prepare_mode);
+ find_cell(libparser.ast, "$_DFF_PP0_", true, true, true, false, prepare_mode);
+ find_cell(libparser.ast, "$_DFF_PP1_", true, true, true, true, prepare_mode);
+
+ find_cell_sr(libparser.ast, "$_DFFSR_NNN_", false, false, false, prepare_mode);
+ find_cell_sr(libparser.ast, "$_DFFSR_NNP_", false, false, true, prepare_mode);
+ find_cell_sr(libparser.ast, "$_DFFSR_NPN_", false, true, false, prepare_mode);
+ find_cell_sr(libparser.ast, "$_DFFSR_NPP_", false, true, true, prepare_mode);
+ find_cell_sr(libparser.ast, "$_DFFSR_PNN_", true, false, false, prepare_mode);
+ find_cell_sr(libparser.ast, "$_DFFSR_PNP_", true, false, true, prepare_mode);
+ find_cell_sr(libparser.ast, "$_DFFSR_PPN_", true, true, false, prepare_mode);
+ find_cell_sr(libparser.ast, "$_DFFSR_PPP_", true, true, true, prepare_mode);
+
+ // try to implement as many cells as possible just by inverting
+ // the SET and RESET pins. If necessary, implement cell types
+ // by inverting both D and Q. Only invert clock pins if there
+ // is no other way of implementing the cell.
+ while (1)
+ {
+ if (expand_cellmap("$_DFF_?*?_", "R") ||
+ expand_cellmap("$_DFFSR_?*?_", "S") ||
+ expand_cellmap("$_DFFSR_??*_", "R"))
+ continue;
+
+ if (expand_cellmap("$_DFF_??*_", "DQ"))
+ continue;
+
+ if (expand_cellmap("$_DFF_*_", "C") ||
+ expand_cellmap("$_DFF_*??_", "C") ||
+ expand_cellmap("$_DFFSR_*??_", "C"))
+ continue;
+
+ break;
+ }
+
+ map_sr_to_arst("$_DFFSR_NNN_", "$_DFF_NN0_");
+ map_sr_to_arst("$_DFFSR_NNN_", "$_DFF_NN1_");
+ map_sr_to_arst("$_DFFSR_NPP_", "$_DFF_NP0_");
+ map_sr_to_arst("$_DFFSR_NPP_", "$_DFF_NP1_");
+ map_sr_to_arst("$_DFFSR_PNN_", "$_DFF_PN0_");
+ map_sr_to_arst("$_DFFSR_PNN_", "$_DFF_PN1_");
+ map_sr_to_arst("$_DFFSR_PPP_", "$_DFF_PP0_");
+ map_sr_to_arst("$_DFFSR_PPP_", "$_DFF_PP1_");
+
+ map_adff_to_dff("$_DFF_NN0_", "$_DFF_N_");
+ map_adff_to_dff("$_DFF_NN1_", "$_DFF_N_");
+ map_adff_to_dff("$_DFF_NP0_", "$_DFF_N_");
+ map_adff_to_dff("$_DFF_NP1_", "$_DFF_N_");
+ map_adff_to_dff("$_DFF_PN0_", "$_DFF_P_");
+ map_adff_to_dff("$_DFF_PN1_", "$_DFF_P_");
+ map_adff_to_dff("$_DFF_PP0_", "$_DFF_P_");
+ map_adff_to_dff("$_DFF_PP1_", "$_DFF_P_");
+
+ log(" final dff cell mappings:\n");
+ logmap_all();
+
+ for (auto &it : design->modules_)
+ if (design->selected(it.second) && !it.second->get_bool_attribute("\\blackbox"))
+ dfflibmap(design, it.second, prepare_mode);
+
+ cell_mappings.clear();
+ }
+} DfflibmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/dffsr2dff.cc b/passes/techmap/dffsr2dff.cc
new file mode 100644
index 00000000..0d4d5362
--- /dev/null
+++ b/passes/techmap/dffsr2dff.cc
@@ -0,0 +1,213 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void dffsr_worker(SigMap &sigmap, Module *module, Cell *cell)
+{
+ if (cell->type == "$dffsr")
+ {
+ int width = cell->getParam("\\WIDTH").as_int();
+ bool setpol = cell->getParam("\\SET_POLARITY").as_bool();
+ bool clrpol = cell->getParam("\\CLR_POLARITY").as_bool();
+
+ SigBit setunused = setpol ? State::S0 : State::S1;
+ SigBit clrunused = clrpol ? State::S0 : State::S1;
+
+ SigSpec setsig = sigmap(cell->getPort("\\SET"));
+ SigSpec clrsig = sigmap(cell->getPort("\\CLR"));
+
+ Const reset_val;
+ SigSpec setctrl, clrctrl;
+
+ for (int i = 0; i < width; i++)
+ {
+ SigBit setbit = setsig[i], clrbit = clrsig[i];
+
+ if (setbit == setunused) {
+ clrctrl.append(clrbit);
+ reset_val.bits.push_back(State::S0);
+ continue;
+ }
+
+ if (clrbit == clrunused) {
+ setctrl.append(setbit);
+ reset_val.bits.push_back(State::S1);
+ continue;
+ }
+
+ return;
+ }
+
+ setctrl.sort_and_unify();
+ clrctrl.sort_and_unify();
+
+ if (GetSize(setctrl) > 1 || GetSize(clrctrl) > 1)
+ return;
+
+ if (GetSize(setctrl) == 0 && GetSize(clrctrl) == 0)
+ return;
+
+ if (GetSize(setctrl) == 1 && GetSize(clrctrl) == 1) {
+ if (setpol != clrpol)
+ return;
+ if (setctrl != clrctrl)
+ return;
+ }
+
+ log("Converting %s cell %s.%s to $adff.\n", log_id(cell->type), log_id(module), log_id(cell));
+
+ if (GetSize(setctrl) == 1) {
+ cell->setPort("\\ARST", setctrl);
+ cell->setParam("\\ARST_POLARITY", setpol);
+ } else {
+ cell->setPort("\\ARST", clrctrl);
+ cell->setParam("\\ARST_POLARITY", clrpol);
+ }
+
+ cell->type = "$adff";
+ cell->unsetPort("\\SET");
+ cell->unsetPort("\\CLR");
+ cell->setParam("\\ARST_VALUE", reset_val);
+ cell->unsetParam("\\SET_POLARITY");
+ cell->unsetParam("\\CLR_POLARITY");
+
+ return;
+ }
+
+ if (cell->type.in("$_DFFSR_NNN_", "$_DFFSR_NNP_", "$_DFFSR_NPN_", "$_DFFSR_NPP_",
+ "$_DFFSR_PNN_", "$_DFFSR_PNP_", "$_DFFSR_PPN_", "$_DFFSR_PPP_"))
+ {
+ char clkpol = cell->type.c_str()[8];
+ char setpol = cell->type.c_str()[9];
+ char clrpol = cell->type.c_str()[10];
+
+ SigBit setbit = sigmap(cell->getPort("\\S"));
+ SigBit clrbit = sigmap(cell->getPort("\\R"));
+
+ SigBit setunused = setpol == 'P' ? State::S0 : State::S1;
+ SigBit clrunused = clrpol == 'P' ? State::S0 : State::S1;
+
+ IdString oldtype = cell->type;
+
+ if (setbit == setunused) {
+ cell->type = stringf("$_DFF_%c%c0_", clkpol, clrpol);
+ cell->unsetPort("\\S");
+ goto converted_gate;
+ }
+
+ if (clrbit == clrunused) {
+ cell->type = stringf("$_DFF_%c%c1_", clkpol, setpol);
+ cell->setPort("\\R", cell->getPort("\\S"));
+ cell->unsetPort("\\S");
+ goto converted_gate;
+ }
+
+ return;
+
+ converted_gate:
+ log("Converting %s cell %s.%s to %s.\n", log_id(oldtype), log_id(module), log_id(cell), log_id(cell->type));
+ return;
+ }
+}
+
+void adff_worker(SigMap &sigmap, Module *module, Cell *cell)
+{
+ if (cell->type == "$adff")
+ {
+ bool rstpol = cell->getParam("\\ARST_POLARITY").as_bool();
+ SigBit rstunused = rstpol ? State::S0 : State::S1;
+ SigSpec rstsig = sigmap(cell->getPort("\\ARST"));
+
+ if (rstsig != rstunused)
+ return;
+
+ log("Converting %s cell %s.%s to $dff.\n", log_id(cell->type), log_id(module), log_id(cell));
+
+ cell->type = "$dff";
+ cell->unsetPort("\\ARST");
+ cell->unsetParam("\\ARST_VALUE");
+ cell->unsetParam("\\ARST_POLARITY");
+
+ return;
+ }
+
+ if (cell->type.in("$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_",
+ "$_DFF_PN0_", "$_DFF_PN1_", "$_DFF_PP0_", "$_DFF_PP1_"))
+ {
+ char clkpol = cell->type.c_str()[6];
+ char rstpol = cell->type.c_str()[7];
+
+ SigBit rstbit = sigmap(cell->getPort("\\R"));
+ SigBit rstunused = rstpol == 'P' ? State::S0 : State::S1;
+
+ if (rstbit != rstunused)
+ return;
+
+ IdString newtype = stringf("$_DFF_%c_", clkpol);
+ log("Converting %s cell %s.%s to %s.\n", log_id(cell->type), log_id(module), log_id(cell), log_id(newtype));
+
+ cell->type = newtype;
+ cell->unsetPort("\\R");
+
+ return;
+ }
+}
+
+struct Dffsr2dffPass : public Pass {
+ Dffsr2dffPass() : Pass("dffsr2dff", "convert DFFSR cells to simpler FF cell types") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" dffsr2dff [options] [selection]\n");
+ log("\n");
+ log("This pass converts DFFSR cells ($dffsr, $_DFFSR_???_) and ADFF cells ($adff,\n");
+ log("$_DFF_???_) to simpler FF cell types when any of the set/reset inputs is unused.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing DFFSR2DFF pass (mapping DFFSR cells to simpler FFs).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-v") {
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules()) {
+ SigMap sigmap(module);
+ for (auto cell : module->selected_cells()) {
+ dffsr_worker(sigmap, module, cell);
+ adff_worker(sigmap, module, cell);
+ }
+ }
+ }
+} Dffsr2dffPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/extract.cc b/passes/techmap/extract.cc
new file mode 100644
index 00000000..71e29c60
--- /dev/null
+++ b/passes/techmap/extract.cc
@@ -0,0 +1,763 @@
+/*
+ * 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/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include "libs/subcircuit/subcircuit.h"
+#include <algorithm>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+using RTLIL::id2cstr;
+
+class SubCircuitSolver : public SubCircuit::Solver
+{
+public:
+ bool ignore_parameters;
+ std::set<std::pair<RTLIL::IdString, RTLIL::IdString>> ignored_parameters;
+ std::set<RTLIL::IdString> cell_attr, wire_attr;
+
+ SubCircuitSolver() : ignore_parameters(false)
+ {
+ }
+
+ bool compareAttributes(const std::set<RTLIL::IdString> &attr, const dict<RTLIL::IdString, RTLIL::Const> &needleAttr, const dict<RTLIL::IdString, RTLIL::Const> &haystackAttr)
+ {
+ for (auto &it : attr) {
+ size_t nc = needleAttr.count(it), hc = haystackAttr.count(it);
+ if (nc != hc || (nc > 0 && needleAttr.at(it) != haystackAttr.at(it)))
+ return false;
+ }
+ return true;
+ }
+
+ RTLIL::Const unified_param(RTLIL::IdString cell_type, RTLIL::IdString param, RTLIL::Const value)
+ {
+ if (cell_type.substr(0, 1) != "$" || cell_type.substr(0, 2) == "$_")
+ return value;
+
+ #define param_bool(_n) if (param == _n) return value.as_bool();
+ param_bool("\\ARST_POLARITY");
+ param_bool("\\A_SIGNED");
+ param_bool("\\B_SIGNED");
+ param_bool("\\CLK_ENABLE");
+ param_bool("\\CLK_POLARITY");
+ param_bool("\\CLR_POLARITY");
+ param_bool("\\EN_POLARITY");
+ param_bool("\\SET_POLARITY");
+ param_bool("\\TRANSPARENT");
+ #undef param_bool
+
+ #define param_int(_n) if (param == _n) return value.as_int();
+ param_int("\\ABITS")
+ param_int("\\A_WIDTH")
+ param_int("\\B_WIDTH")
+ param_int("\\CTRL_IN_WIDTH")
+ param_int("\\CTRL_OUT_WIDTH")
+ param_int("\\OFFSET")
+ param_int("\\PRIORITY")
+ param_int("\\RD_PORTS")
+ param_int("\\SIZE")
+ param_int("\\STATE_BITS")
+ param_int("\\STATE_NUM")
+ param_int("\\STATE_NUM_LOG2")
+ param_int("\\STATE_RST")
+ param_int("\\S_WIDTH")
+ param_int("\\TRANS_NUM")
+ param_int("\\WIDTH")
+ param_int("\\WR_PORTS")
+ param_int("\\Y_WIDTH")
+ #undef param_int
+
+ return value;
+ }
+
+ virtual bool userCompareNodes(const std::string &, const std::string &, void *needleUserData,
+ const std::string &, const std::string &, void *haystackUserData, const std::map<std::string, std::string> &portMapping)
+ {
+ RTLIL::Cell *needleCell = (RTLIL::Cell*) needleUserData;
+ RTLIL::Cell *haystackCell = (RTLIL::Cell*) haystackUserData;
+
+ if (!needleCell || !haystackCell) {
+ log_assert(!needleCell && !haystackCell);
+ return true;
+ }
+
+ if (!ignore_parameters) {
+ std::map<RTLIL::IdString, RTLIL::Const> needle_param, haystack_param;
+ for (auto &it : needleCell->parameters)
+ if (!ignored_parameters.count(std::pair<RTLIL::IdString, RTLIL::IdString>(needleCell->type, it.first)))
+ needle_param[it.first] = unified_param(needleCell->type, it.first, it.second);
+ for (auto &it : haystackCell->parameters)
+ if (!ignored_parameters.count(std::pair<RTLIL::IdString, RTLIL::IdString>(haystackCell->type, it.first)))
+ haystack_param[it.first] = unified_param(haystackCell->type, it.first, it.second);
+ if (needle_param != haystack_param)
+ return false;
+ }
+
+ if (cell_attr.size() > 0 && !compareAttributes(cell_attr, needleCell->attributes, haystackCell->attributes))
+ return false;
+
+ if (wire_attr.size() > 0)
+ {
+ RTLIL::Wire *lastNeedleWire = NULL;
+ RTLIL::Wire *lastHaystackWire = NULL;
+ dict<RTLIL::IdString, RTLIL::Const> emptyAttr;
+
+ for (auto &conn : needleCell->connections())
+ {
+ RTLIL::SigSpec needleSig = conn.second;
+ RTLIL::SigSpec haystackSig = haystackCell->getPort(portMapping.at(conn.first.str()));
+
+ for (int i = 0; i < min(needleSig.size(), haystackSig.size()); i++) {
+ RTLIL::Wire *needleWire = needleSig[i].wire, *haystackWire = haystackSig[i].wire;
+ if (needleWire != lastNeedleWire || haystackWire != lastHaystackWire)
+ if (!compareAttributes(wire_attr, needleWire ? needleWire->attributes : emptyAttr, haystackWire ? haystackWire->attributes : emptyAttr))
+ return false;
+ lastNeedleWire = needleWire, lastHaystackWire = haystackWire;
+ }
+ }
+ }
+
+ return true;
+ }
+};
+
+struct bit_ref_t {
+ std::string cell, port;
+ int bit;
+};
+
+bool module2graph(SubCircuit::Graph &graph, RTLIL::Module *mod, bool constports, RTLIL::Design *sel = NULL,
+ int max_fanout = -1, std::set<std::pair<RTLIL::IdString, RTLIL::IdString>> *split = NULL)
+{
+ SigMap sigmap(mod);
+ std::map<RTLIL::SigBit, bit_ref_t> sig_bit_ref;
+
+ if (sel && !sel->selected(mod)) {
+ log(" Skipping module %s as it is not selected.\n", id2cstr(mod->name));
+ return false;
+ }
+
+ if (mod->processes.size() > 0) {
+ log(" Skipping module %s as it contains unprocessed processes.\n", id2cstr(mod->name));
+ return false;
+ }
+
+ if (constports) {
+ graph.createNode("$const$0", "$const$0", NULL, true);
+ graph.createNode("$const$1", "$const$1", NULL, true);
+ graph.createNode("$const$x", "$const$x", NULL, true);
+ graph.createNode("$const$z", "$const$z", NULL, true);
+ graph.createPort("$const$0", "\\Y", 1);
+ graph.createPort("$const$1", "\\Y", 1);
+ graph.createPort("$const$x", "\\Y", 1);
+ graph.createPort("$const$z", "\\Y", 1);
+ graph.markExtern("$const$0", "\\Y", 0);
+ graph.markExtern("$const$1", "\\Y", 0);
+ graph.markExtern("$const$x", "\\Y", 0);
+ graph.markExtern("$const$z", "\\Y", 0);
+ }
+
+ std::map<std::pair<RTLIL::Wire*, int>, int> sig_use_count;
+ if (max_fanout > 0)
+ for (auto &cell_it : mod->cells_)
+ {
+ RTLIL::Cell *cell = cell_it.second;
+ if (!sel || sel->selected(mod, cell))
+ for (auto &conn : cell->connections()) {
+ RTLIL::SigSpec conn_sig = conn.second;
+ sigmap.apply(conn_sig);
+ for (auto &bit : conn_sig)
+ if (bit.wire != NULL)
+ sig_use_count[std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset)]++;
+ }
+ }
+
+ // create graph nodes from cells
+ for (auto &cell_it : mod->cells_)
+ {
+ RTLIL::Cell *cell = cell_it.second;
+ if (sel && !sel->selected(mod, cell))
+ continue;
+
+ std::string type = cell->type.str();
+ if (sel == NULL && type.substr(0, 2) == "\\$")
+ type = type.substr(1);
+ graph.createNode(cell->name.str(), type, (void*)cell);
+
+ for (auto &conn : cell->connections())
+ {
+ graph.createPort(cell->name.str(), conn.first.str(), conn.second.size());
+
+ if (split && split->count(std::pair<RTLIL::IdString, RTLIL::IdString>(cell->type, conn.first)) > 0)
+ continue;
+
+ RTLIL::SigSpec conn_sig = conn.second;
+ sigmap.apply(conn_sig);
+
+ for (int i = 0; i < conn_sig.size(); i++)
+ {
+ auto &bit = conn_sig[i];
+
+ if (bit.wire == NULL) {
+ if (constports) {
+ std::string node = "$const$x";
+ if (bit == RTLIL::State::S0) node = "$const$0";
+ if (bit == RTLIL::State::S1) node = "$const$1";
+ if (bit == RTLIL::State::Sz) node = "$const$z";
+ graph.createConnection(cell->name.str(), conn.first.str(), i, node, "\\Y", 0);
+ } else
+ graph.createConstant(cell->name.str(), conn.first.str(), i, int(bit.data));
+ continue;
+ }
+
+ if (max_fanout > 0 && sig_use_count[std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset)] > max_fanout)
+ continue;
+
+ if (sel && !sel->selected(mod, bit.wire))
+ continue;
+
+ if (sig_bit_ref.count(bit) == 0) {
+ bit_ref_t &bit_ref = sig_bit_ref[bit];
+ bit_ref.cell = cell->name.str();
+ bit_ref.port = conn.first.str();
+ bit_ref.bit = i;
+ }
+
+ bit_ref_t &bit_ref = sig_bit_ref[bit];
+ graph.createConnection(bit_ref.cell, bit_ref.port, bit_ref.bit, cell->name.str(), conn.first.str(), i);
+ }
+ }
+ }
+
+ // mark external signals (used in non-selected cells)
+ for (auto &cell_it : mod->cells_)
+ {
+ RTLIL::Cell *cell = cell_it.second;
+ if (sel && !sel->selected(mod, cell))
+ for (auto &conn : cell->connections())
+ {
+ RTLIL::SigSpec conn_sig = conn.second;
+ sigmap.apply(conn_sig);
+
+ for (auto &bit : conn_sig)
+ if (sig_bit_ref.count(bit) != 0) {
+ bit_ref_t &bit_ref = sig_bit_ref[bit];
+ graph.markExtern(bit_ref.cell, bit_ref.port, bit_ref.bit);
+ }
+ }
+ }
+
+ // mark external signals (used in module ports)
+ for (auto &wire_it : mod->wires_)
+ {
+ RTLIL::Wire *wire = wire_it.second;
+ if (wire->port_id > 0)
+ {
+ RTLIL::SigSpec conn_sig(wire);
+ sigmap.apply(conn_sig);
+
+ for (auto &bit : conn_sig)
+ if (sig_bit_ref.count(bit) != 0) {
+ bit_ref_t &bit_ref = sig_bit_ref[bit];
+ graph.markExtern(bit_ref.cell, bit_ref.port, bit_ref.bit);
+ }
+ }
+ }
+
+ // graph.print();
+ return true;
+}
+
+RTLIL::Cell *replace(RTLIL::Module *needle, RTLIL::Module *haystack, SubCircuit::Solver::Result &match)
+{
+ SigMap sigmap(needle);
+ SigSet<std::pair<RTLIL::IdString, int>> sig2port;
+
+ // create new cell
+ RTLIL::Cell *cell = haystack->addCell(stringf("$extract$%s$%d", needle->name.c_str(), autoidx++), needle->name);
+
+ // create cell ports
+ for (auto &it : needle->wires_) {
+ RTLIL::Wire *wire = it.second;
+ if (wire->port_id > 0) {
+ for (int i = 0; i < wire->width; i++)
+ sig2port.insert(sigmap(RTLIL::SigSpec(wire, i)), std::pair<RTLIL::IdString, int>(wire->name, i));
+ cell->setPort(wire->name, RTLIL::SigSpec(RTLIL::State::Sz, wire->width));
+ }
+ }
+
+ // delete replaced cells and connect new ports
+ for (auto &it : match.mappings)
+ {
+ auto &mapping = it.second;
+ RTLIL::Cell *needle_cell = (RTLIL::Cell*)mapping.needleUserData;
+ RTLIL::Cell *haystack_cell = (RTLIL::Cell*)mapping.haystackUserData;
+
+ if (needle_cell == NULL)
+ continue;
+
+ for (auto &conn : needle_cell->connections()) {
+ RTLIL::SigSpec sig = sigmap(conn.second);
+ if (mapping.portMapping.count(conn.first.str()) > 0 && sig2port.has(sigmap(sig))) {
+ for (int i = 0; i < sig.size(); i++)
+ for (auto &port : sig2port.find(sig[i])) {
+ RTLIL::SigSpec bitsig = haystack_cell->getPort(mapping.portMapping[conn.first.str()]).extract(i, 1);
+ RTLIL::SigSpec new_sig = cell->getPort(port.first);
+ new_sig.replace(port.second, bitsig);
+ cell->setPort(port.first, new_sig);
+ }
+ }
+ }
+
+ haystack->remove(haystack_cell);
+ }
+
+ return cell;
+}
+
+bool compareSortNeedleList(RTLIL::Module *left, RTLIL::Module *right)
+{
+ int left_idx = 0, right_idx = 0;
+ if (left->attributes.count("\\extract_order") > 0)
+ left_idx = left->attributes.at("\\extract_order").as_int();
+ if (right->attributes.count("\\extract_order") > 0)
+ right_idx = right->attributes.at("\\extract_order").as_int();
+ if (left_idx != right_idx)
+ return left_idx < right_idx;
+ return left->name < right->name;
+}
+
+struct ExtractPass : public Pass {
+ ExtractPass() : Pass("extract", "find subcircuits and replace them with cells") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" extract -map <map_file> [options] [selection]\n");
+ log(" extract -mine <out_file> [options] [selection]\n");
+ log("\n");
+ log("This pass looks for subcircuits that are isomorphic to any of the modules\n");
+ log("in the given map file and replaces them with instances of this modules. The\n");
+ log("map file can be a Verilog source file (*.v) or an ilang file (*.il).\n");
+ log("\n");
+ log(" -map <map_file>\n");
+ log(" use the modules in this file as reference. This option can be used\n");
+ log(" multiple times.\n");
+ log("\n");
+ log(" -map %%<design-name>\n");
+ log(" use the modules in this in-memory design as reference. This option can\n");
+ log(" be used multiple times.\n");
+ log("\n");
+ log(" -verbose\n");
+ log(" print debug output while analyzing\n");
+ log("\n");
+ log(" -constports\n");
+ log(" also find instances with constant drivers. this may be much\n");
+ log(" slower than the normal operation.\n");
+ log("\n");
+ log(" -nodefaultswaps\n");
+ log(" normally builtin port swapping rules for internal cells are used per\n");
+ log(" default. This turns that off, so e.g. 'a^b' does not match 'b^a'\n");
+ log(" when this option is used.\n");
+ log("\n");
+ log(" -compat <needle_type> <haystack_type>\n");
+ log(" Per default, the cells in the map file (needle) must have the\n");
+ log(" type as the cells in the active design (haystack). This option\n");
+ log(" can be used to register additional pairs of types that should\n");
+ log(" match. This option can be used multiple times.\n");
+ log("\n");
+ log(" -swap <needle_type> <port1>,<port2>[,...]\n");
+ log(" Register a set of swappable ports for a needle cell type.\n");
+ log(" This option can be used multiple times.\n");
+ log("\n");
+ log(" -perm <needle_type> <port1>,<port2>[,...] <portA>,<portB>[,...]\n");
+ log(" Register a valid permutation of swappable ports for a needle\n");
+ log(" cell type. This option can be used multiple times.\n");
+ log("\n");
+ log(" -cell_attr <attribute_name>\n");
+ log(" Attributes on cells with the given name must match.\n");
+ log("\n");
+ log(" -wire_attr <attribute_name>\n");
+ log(" Attributes on wires with the given name must match.\n");
+ log("\n");
+ log(" -ignore_parameters\n");
+ log(" Do not use parameters when matching cells.\n");
+ log("\n");
+ log(" -ignore_param <cell_type> <parameter_name>\n");
+ log(" Do not use this parameter when matching cells.\n");
+ log("\n");
+ log("This pass does not operate on modules with unprocessed processes in it.\n");
+ log("(I.e. the 'proc' pass should be used first to convert processes to netlists.)\n");
+ log("\n");
+ log("This pass can also be used for mining for frequent subcircuits. In this mode\n");
+ log("the following options are to be used instead of the -map option.\n");
+ log("\n");
+ log(" -mine <out_file>\n");
+ log(" mine for frequent subcircuits and write them to the given ilang file\n");
+ log("\n");
+ log(" -mine_cells_span <min> <max>\n");
+ log(" only mine for subcircuits with the specified number of cells\n");
+ log(" default value: 3 5\n");
+ log("\n");
+ log(" -mine_min_freq <num>\n");
+ log(" only mine for subcircuits with at least the specified number of matches\n");
+ log(" default value: 10\n");
+ log("\n");
+ log(" -mine_limit_matches_per_module <num>\n");
+ log(" when calculating the number of matches for a subcircuit, don't count\n");
+ log(" more than the specified number of matches per module\n");
+ log("\n");
+ log(" -mine_max_fanout <num>\n");
+ log(" don't consider internal signals with more than <num> connections\n");
+ log("\n");
+ log("The modules in the map file may have the attribute 'extract_order' set to an\n");
+ log("integer value. Then this value is used to determine the order in which the pass\n");
+ log("tries to map the modules to the design (ascending, default value is 0).\n");
+ log("\n");
+ log("See 'help techmap' for a pass that does the opposite thing.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing EXTRACT pass (map subcircuits to cells).\n");
+ log_push();
+
+ SubCircuitSolver solver;
+
+ std::vector<std::string> map_filenames;
+ std::string mine_outfile;
+ bool constports = false;
+ bool nodefaultswaps = false;
+
+ bool mine_mode = false;
+ int mine_cells_min = 3;
+ int mine_cells_max = 5;
+ int mine_min_freq = 10;
+ int mine_limit_mod = -1;
+ int mine_max_fanout = -1;
+ std::set<std::pair<RTLIL::IdString, RTLIL::IdString>> mine_split;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-map" && argidx+1 < args.size()) {
+ if (mine_mode)
+ log_cmd_error("You cannot mix -map and -mine.\n");
+ map_filenames.push_back(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-mine" && argidx+1 < args.size()) {
+ if (!map_filenames.empty())
+ log_cmd_error("You cannot mix -map and -mine.\n");
+ mine_outfile = args[++argidx];
+ mine_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-mine_cells_span" && argidx+2 < args.size()) {
+ mine_cells_min = atoi(args[++argidx].c_str());
+ mine_cells_max = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-mine_min_freq" && argidx+1 < args.size()) {
+ mine_min_freq = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-mine_limit_matches_per_module" && argidx+1 < args.size()) {
+ mine_limit_mod = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-mine_split" && argidx+2 < args.size()) {
+ mine_split.insert(std::pair<RTLIL::IdString, RTLIL::IdString>(RTLIL::escape_id(args[argidx+1]), RTLIL::escape_id(args[argidx+2])));
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx] == "-mine_max_fanout" && argidx+1 < args.size()) {
+ mine_max_fanout = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-verbose") {
+ solver.setVerbose();
+ continue;
+ }
+ if (args[argidx] == "-constports") {
+ constports = true;
+ continue;
+ }
+ if (args[argidx] == "-nodefaultswaps") {
+ nodefaultswaps = true;
+ continue;
+ }
+ if (args[argidx] == "-compat" && argidx+2 < args.size()) {
+ std::string needle_type = RTLIL::escape_id(args[++argidx]);
+ std::string haystack_type = RTLIL::escape_id(args[++argidx]);
+ solver.addCompatibleTypes(needle_type, haystack_type);
+ continue;
+ }
+ if (args[argidx] == "-swap" && argidx+2 < args.size()) {
+ std::string type = RTLIL::escape_id(args[++argidx]);
+ std::set<std::string> ports;
+ std::string ports_str = args[++argidx], p;
+ while (!(p = next_token(ports_str, ",\t\r\n ")).empty())
+ ports.insert(RTLIL::escape_id(p));
+ solver.addSwappablePorts(type, ports);
+ continue;
+ }
+ if (args[argidx] == "-perm" && argidx+3 < args.size()) {
+ std::string type = RTLIL::escape_id(args[++argidx]);
+ std::vector<std::string> map_left, map_right;
+ std::string left_str = args[++argidx];
+ std::string right_str = args[++argidx], p;
+ while (!(p = next_token(left_str, ",\t\r\n ")).empty())
+ map_left.push_back(RTLIL::escape_id(p));
+ while (!(p = next_token(right_str, ",\t\r\n ")).empty())
+ map_right.push_back(RTLIL::escape_id(p));
+ if (map_left.size() != map_right.size())
+ log_cmd_error("Arguments to -perm are not a valid permutation!\n");
+ std::map<std::string, std::string> map;
+ for (size_t i = 0; i < map_left.size(); i++)
+ map[map_left[i]] = map_right[i];
+ std::sort(map_left.begin(), map_left.end());
+ std::sort(map_right.begin(), map_right.end());
+ if (map_left != map_right)
+ log_cmd_error("Arguments to -perm are not a valid permutation!\n");
+ solver.addSwappablePortsPermutation(type, map);
+ continue;
+ }
+ if (args[argidx] == "-cell_attr" && argidx+1 < args.size()) {
+ solver.cell_attr.insert(RTLIL::escape_id(args[++argidx]));
+ continue;
+ }
+ if (args[argidx] == "-wire_attr" && argidx+1 < args.size()) {
+ solver.wire_attr.insert(RTLIL::escape_id(args[++argidx]));
+ continue;
+ }
+ if (args[argidx] == "-ignore_parameters") {
+ solver.ignore_parameters = true;
+ continue;
+ }
+ if (args[argidx] == "-ignore_param" && argidx+2 < args.size()) {
+ solver.ignored_parameters.insert(std::pair<RTLIL::IdString, RTLIL::IdString>(RTLIL::escape_id(args[argidx+1]), RTLIL::escape_id(args[argidx+2])));
+ argidx += 2;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!nodefaultswaps) {
+ solver.addSwappablePorts("$and", "\\A", "\\B");
+ solver.addSwappablePorts("$or", "\\A", "\\B");
+ solver.addSwappablePorts("$xor", "\\A", "\\B");
+ solver.addSwappablePorts("$xnor", "\\A", "\\B");
+ solver.addSwappablePorts("$eq", "\\A", "\\B");
+ solver.addSwappablePorts("$ne", "\\A", "\\B");
+ solver.addSwappablePorts("$eqx", "\\A", "\\B");
+ solver.addSwappablePorts("$nex", "\\A", "\\B");
+ solver.addSwappablePorts("$add", "\\A", "\\B");
+ solver.addSwappablePorts("$mul", "\\A", "\\B");
+ solver.addSwappablePorts("$logic_and", "\\A", "\\B");
+ solver.addSwappablePorts("$logic_or", "\\A", "\\B");
+ solver.addSwappablePorts("$_AND_", "\\A", "\\B");
+ solver.addSwappablePorts("$_OR_", "\\A", "\\B");
+ solver.addSwappablePorts("$_XOR_", "\\A", "\\B");
+ }
+
+ if (map_filenames.empty() && mine_outfile.empty())
+ log_cmd_error("Missing option -map <verilog_or_ilang_file> or -mine <output_ilang_file>.\n");
+
+ RTLIL::Design *map = NULL;
+
+ if (!mine_mode)
+ {
+ map = new RTLIL::Design;
+ for (auto &filename : map_filenames)
+ {
+ if (filename.substr(0, 1) == "%")
+ {
+ if (!saved_designs.count(filename.substr(1))) {
+ delete map;
+ log_cmd_error("Can't saved design `%s'.\n", filename.c_str()+1);
+ }
+ for (auto mod : saved_designs.at(filename.substr(1))->modules())
+ if (!map->has(mod->name))
+ map->add(mod->clone());
+ }
+ else
+ {
+ std::ifstream f;
+ rewrite_filename(filename);
+ f.open(filename.c_str());
+ if (f.fail()) {
+ delete map;
+ log_cmd_error("Can't open map file `%s'.\n", filename.c_str());
+ }
+ Frontend::frontend_call(map, &f, filename, (filename.size() > 3 && filename.substr(filename.size()-3) == ".il") ? "ilang" : "verilog");
+ f.close();
+
+ if (filename.size() <= 3 || filename.substr(filename.size()-3) != ".il") {
+ Pass::call(map, "proc");
+ Pass::call(map, "opt_clean");
+ }
+ }
+ }
+ }
+
+ std::map<std::string, RTLIL::Module*> needle_map, haystack_map;
+ std::vector<RTLIL::Module*> needle_list;
+
+ log_header(design, "Creating graphs for SubCircuit library.\n");
+
+ if (!mine_mode)
+ for (auto &mod_it : map->modules_) {
+ SubCircuit::Graph mod_graph;
+ std::string graph_name = "needle_" + RTLIL::unescape_id(mod_it.first);
+ log("Creating needle graph %s.\n", graph_name.c_str());
+ if (module2graph(mod_graph, mod_it.second, constports)) {
+ solver.addGraph(graph_name, mod_graph);
+ needle_map[graph_name] = mod_it.second;
+ needle_list.push_back(mod_it.second);
+ }
+ }
+
+ for (auto &mod_it : design->modules_) {
+ SubCircuit::Graph mod_graph;
+ std::string graph_name = "haystack_" + RTLIL::unescape_id(mod_it.first);
+ log("Creating haystack graph %s.\n", graph_name.c_str());
+ if (module2graph(mod_graph, mod_it.second, constports, design, mine_mode ? mine_max_fanout : -1, mine_mode ? &mine_split : NULL)) {
+ solver.addGraph(graph_name, mod_graph);
+ haystack_map[graph_name] = mod_it.second;
+ }
+ }
+
+ if (!mine_mode)
+ {
+ std::vector<SubCircuit::Solver::Result> results;
+ log_header(design, "Running solver from SubCircuit library.\n");
+
+ std::sort(needle_list.begin(), needle_list.end(), compareSortNeedleList);
+
+ for (auto needle : needle_list)
+ for (auto &haystack_it : haystack_map) {
+ log("Solving for %s in %s.\n", ("needle_" + RTLIL::unescape_id(needle->name)).c_str(), haystack_it.first.c_str());
+ solver.solve(results, "needle_" + RTLIL::unescape_id(needle->name), haystack_it.first, false);
+ }
+ log("Found %d matches.\n", GetSize(results));
+
+ if (results.size() > 0)
+ {
+ log_header(design, "Substitute SubCircuits with cells.\n");
+
+ for (int i = 0; i < int(results.size()); i++) {
+ auto &result = results[i];
+ log("\nMatch #%d: (%s in %s)\n", i, result.needleGraphId.c_str(), result.haystackGraphId.c_str());
+ for (const auto &it : result.mappings) {
+ log(" %s -> %s", it.first.c_str(), it.second.haystackNodeId.c_str());
+ for (const auto & it2 : it.second.portMapping)
+ log(" %s:%s", it2.first.c_str(), it2.second.c_str());
+ log("\n");
+ }
+ RTLIL::Cell *new_cell = replace(needle_map.at(result.needleGraphId), haystack_map.at(result.haystackGraphId), result);
+ design->select(haystack_map.at(result.haystackGraphId), new_cell);
+ log(" new cell: %s\n", id2cstr(new_cell->name));
+ }
+ }
+ }
+ else
+ {
+ std::vector<SubCircuit::Solver::MineResult> results;
+
+ log_header(design, "Running miner from SubCircuit library.\n");
+ solver.mine(results, mine_cells_min, mine_cells_max, mine_min_freq, mine_limit_mod);
+
+ map = new RTLIL::Design;
+
+ int needleCounter = 0;
+ for (auto &result: results)
+ {
+ log("\nFrequent SubCircuit with %d nodes and %d matches:\n", int(result.nodes.size()), result.totalMatchesAfterLimits);
+ log(" primary match in %s:", id2cstr(haystack_map.at(result.graphId)->name));
+ for (auto &node : result.nodes)
+ log(" %s", RTLIL::unescape_id(node.nodeId).c_str());
+ log("\n");
+ for (auto &it : result.matchesPerGraph)
+ log(" matches in %s: %d\n", id2cstr(haystack_map.at(it.first)->name), it.second);
+
+ RTLIL::Module *mod = haystack_map.at(result.graphId);
+ std::set<RTLIL::Cell*> cells;
+ std::set<RTLIL::Wire*> wires;
+
+ SigMap sigmap(mod);
+
+ for (auto &node : result.nodes)
+ cells.insert((RTLIL::Cell*)node.userData);
+
+ for (auto cell : cells)
+ for (auto &conn : cell->connections()) {
+ RTLIL::SigSpec sig = sigmap(conn.second);
+ for (auto &chunk : sig.chunks())
+ if (chunk.wire != NULL)
+ wires.insert(chunk.wire);
+ }
+
+ RTLIL::Module *newMod = new RTLIL::Module;
+ newMod->name = stringf("\\needle%05d_%s_%dx", needleCounter++, id2cstr(haystack_map.at(result.graphId)->name), result.totalMatchesAfterLimits);
+ map->add(newMod);
+
+ for (auto wire : wires) {
+ RTLIL::Wire *newWire = newMod->addWire(wire->name, wire->width);
+ newWire->port_input = true;
+ newWire->port_output = true;
+ }
+
+ newMod->fixup_ports();
+
+ for (auto cell : cells) {
+ RTLIL::Cell *newCell = newMod->addCell(cell->name, cell->type);
+ newCell->parameters = cell->parameters;
+ for (auto &conn : cell->connections()) {
+ std::vector<SigChunk> chunks = sigmap(conn.second);
+ for (auto &chunk : chunks)
+ if (chunk.wire != NULL)
+ chunk.wire = newMod->wires_.at(chunk.wire->name);
+ newCell->setPort(conn.first, chunks);
+ }
+ }
+ }
+
+ std::ofstream f;
+ rewrite_filename(mine_outfile);
+ f.open(mine_outfile.c_str(), std::ofstream::trunc);
+ if (f.fail())
+ log_error("Can't open output file `%s'.\n", mine_outfile.c_str());
+ Backend::backend_call(map, &f, mine_outfile, "ilang");
+ f.close();
+ }
+
+ delete map;
+ log_pop();
+ }
+} ExtractPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/filterlib.cc b/passes/techmap/filterlib.cc
new file mode 100644
index 00000000..05cfa6d2
--- /dev/null
+++ b/passes/techmap/filterlib.cc
@@ -0,0 +1,4 @@
+
+#define FILTERLIB
+#include "libparse.cc"
+
diff --git a/passes/techmap/hilomap.cc b/passes/techmap/hilomap.cc
new file mode 100644
index 00000000..82cecac2
--- /dev/null
+++ b/passes/techmap/hilomap.cc
@@ -0,0 +1,123 @@
+/*
+ * 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/register.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+static std::string hicell_celltype, hicell_portname;
+static std::string locell_celltype, locell_portname;
+static bool singleton_mode;
+
+static RTLIL::Module *module;
+static RTLIL::SigBit last_hi, last_lo;
+
+void hilomap_worker(RTLIL::SigSpec &sig)
+{
+ for (auto &bit : sig) {
+ if (bit == RTLIL::State::S1 && !hicell_celltype.empty()) {
+ if (!singleton_mode || last_hi == RTLIL::State::Sm) {
+ last_hi = module->addWire(NEW_ID);
+ RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(hicell_celltype));
+ cell->setPort(RTLIL::escape_id(hicell_portname), last_hi);
+ }
+ bit = last_hi;
+ }
+ if (bit == RTLIL::State::S0 && !locell_celltype.empty()) {
+ if (!singleton_mode || last_lo == RTLIL::State::Sm) {
+ last_lo = module->addWire(NEW_ID);
+ RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(locell_celltype));
+ cell->setPort(RTLIL::escape_id(locell_portname), last_lo);
+ }
+ bit = last_lo;
+ }
+ }
+}
+
+struct HilomapPass : public Pass {
+ HilomapPass() : Pass("hilomap", "technology mapping of constant hi- and/or lo-drivers") { }
+ virtual void help()
+ {
+ log("\n");
+ log(" hilomap [options] [selection]\n");
+ log("\n");
+ log("Map constants to 'tielo' and 'tiehi' driver cells.\n");
+ log("\n");
+ log(" -hicell <celltype> <portname>\n");
+ log(" Replace constant hi bits with this cell.\n");
+ log("\n");
+ log(" -locell <celltype> <portname>\n");
+ log(" Replace constant lo bits with this cell.\n");
+ log("\n");
+ log(" -singleton\n");
+ log(" Create only one hi/lo cell and connect all constant bits\n");
+ log(" to that cell. Per default a separate cell is created for\n");
+ log(" each constant bit.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing HILOMAP pass (mapping to constant drivers).\n");
+
+ hicell_celltype = std::string();
+ hicell_portname = std::string();
+ locell_celltype = std::string();
+ locell_portname = std::string();
+ singleton_mode = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-hicell" && argidx+2 < args.size()) {
+ hicell_celltype = args[++argidx];
+ hicell_portname = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-locell" && argidx+2 < args.size()) {
+ locell_celltype = args[++argidx];
+ locell_portname = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-singleton") {
+ singleton_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto &it : design->modules_)
+ {
+ module = it.second;
+
+ if (!design->selected(module))
+ continue;
+
+ last_hi = RTLIL::State::Sm;
+ last_lo = RTLIL::State::Sm;
+
+ module->rewrite_sigspecs(hilomap_worker);
+ }
+ }
+} HilomapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/insbuf.cc b/passes/techmap/insbuf.cc
new file mode 100644
index 00000000..aa81468d
--- /dev/null
+++ b/passes/techmap/insbuf.cc
@@ -0,0 +1,94 @@
+/*
+ * 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 InsbufPass : public Pass {
+ InsbufPass() : Pass("insbuf", "insert buffer cells for connected wires") { }
+ virtual void help()
+ {
+ log("\n");
+ log(" insbuf [options] [selection]\n");
+ log("\n");
+ log("Insert buffer cells into the design for directly connected wires.\n");
+ log("\n");
+ log(" -buf <celltype> <in-portname> <out-portname>\n");
+ log(" Use the given cell type instead of $_BUF_. (Notice that the next\n");
+ log(" call to \"clean\" will remove all $_BUF_ in the design.)\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing INSBUF pass (insert buffer cells for connected wires).\n");
+
+ std::string celltype = "$_BUF_", in_portname = "\\A", out_portname = "\\Y";
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-buf" && argidx+3 < args.size()) {
+ celltype = args[++argidx];
+ in_portname = args[++argidx];
+ out_portname = args[++argidx];
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ std::vector<RTLIL::SigSig> new_connections;
+
+ for (auto &conn : module->connections())
+ {
+ RTLIL::SigSig new_conn;
+
+ for (int i = 0; i < GetSize(conn.first); i++)
+ {
+ SigBit lhs = conn.first[i];
+ SigBit rhs = conn.second[i];
+
+ if (lhs.wire && !design->selected(module, lhs.wire)) {
+ new_conn.first.append(lhs);
+ new_conn.second.append(rhs);
+ continue;
+ }
+
+ Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype));
+ cell->setPort(RTLIL::escape_id(in_portname), rhs);
+ cell->setPort(RTLIL::escape_id(out_portname), lhs);
+ log("Added %s.%s: %s -> %s\n", log_id(module), log_id(cell), log_signal(rhs), log_signal(lhs));
+ }
+
+ if (GetSize(new_conn.first))
+ new_connections.push_back(new_conn);
+ }
+
+ module->new_connections(new_connections);
+ }
+ }
+} InsbufPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc
new file mode 100644
index 00000000..4acbf7c0
--- /dev/null
+++ b/passes/techmap/iopadmap.cc
@@ -0,0 +1,364 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void split_portname_pair(std::string &port1, std::string &port2)
+{
+ size_t pos = port1.find_first_of(':');
+ if (pos != std::string::npos) {
+ port2 = port1.substr(pos+1);
+ port1 = port1.substr(0, pos);
+ }
+}
+
+struct IopadmapPass : public Pass {
+ IopadmapPass() : Pass("iopadmap", "technology mapping of i/o pads (or buffers)") { }
+ virtual void help()
+ {
+ log("\n");
+ log(" iopadmap [options] [selection]\n");
+ log("\n");
+ log("Map module inputs/outputs to PAD cells from a library. This pass\n");
+ log("can only map to very simple PAD cells. Use 'techmap' to further map\n");
+ log("the resulting cells to more sophisticated PAD cells.\n");
+ log("\n");
+ log(" -inpad <celltype> <portname>[:<portname>]\n");
+ log(" Map module input ports to the given cell type with the\n");
+ log(" given output port name. if a 2nd portname is given, the\n");
+ log(" signal is passed through the pad call, using the 2nd\n");
+ log(" portname as the port facing the module port.\n");
+ log("\n");
+ log(" -outpad <celltype> <portname>[:<portname>]\n");
+ log(" -inoutpad <celltype> <portname>[:<portname>]\n");
+ log(" Similar to -inpad, but for output and inout ports.\n");
+ log("\n");
+ log(" -toutpad <celltype> <portname>:<portname>[:<portname>]\n");
+ log(" Merges $_TBUF_ cells into the output pad cell. This takes precedence\n");
+ log(" over the other -outpad cell. The first portname is the enable input\n");
+ log(" of the tristate driver.\n");
+ log("\n");
+ log(" -tinoutpad <celltype> <portname>:<portname>:<portname>[:<portname>]\n");
+ log(" Merges $_TBUF_ cells into the inout pad cell. This takes precedence\n");
+ log(" over the other -inoutpad cell. The first portname is the enable input\n");
+ log(" of the tristate driver and the 2nd portname is the internal output\n");
+ log(" buffering the external signal.\n");
+ log("\n");
+ log(" -widthparam <param_name>\n");
+ log(" Use the specified parameter name to set the port width.\n");
+ log("\n");
+ log(" -nameparam <param_name>\n");
+ log(" Use the specified parameter to set the port name.\n");
+ log("\n");
+ log(" -bits\n");
+ log(" create individual bit-wide buffers even for ports that\n");
+ log(" are wider. (the default behavior is to create word-wide\n");
+ log(" buffers using -widthparam to set the word size on the cell.)\n");
+ log("\n");
+ log("Tristate PADS (-toutpad, -tinoutpad) always operate in -bits mode.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing IOPADMAP pass (mapping inputs/outputs to IO-PAD cells).\n");
+
+ std::string inpad_celltype, inpad_portname, inpad_portname2;
+ std::string outpad_celltype, outpad_portname, outpad_portname2;
+ std::string inoutpad_celltype, inoutpad_portname, inoutpad_portname2;
+ std::string toutpad_celltype, toutpad_portname, toutpad_portname2, toutpad_portname3;
+ std::string tinoutpad_celltype, tinoutpad_portname, tinoutpad_portname2, tinoutpad_portname3, tinoutpad_portname4;
+ std::string widthparam, nameparam;
+ bool flag_bits = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-inpad" && argidx+2 < args.size()) {
+ inpad_celltype = args[++argidx];
+ inpad_portname = args[++argidx];
+ split_portname_pair(inpad_portname, inpad_portname2);
+ continue;
+ }
+ if (arg == "-outpad" && argidx+2 < args.size()) {
+ outpad_celltype = args[++argidx];
+ outpad_portname = args[++argidx];
+ split_portname_pair(outpad_portname, outpad_portname2);
+ continue;
+ }
+ if (arg == "-inoutpad" && argidx+2 < args.size()) {
+ inoutpad_celltype = args[++argidx];
+ inoutpad_portname = args[++argidx];
+ split_portname_pair(inoutpad_portname, inoutpad_portname2);
+ continue;
+ }
+ if (arg == "-toutpad" && argidx+2 < args.size()) {
+ toutpad_celltype = args[++argidx];
+ toutpad_portname = args[++argidx];
+ split_portname_pair(toutpad_portname, toutpad_portname2);
+ split_portname_pair(toutpad_portname2, toutpad_portname3);
+ continue;
+ }
+ if (arg == "-tinoutpad" && argidx+2 < args.size()) {
+ tinoutpad_celltype = args[++argidx];
+ tinoutpad_portname = args[++argidx];
+ split_portname_pair(tinoutpad_portname, tinoutpad_portname2);
+ split_portname_pair(tinoutpad_portname2, tinoutpad_portname3);
+ split_portname_pair(tinoutpad_portname3, tinoutpad_portname4);
+ continue;
+ }
+ if (arg == "-widthparam" && argidx+1 < args.size()) {
+ widthparam = args[++argidx];
+ continue;
+ }
+ if (arg == "-nameparam" && argidx+1 < args.size()) {
+ nameparam = args[++argidx];
+ continue;
+ }
+ if (arg == "-bits") {
+ flag_bits = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ dict<IdString, pool<int>> skip_wires;
+
+ if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty())
+ {
+ SigMap sigmap(module);
+ dict<SigBit, pair<IdString, pool<IdString>>> tbuf_bits;
+
+ for (auto cell : module->cells())
+ if (cell->type == "$_TBUF_") {
+ SigBit bit = sigmap(cell->getPort("\\Y").as_bit());
+ tbuf_bits[bit].first = cell->name;
+ }
+
+ for (auto cell : module->cells())
+ for (auto port : cell->connections())
+ for (auto bit : sigmap(port.second))
+ if (tbuf_bits.count(bit))
+ tbuf_bits.at(bit).second.insert(cell->name);
+
+ for (auto wire : module->selected_wires())
+ {
+ if (!wire->port_output)
+ continue;
+
+ for (int i = 0; i < GetSize(wire); i++)
+ {
+ SigBit wire_bit(wire, i);
+ SigBit mapped_wire_bit = sigmap(wire_bit);
+
+ if (tbuf_bits.count(mapped_wire_bit) == 0)
+ continue;
+
+ auto &tbuf_cache = tbuf_bits.at(mapped_wire_bit);
+ Cell *tbuf_cell = module->cell(tbuf_cache.first);
+
+ if (tbuf_cell == nullptr)
+ continue;
+
+ SigBit en_sig = tbuf_cell->getPort("\\E").as_bit();
+ SigBit data_sig = tbuf_cell->getPort("\\A").as_bit();
+
+ if (wire->port_input && !tinoutpad_celltype.empty())
+ {
+ log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, tinoutpad_celltype.c_str());
+
+ Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(tinoutpad_celltype));
+ Wire *owire = module->addWire(NEW_ID);
+
+ cell->setPort(RTLIL::escape_id(tinoutpad_portname), en_sig);
+ cell->setPort(RTLIL::escape_id(tinoutpad_portname2), owire);
+ cell->setPort(RTLIL::escape_id(tinoutpad_portname3), data_sig);
+ cell->setPort(RTLIL::escape_id(tinoutpad_portname4), wire_bit);
+ cell->attributes["\\keep"] = RTLIL::Const(1);
+
+ for (auto cn : tbuf_cache.second) {
+ auto c = module->cell(cn);
+ if (c == nullptr)
+ continue;
+ for (auto port : c->connections()) {
+ SigSpec sig = port.second;
+ bool newsig = false;
+ for (auto &bit : sig)
+ if (sigmap(bit) == mapped_wire_bit) {
+ bit = owire;
+ newsig = true;
+ }
+ if (newsig)
+ c->setPort(port.first, sig);
+ }
+ }
+
+
+ module->remove(tbuf_cell);
+ skip_wires[wire->name].insert(i);
+ continue;
+ }
+
+ if (!wire->port_input && !toutpad_celltype.empty())
+ {
+ log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, toutpad_celltype.c_str());
+
+ Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(toutpad_celltype));
+
+ cell->setPort(RTLIL::escape_id(toutpad_portname), en_sig);
+ cell->setPort(RTLIL::escape_id(toutpad_portname2), data_sig);
+ cell->setPort(RTLIL::escape_id(toutpad_portname3), wire_bit);
+ cell->attributes["\\keep"] = RTLIL::Const(1);
+
+ for (auto cn : tbuf_cache.second) {
+ auto c = module->cell(cn);
+ if (c == nullptr)
+ continue;
+ for (auto port : c->connections()) {
+ SigSpec sig = port.second;
+ bool newsig = false;
+ for (auto &bit : sig)
+ if (sigmap(bit) == mapped_wire_bit) {
+ bit = data_sig;
+ newsig = true;
+ }
+ if (newsig)
+ c->setPort(port.first, sig);
+ }
+ }
+
+ module->remove(tbuf_cell);
+ skip_wires[wire->name].insert(i);
+ continue;
+ }
+ }
+ }
+ }
+
+ for (auto wire : module->selected_wires())
+ {
+ if (!wire->port_id)
+ continue;
+
+ std::string celltype, portname, portname2;
+ pool<int> skip_bit_indices;
+
+ if (skip_wires.count(wire->name)) {
+ if (!flag_bits)
+ continue;
+ skip_bit_indices = skip_wires.at(wire->name);
+ }
+
+ if (wire->port_input && !wire->port_output) {
+ if (inpad_celltype.empty()) {
+ log("Don't map input port %s.%s: Missing option -inpad.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire->name));
+ continue;
+ }
+ celltype = inpad_celltype;
+ portname = inpad_portname;
+ portname2 = inpad_portname2;
+ } else
+ if (!wire->port_input && wire->port_output) {
+ if (outpad_celltype.empty()) {
+ log("Don't map output port %s.%s: Missing option -outpad.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire->name));
+ continue;
+ }
+ celltype = outpad_celltype;
+ portname = outpad_portname;
+ portname2 = outpad_portname2;
+ } else
+ if (wire->port_input && wire->port_output) {
+ if (inoutpad_celltype.empty()) {
+ log("Don't map inout port %s.%s: Missing option -inoutpad.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire->name));
+ continue;
+ }
+ celltype = inoutpad_celltype;
+ portname = inoutpad_portname;
+ portname2 = inoutpad_portname2;
+ } else
+ log_abort();
+
+ if (!flag_bits && wire->width != 1 && widthparam.empty()) {
+ log("Don't map multi-bit port %s.%s: Missing option -widthparam or -bits.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire->name));
+ continue;
+ }
+
+ log("Mapping port %s.%s using %s.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire->name), celltype.c_str());
+
+ RTLIL::Wire *new_wire = NULL;
+ if (!portname2.empty()) {
+ new_wire = module->addWire(NEW_ID, wire);
+ module->swap_names(new_wire, wire);
+ wire->attributes.clear();
+ }
+
+ if (flag_bits)
+ {
+ for (int i = 0; i < wire->width; i++)
+ {
+ if (skip_bit_indices.count(i)) {
+ if (wire->port_output)
+ module->connect(SigSpec(new_wire, i), SigSpec(wire, i));
+ else
+ module->connect(SigSpec(wire, i), SigSpec(new_wire, i));
+ continue;
+ }
+
+ RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype));
+ cell->setPort(RTLIL::escape_id(portname), RTLIL::SigSpec(wire, i));
+ if (!portname2.empty())
+ cell->setPort(RTLIL::escape_id(portname2), RTLIL::SigSpec(new_wire, i));
+ if (!widthparam.empty())
+ cell->parameters[RTLIL::escape_id(widthparam)] = RTLIL::Const(1);
+ if (!nameparam.empty())
+ cell->parameters[RTLIL::escape_id(nameparam)] = RTLIL::Const(stringf("%s[%d]", RTLIL::id2cstr(wire->name), i));
+ cell->attributes["\\keep"] = RTLIL::Const(1);
+ }
+ }
+ else
+ {
+ RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype));
+ cell->setPort(RTLIL::escape_id(portname), RTLIL::SigSpec(wire));
+ if (!portname2.empty())
+ cell->setPort(RTLIL::escape_id(portname2), RTLIL::SigSpec(new_wire));
+ if (!widthparam.empty())
+ cell->parameters[RTLIL::escape_id(widthparam)] = RTLIL::Const(wire->width);
+ if (!nameparam.empty())
+ cell->parameters[RTLIL::escape_id(nameparam)] = RTLIL::Const(RTLIL::id2cstr(wire->name));
+ cell->attributes["\\keep"] = RTLIL::Const(1);
+ }
+
+ wire->port_id = 0;
+ wire->port_input = false;
+ wire->port_output = false;
+ }
+
+ module->fixup_ports();
+ }
+ }
+} IopadmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc
new file mode 100644
index 00000000..d5254c02
--- /dev/null
+++ b/passes/techmap/libparse.cc
@@ -0,0 +1,634 @@
+/*
+ * 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 "libparse.h"
+#include <stdlib.h>
+#include <string.h>
+
+#include <istream>
+#include <fstream>
+#include <iostream>
+
+#ifndef FILTERLIB
+#include "kernel/log.h"
+#endif
+
+using namespace Yosys;
+
+std::set<std::string> LibertyAst::blacklist;
+std::set<std::string> LibertyAst::whitelist;
+
+LibertyAst::~LibertyAst()
+{
+ for (auto child : children)
+ delete child;
+ children.clear();
+}
+
+LibertyAst *LibertyAst::find(std::string name)
+{
+ for (auto child : children)
+ if (child->id == name)
+ return child;
+ return NULL;
+}
+
+void LibertyAst::dump(FILE *f, std::string indent, std::string path, bool path_ok)
+{
+ if (whitelist.count(path + "/*") > 0)
+ path_ok = true;
+
+ path += "/" + id;
+
+ if (blacklist.count(id) > 0 || blacklist.count(path) > 0)
+ return;
+ if (whitelist.size() > 0 && whitelist.count(id) == 0 && whitelist.count(path) == 0 && !path_ok) {
+ fprintf(stderr, "Automatically added to blacklist: %s\n", path.c_str());
+ blacklist.insert(id);
+ return;
+ }
+
+ fprintf(f, "%s%s", indent.c_str(), id.c_str());
+ if (!args.empty() || !children.empty()) {
+ fprintf(f, "(");
+ for (size_t i = 0; i < args.size(); i++)
+ fprintf(f, "%s%s", i > 0 ? ", " : "", args[i].c_str());
+ fprintf(f, ")");
+ }
+ if (!value.empty())
+ fprintf(f, " : %s", value.c_str());
+ if (!children.empty()) {
+ fprintf(f, " {\n");
+ for (size_t i = 0; i < children.size(); i++)
+ children[i]->dump(f, indent + " ", path, path_ok);
+ fprintf(f, "%s}\n", indent.c_str());
+ } else
+ fprintf(f, " ;\n");
+}
+
+int LibertyParser::lexer(std::string &str)
+{
+ int c;
+
+ do {
+ c = f.get();
+ } while (c == ' ' || c == '\t' || c == '\r');
+
+ if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.') {
+ str = c;
+ while (1) {
+ c = f.get();
+ if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.')
+ str += c;
+ else
+ break;
+ }
+ f.unget();
+ // fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str());
+ return 'v';
+ }
+
+ if (c == '"') {
+ str = "";
+ while (1) {
+ c = f.get();
+ if (c == '\n')
+ line++;
+ if (c == '"')
+ break;
+ str += c;
+ }
+ // fprintf(stderr, "LEX: string >>%s<<\n", str.c_str());
+ return 'v';
+ }
+
+ if (c == '/') {
+ c = f.get();
+ if (c == '*') {
+ int last_c = 0;
+ while (c > 0 && (last_c != '*' || c != '/')) {
+ last_c = c;
+ c = f.get();
+ if (c == '\n')
+ line++;
+ }
+ return lexer(str);
+ } else if (c == '/') {
+ while (c > 0 && c != '\n')
+ c = f.get();
+ line++;
+ return lexer(str);
+ }
+ f.unget();
+ // fprintf(stderr, "LEX: char >>/<<\n");
+ return '/';
+ }
+
+ if (c == '\\') {
+ c = f.get();
+ if (c == '\r')
+ c = f.get();
+ if (c == '\n')
+ return lexer(str);
+ f.unget();
+ return '\\';
+ }
+
+ if (c == '\n') {
+ line++;
+ return ';';
+ }
+
+ // if (c >= 32 && c < 255)
+ // fprintf(stderr, "LEX: char >>%c<<\n", c);
+ // else
+ // fprintf(stderr, "LEX: char %d\n", c);
+ return c;
+}
+
+LibertyAst *LibertyParser::parse()
+{
+ std::string str;
+
+ int tok = lexer(str);
+
+ while (tok == ';')
+ tok = lexer(str);
+
+ if (tok == '}' || tok < 0)
+ return NULL;
+
+ if (tok != 'v')
+ error();
+
+ LibertyAst *ast = new LibertyAst;
+ ast->id = str;
+
+ while (1)
+ {
+ tok = lexer(str);
+
+ if (tok == ';')
+ break;
+
+ if (tok == ':' && ast->value.empty()) {
+ tok = lexer(ast->value);
+ if (tok != 'v')
+ error();
+ continue;
+ }
+
+ if (tok == '(') {
+ while (1) {
+ std::string arg;
+ tok = lexer(arg);
+ if (tok == ',')
+ continue;
+ if (tok == ')')
+ break;
+ if (tok != 'v')
+ error();
+ ast->args.push_back(arg);
+ }
+ continue;
+ }
+
+ if (tok == '{') {
+ while (1) {
+ LibertyAst *child = parse();
+ if (child == NULL)
+ break;
+ ast->children.push_back(child);
+ }
+ break;
+ }
+
+ error();
+ }
+
+ return ast;
+}
+
+#ifndef FILTERLIB
+
+void LibertyParser::error()
+{
+ log_error("Syntax error in line %d.\n", line);
+}
+
+#else
+
+void LibertyParser::error()
+{
+ fprintf(stderr, "Syntax error in line %d.\n", line);
+ exit(1);
+}
+
+/**** BEGIN: http://svn.clifford.at/tools/trunk/examples/check.h ****/
+
+#define CHECK_NV(result, check) \
+ do { \
+ auto _R = (result); \
+ if (!(_R check)) { \
+ fprintf(stderr, "Error from '%s' (%ld %s) in %s:%d.\n", \
+ #result, (long int)_R, #check, __FILE__, __LINE__); \
+ abort(); \
+ } \
+ } while(0)
+
+#define CHECK_COND(result) \
+ do { \
+ if (!(result)) { \
+ fprintf(stderr, "Error from '%s' in %s:%d.\n", \
+ #result, __FILE__, __LINE__); \
+ abort(); \
+ } \
+ } while(0)
+
+/**** END: http://svn.clifford.at/tools/trunk/examples/check.h ****/
+
+LibertyAst *find_non_null(LibertyAst *node, const char *name)
+{
+ LibertyAst *ret = node->find(name);
+ if (ret == NULL)
+ fprintf(stderr, "Error: expected to find `%s' node.\n", name);
+ return ret;
+}
+
+std::string func2vl(std::string str)
+{
+ for (size_t pos = str.find_first_of("\" \t"); pos != std::string::npos; pos = str.find_first_of("\" \t")) {
+ char c_left = pos > 0 ? str[pos-1] : ' ';
+ char c_right = pos+1 < str.size() ? str[pos+1] : ' ';
+ if (std::string("\" \t*+").find(c_left) != std::string::npos)
+ str.erase(pos, 1);
+ else if (std::string("\" \t*+").find(c_right) != std::string::npos)
+ str.erase(pos, 1);
+ else
+ str[pos] = '*';
+ }
+
+ std::vector<size_t> group_start;
+ for (size_t pos = 0; pos < str.size(); pos++) {
+ if (str[pos] == '(')
+ group_start.push_back(pos);
+ if (str[pos] == ')' && group_start.size() > 0) {
+ if (pos+1 < str.size() && str[pos+1] == '\'') {
+ std::string group = str.substr(group_start.back(), pos-group_start.back()+1);
+ str[group_start.back()] = '~';
+ str.replace(group_start.back()+1, group.size(), group);
+ pos++;
+ }
+ group_start.pop_back();
+ }
+ if (str[pos] == '\'' && pos > 0) {
+ size_t start = str.find_last_of("()'*+^&| ", pos-1)+1;
+ std::string group = str.substr(start, pos-start);
+ str[start] = '~';
+ str.replace(start+1, group.size(), group);
+ }
+ if (str[pos] == '*')
+ str[pos] = '&';
+ if (str[pos] == '+')
+ str[pos] = '|';
+ }
+
+ return str;
+}
+
+void event2vl(LibertyAst *ast, std::string &edge, std::string &expr)
+{
+ edge.clear();
+ expr.clear();
+
+ if (ast != NULL) {
+ expr = func2vl(ast->value);
+ if (expr.size() > 0 && expr[0] == '~')
+ edge = "negedge " + expr.substr(1);
+ else
+ edge = "posedge " + expr;
+ }
+}
+
+void clear_preset_var(std::string var, std::string type)
+{
+ if (type.find('L') != std::string::npos) {
+ printf(" %s <= 0;\n", var.c_str());
+ return;
+ }
+ if (type.find('H') != std::string::npos) {
+ printf(" %s <= 1;\n", var.c_str());
+ return;
+ }
+ if (type.find('T') != std::string::npos) {
+ printf(" %s <= ~%s;\n", var.c_str(), var.c_str());
+ return;
+ }
+ if (type.find('X') != std::string::npos) {
+ printf(" %s <= 'bx;\n", var.c_str());
+ return;
+ }
+}
+
+void gen_verilogsim_cell(LibertyAst *ast)
+{
+ if (ast->find("statetable") != NULL)
+ return;
+
+ CHECK_NV(ast->args.size(), == 1);
+ printf("module %s (", ast->args[0].c_str());
+ bool first = true;
+ for (auto child : ast->children) {
+ if (child->id != "pin")
+ continue;
+ CHECK_NV(child->args.size(), == 1);
+ printf("%s%s", first ? "" : ", ", child->args[0].c_str());
+ first = false;
+ }
+ printf(");\n");
+
+ for (auto child : ast->children) {
+ if (child->id != "ff" && child->id != "latch")
+ continue;
+ printf(" reg ");
+ first = true;
+ for (auto arg : child->args) {
+ printf("%s%s", first ? "" : ", ", arg.c_str());
+ first = false;
+ }
+ printf(";\n");
+ }
+
+ for (auto child : ast->children) {
+ if (child->id != "pin")
+ continue;
+ CHECK_NV(child->args.size(), == 1);
+ LibertyAst *dir = find_non_null(child, "direction");
+ LibertyAst *func = child->find("function");
+ printf(" %s %s;\n", dir->value.c_str(), child->args[0].c_str());
+ if (func != NULL)
+ printf(" assign %s = %s; // %s\n", child->args[0].c_str(), func2vl(func->value).c_str(), func->value.c_str());
+ }
+
+ for (auto child : ast->children)
+ {
+ if (child->id != "ff" || child->args.size() != 2)
+ continue;
+
+ std::string iq_var = child->args[0];
+ std::string iqn_var = child->args[1];
+
+ std::string clock_edge, clock_expr;
+ event2vl(child->find("clocked_on"), clock_edge, clock_expr);
+
+ std::string clear_edge, clear_expr;
+ event2vl(child->find("clear"), clear_edge, clear_expr);
+
+ std::string preset_edge, preset_expr;
+ event2vl(child->find("preset"), preset_edge, preset_expr);
+
+ std::string edge = "";
+ if (!clock_edge.empty())
+ edge += (edge.empty() ? "" : ", ") + clock_edge;
+ if (!clear_edge.empty())
+ edge += (edge.empty() ? "" : ", ") + clear_edge;
+ if (!preset_edge.empty())
+ edge += (edge.empty() ? "" : ", ") + preset_edge;
+
+ if (edge.empty())
+ continue;
+
+ printf(" always @(%s) begin\n", edge.c_str());
+
+ const char *else_prefix = "";
+ if (!clear_expr.empty() && !preset_expr.empty()) {
+ printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear_expr.c_str(), preset_expr.c_str());
+ clear_preset_var(iq_var, find_non_null(child, "clear_preset_var1")->value);
+ clear_preset_var(iqn_var, find_non_null(child, "clear_preset_var2")->value);
+ printf(" end\n");
+ else_prefix = "else ";
+ }
+ if (!clear_expr.empty()) {
+ printf(" %sif (%s) begin\n", else_prefix, clear_expr.c_str());
+ printf(" %s <= 0;\n", iq_var.c_str());
+ printf(" %s <= 1;\n", iqn_var.c_str());
+ printf(" end\n");
+ else_prefix = "else ";
+ }
+ if (!preset_expr.empty()) {
+ printf(" %sif (%s) begin\n", else_prefix, preset_expr.c_str());
+ printf(" %s <= 1;\n", iq_var.c_str());
+ printf(" %s <= 0;\n", iqn_var.c_str());
+ printf(" end\n");
+ else_prefix = "else ";
+ }
+ if (*else_prefix)
+ printf(" %sbegin\n", else_prefix);
+ std::string expr = find_non_null(child, "next_state")->value;
+ printf(" // %s\n", expr.c_str());
+ printf(" %s <= %s;\n", iq_var.c_str(), func2vl(expr).c_str());
+ printf(" %s <= ~(%s);\n", iqn_var.c_str(), func2vl(expr).c_str());
+ if (*else_prefix)
+ printf(" end\n");
+
+ printf(" end\n");
+ }
+
+ for (auto child : ast->children)
+ {
+ if (child->id != "latch" || child->args.size() != 2)
+ continue;
+
+ std::string iq_var = child->args[0];
+ std::string iqn_var = child->args[1];
+
+ std::string enable_edge, enable_expr;
+ event2vl(child->find("enable"), enable_edge, enable_expr);
+
+ std::string clear_edge, clear_expr;
+ event2vl(child->find("clear"), clear_edge, clear_expr);
+
+ std::string preset_edge, preset_expr;
+ event2vl(child->find("preset"), preset_edge, preset_expr);
+
+ printf(" always @* begin\n");
+
+ const char *else_prefix = "";
+ if (!clear_expr.empty() && !preset_expr.empty()) {
+ printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear_expr.c_str(), preset_expr.c_str());
+ clear_preset_var(iq_var, find_non_null(child, "clear_preset_var1")->value);
+ clear_preset_var(iqn_var, find_non_null(child, "clear_preset_var2")->value);
+ printf(" end\n");
+ else_prefix = "else ";
+ }
+ if (!clear_expr.empty()) {
+ printf(" %sif (%s) begin\n", else_prefix, clear_expr.c_str());
+ printf(" %s <= 0;\n", iq_var.c_str());
+ printf(" %s <= 1;\n", iqn_var.c_str());
+ printf(" end\n");
+ else_prefix = "else ";
+ }
+ if (!preset_expr.empty()) {
+ printf(" %sif (%s) begin\n", else_prefix, preset_expr.c_str());
+ printf(" %s <= 1;\n", iq_var.c_str());
+ printf(" %s <= 0;\n", iqn_var.c_str());
+ printf(" end\n");
+ else_prefix = "else ";
+ }
+ if (!enable_expr.empty()) {
+ printf(" %sif (%s) begin\n", else_prefix, enable_expr.c_str());
+ std::string expr = find_non_null(child, "data_in")->value;
+ printf(" %s <= %s;\n", iq_var.c_str(), func2vl(expr).c_str());
+ printf(" %s <= ~(%s);\n", iqn_var.c_str(), func2vl(expr).c_str());
+ printf(" end\n");
+ else_prefix = "else ";
+ }
+
+ printf(" end\n");
+ }
+
+ printf("endmodule\n");
+}
+
+void gen_verilogsim(LibertyAst *ast)
+{
+ CHECK_COND(ast->id == "library");
+
+ for (auto child : ast->children)
+ if (child->id == "cell" && !child->find("dont_use"))
+ gen_verilogsim_cell(child);
+}
+
+void usage()
+{
+ fprintf(stderr, "Usage: filterlib [rules-file [liberty-file]]\n");
+ fprintf(stderr, " or: filterlib -verilogsim [liberty-file]\n");
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ bool flag_verilogsim = false;
+
+ if (argc > 3)
+ usage();
+
+ if (argc > 1)
+ {
+ if (!strcmp(argv[1], "-verilogsim"))
+ flag_verilogsim = true;
+ if (!strcmp(argv[1], "-") || !strcmp(argv[1], "-verilogsim"))
+ {
+ LibertyAst::whitelist.insert("/library");
+ LibertyAst::whitelist.insert("/library/cell");
+ LibertyAst::whitelist.insert("/library/cell/area");
+ LibertyAst::whitelist.insert("/library/cell/cell_footprint");
+ LibertyAst::whitelist.insert("/library/cell/dont_touch");
+ LibertyAst::whitelist.insert("/library/cell/dont_use");
+ LibertyAst::whitelist.insert("/library/cell/ff");
+ LibertyAst::whitelist.insert("/library/cell/ff/*");
+ LibertyAst::whitelist.insert("/library/cell/latch");
+ LibertyAst::whitelist.insert("/library/cell/latch/*");
+ LibertyAst::whitelist.insert("/library/cell/pin");
+ LibertyAst::whitelist.insert("/library/cell/pin/clock");
+ LibertyAst::whitelist.insert("/library/cell/pin/direction");
+ LibertyAst::whitelist.insert("/library/cell/pin/driver_type");
+ LibertyAst::whitelist.insert("/library/cell/pin/function");
+ LibertyAst::whitelist.insert("/library/cell/pin_opposite");
+ LibertyAst::whitelist.insert("/library/cell/pin/state_function");
+ LibertyAst::whitelist.insert("/library/cell/pin/three_state");
+ LibertyAst::whitelist.insert("/library/cell/statetable");
+ LibertyAst::whitelist.insert("/library/cell/statetable/*");
+ }
+ else
+ {
+ FILE *f = fopen(argv[1], "r");
+ if (f == NULL) {
+ fprintf(stderr, "Can't open rules file `%s'.\n", argv[1]);
+ usage();
+ }
+
+ char buffer[1024];
+ while (fgets(buffer, 1024, f) != NULL)
+ {
+ char mode = 0;
+ std::string id;
+ for (char *p = buffer; *p; p++)
+ {
+ if (*p == '-' || *p == '+') {
+ if (mode != 0)
+ goto syntax_error;
+ mode = *p;
+ continue;
+ }
+ if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == '#') {
+ if (!id.empty()) {
+ if (mode == '-')
+ LibertyAst::blacklist.insert(id);
+ else
+ if (mode == '+')
+ LibertyAst::whitelist.insert(id);
+ else
+ goto syntax_error;
+ }
+ id.clear();
+ if (*p == '#')
+ break;
+ continue;
+ }
+ id += *p;
+ continue;
+
+ syntax_error:
+ fprintf(stderr, "Syntax error in rules file:\n%s", buffer);
+ exit(1);
+ }
+ }
+ }
+ }
+
+ std::istream *f = &std::cin;
+
+ if (argc == 3) {
+ std::ifstream *ff = new std::ifstream;
+ ff->open(argv[2]);
+ if (ff->fail()) {
+ delete ff;
+ fprintf(stderr, "Can't open liberty file `%s'.\n", argv[2]);
+ usage();
+ }
+ f = ff;
+ }
+
+ LibertyParser parser(*f);
+ if (parser.ast) {
+ if (flag_verilogsim)
+ gen_verilogsim(parser.ast);
+ else
+ parser.ast->dump(stdout);
+ }
+
+ if (argc == 3)
+ delete f;
+
+ return 0;
+}
+
+#endif
+
diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h
new file mode 100644
index 00000000..cf632557
--- /dev/null
+++ b/passes/techmap/libparse.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef LIBPARSE_H
+#define LIBPARSE_H
+
+#include <stdio.h>
+#include <string>
+#include <vector>
+#include <set>
+
+namespace Yosys
+{
+ struct LibertyAst
+ {
+ std::string id, value;
+ std::vector<std::string> args;
+ std::vector<LibertyAst*> children;
+ ~LibertyAst();
+ LibertyAst *find(std::string name);
+ void dump(FILE *f, std::string indent = "", std::string path = "", bool path_ok = false);
+ static std::set<std::string> blacklist;
+ static std::set<std::string> whitelist;
+ };
+
+ struct LibertyParser
+ {
+ std::istream &f;
+ int line;
+ LibertyAst *ast;
+ LibertyParser(std::istream &f) : f(f), line(1), ast(parse()) {}
+ ~LibertyParser() { if (ast) delete ast; }
+ int lexer(std::string &str);
+ LibertyAst *parse();
+ void error();
+ };
+}
+
+#endif
+
diff --git a/passes/techmap/lut2mux.cc b/passes/techmap/lut2mux.cc
new file mode 100644
index 00000000..2bb0bd8b
--- /dev/null
+++ b/passes/techmap/lut2mux.cc
@@ -0,0 +1,93 @@
+/*
+ * 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
+
+int lut2mux(Cell *cell)
+{
+ SigSpec sig_a = cell->getPort("\\A");
+ SigSpec sig_y = cell->getPort("\\Y");
+ Const lut = cell->getParam("\\LUT");
+ int count = 1;
+
+ if (GetSize(sig_a) == 1)
+ {
+ cell->module->addMuxGate(NEW_ID, lut[0], lut[1], sig_a, sig_y);
+ }
+ else
+ {
+ SigSpec sig_a_hi = sig_a[GetSize(sig_a)-1];
+ SigSpec sig_a_lo = sig_a.extract(0, GetSize(sig_a)-1);
+ SigSpec sig_y1 = cell->module->addWire(NEW_ID);
+ SigSpec sig_y2 = cell->module->addWire(NEW_ID);
+
+ Const lut1 = lut.extract(0, GetSize(lut)/2);
+ Const lut2 = lut.extract(GetSize(lut)/2, GetSize(lut)/2);
+
+ count += lut2mux(cell->module->addLut(NEW_ID, sig_a_lo, sig_y1, lut1));
+ count += lut2mux(cell->module->addLut(NEW_ID, sig_a_lo, sig_y2, lut2));
+
+ cell->module->addMuxGate(NEW_ID, sig_y1, sig_y2, sig_a_hi, sig_y);
+ }
+
+ cell->module->remove(cell);
+ return count;
+}
+
+struct Lut2muxPass : public Pass {
+ Lut2muxPass() : Pass("lut2mux", "convert $lut to $_MUX_") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" lut2mux [options] [selection]\n");
+ log("\n");
+ log("This pass converts $lut cells to $_MUX_ gates.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing LUT2MUX pass (convert $lut to $_MUX_).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-v") {
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ for (auto cell : module->selected_cells()) {
+ if (cell->type == "$lut") {
+ IdString cell_name = cell->name;
+ int count = lut2mux(cell);
+ log("Converted %s.%s to %d MUX cells.\n", log_id(module), log_id(cell_name), count);
+ }
+ }
+ }
+} Lut2muxPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/maccmap.cc b/passes/techmap/maccmap.cc
new file mode 100644
index 00000000..32569d07
--- /dev/null
+++ b/passes/techmap/maccmap.cc
@@ -0,0 +1,404 @@
+/*
+ * 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/macc.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct MaccmapWorker
+{
+ std::vector<std::set<RTLIL::SigBit>> bits;
+ RTLIL::Module *module;
+ int width;
+
+ MaccmapWorker(RTLIL::Module *module, int width) : module(module), width(width)
+ {
+ bits.resize(width);
+ }
+
+ void add(RTLIL::SigBit bit, int position)
+ {
+ if (position >= width || bit == RTLIL::S0)
+ return;
+
+ if (bits.at(position).count(bit)) {
+ bits.at(position).erase(bit);
+ add(bit, position+1);
+ } else {
+ bits.at(position).insert(bit);
+ }
+ }
+
+ void add(RTLIL::SigSpec a, bool is_signed, bool do_subtract)
+ {
+ a.extend_u0(width, is_signed);
+
+ if (do_subtract) {
+ a = module->Not(NEW_ID, a);
+ add(RTLIL::S1, 0);
+ }
+
+ for (int i = 0; i < width; i++)
+ add(a[i], i);
+ }
+
+ void add(RTLIL::SigSpec a, RTLIL::SigSpec b, bool is_signed, bool do_subtract)
+ {
+ if (GetSize(a) < GetSize(b))
+ std::swap(a, b);
+
+ a.extend_u0(width, is_signed);
+
+ if (GetSize(b) > width)
+ b.extend_u0(width, is_signed);
+
+ for (int i = 0; i < GetSize(b); i++)
+ if (is_signed && i+1 == GetSize(b))
+ {
+ a = {module->Not(NEW_ID, a.extract(i, width-i)), RTLIL::SigSpec(0, i)};
+ add(module->And(NEW_ID, a, RTLIL::SigSpec(b[i], width)), false, do_subtract);
+ add({b[i], RTLIL::SigSpec(0, i)}, false, do_subtract);
+ }
+ else
+ {
+ add(module->And(NEW_ID, a, RTLIL::SigSpec(b[i], width)), false, do_subtract);
+ a = {a.extract(0, width-1), RTLIL::S0};
+ }
+ }
+
+ void fulladd(RTLIL::SigSpec &in1, RTLIL::SigSpec &in2, RTLIL::SigSpec &in3, RTLIL::SigSpec &out1, RTLIL::SigSpec &out2)
+ {
+ int start_index = 0, stop_index = GetSize(in1);
+
+ while (start_index < stop_index && in1[start_index] == RTLIL::S0 && in2[start_index] == RTLIL::S0 && in3[start_index] == RTLIL::S0)
+ start_index++;
+
+ while (start_index < stop_index && in1[stop_index-1] == RTLIL::S0 && in2[stop_index-1] == RTLIL::S0 && in3[stop_index-1] == RTLIL::S0)
+ stop_index--;
+
+ if (start_index == stop_index)
+ {
+ out1 = RTLIL::SigSpec(0, GetSize(in1));
+ out2 = RTLIL::SigSpec(0, GetSize(in1));
+ }
+ else
+ {
+ RTLIL::SigSpec out_zeros_lsb(0, start_index), out_zeros_msb(0, GetSize(in1)-stop_index);
+
+ in1 = in1.extract(start_index, stop_index-start_index);
+ in2 = in2.extract(start_index, stop_index-start_index);
+ in3 = in3.extract(start_index, stop_index-start_index);
+
+ int width = GetSize(in1);
+ RTLIL::Wire *w1 = module->addWire(NEW_ID, width);
+ RTLIL::Wire *w2 = module->addWire(NEW_ID, width);
+
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$fa");
+ cell->setParam("\\WIDTH", width);
+ cell->setPort("\\A", in1);
+ cell->setPort("\\B", in2);
+ cell->setPort("\\C", in3);
+ cell->setPort("\\Y", w1);
+ cell->setPort("\\X", w2);
+
+ out1 = {out_zeros_msb, w1, out_zeros_lsb};
+ out2 = {out_zeros_msb, w2, out_zeros_lsb};
+ }
+ }
+
+ int tree_bit_slots(int n)
+ {
+ #if 0
+ int retval = 1;
+ while (n > 2) {
+ retval += n / 3;
+ n = 2*(n / 3) + (n % 3);
+ }
+ return retval;
+ #else
+ return max(n - 1, 0);
+ #endif
+ }
+
+ RTLIL::SigSpec synth()
+ {
+ std::vector<RTLIL::SigSpec> summands;
+ std::vector<RTLIL::SigBit> tree_sum_bits;
+ int unique_tree_bits = 0;
+ int count_tree_words = 0;
+
+ while (1)
+ {
+ RTLIL::SigSpec summand(0, width);
+ bool got_data_bits = false;
+
+ for (int i = 0; i < width; i++)
+ if (!bits.at(i).empty()) {
+ auto it = bits.at(i).begin();
+ summand[i] = *it;
+ bits.at(i).erase(it);
+ got_data_bits = true;
+ }
+
+ if (!got_data_bits)
+ break;
+
+ summands.push_back(summand);
+
+ while (1)
+ {
+ int free_bit_slots = tree_bit_slots(GetSize(summands)) - GetSize(tree_sum_bits);
+
+ int max_depth = 0, max_position = 0;
+ for (int i = 0; i < width; i++)
+ if (max_depth <= GetSize(bits.at(i))) {
+ max_depth = GetSize(bits.at(i));
+ max_position = i;
+ }
+
+ if (max_depth == 0 || max_position > 4)
+ break;
+
+ int required_bits = 0;
+ for (int i = 0; i <= max_position; i++)
+ if (GetSize(bits.at(i)) == max_depth)
+ required_bits += 1 << i;
+
+ if (required_bits > free_bit_slots)
+ break;
+
+ for (int i = 0; i <= max_position; i++)
+ if (GetSize(bits.at(i)) == max_depth) {
+ auto it = bits.at(i).begin();
+ RTLIL::SigBit bit = *it;
+ for (int k = 0; k < (1 << i); k++, free_bit_slots--)
+ tree_sum_bits.push_back(bit);
+ bits.at(i).erase(it);
+ unique_tree_bits++;
+ }
+
+ count_tree_words++;
+ }
+ }
+
+ if (!tree_sum_bits.empty())
+ log(" packed %d (%d) bits / %d words into adder tree\n", GetSize(tree_sum_bits), unique_tree_bits, count_tree_words);
+
+ if (GetSize(summands) == 0) {
+ log_assert(tree_sum_bits.empty());
+ return RTLIL::SigSpec(0, width);
+ }
+
+ if (GetSize(summands) == 1) {
+ log_assert(tree_sum_bits.empty());
+ return summands.front();
+ }
+
+ while (GetSize(summands) > 2)
+ {
+ std::vector<RTLIL::SigSpec> new_summands;
+ for (int i = 0; i < GetSize(summands); i += 3)
+ if (i+2 < GetSize(summands)) {
+ RTLIL::SigSpec in1 = summands[i];
+ RTLIL::SigSpec in2 = summands[i+1];
+ RTLIL::SigSpec in3 = summands[i+2];
+ RTLIL::SigSpec out1, out2;
+ fulladd(in1, in2, in3, out1, out2);
+ RTLIL::SigBit extra_bit = RTLIL::S0;
+ if (!tree_sum_bits.empty()) {
+ extra_bit = tree_sum_bits.back();
+ tree_sum_bits.pop_back();
+ }
+ new_summands.push_back(out1);
+ new_summands.push_back({out2.extract(0, width-1), extra_bit});
+ } else {
+ new_summands.push_back(summands[i]);
+ i -= 2;
+ }
+ summands.swap(new_summands);
+ }
+
+
+ RTLIL::Cell *c = module->addCell(NEW_ID, "$alu");
+ c->setPort("\\A", summands.front());
+ c->setPort("\\B", summands.back());
+ c->setPort("\\CI", RTLIL::S0);
+ c->setPort("\\BI", RTLIL::S0);
+ c->setPort("\\Y", module->addWire(NEW_ID, width));
+ c->setPort("\\X", module->addWire(NEW_ID, width));
+ c->setPort("\\CO", module->addWire(NEW_ID, width));
+ c->fixup_parameters();
+
+ if (!tree_sum_bits.empty()) {
+ c->setPort("\\CI", tree_sum_bits.back());
+ tree_sum_bits.pop_back();
+ }
+ log_assert(tree_sum_bits.empty());
+
+ return c->getPort("\\Y");
+ }
+};
+
+PRIVATE_NAMESPACE_END
+YOSYS_NAMESPACE_BEGIN
+
+extern void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap = false);
+
+void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap)
+{
+ int width = GetSize(cell->getPort("\\Y"));
+
+ Macc macc;
+ macc.from_cell(cell);
+
+ RTLIL::SigSpec all_input_bits;
+ all_input_bits.append(cell->getPort("\\A"));
+ all_input_bits.append(cell->getPort("\\B"));
+
+ if (all_input_bits.to_sigbit_set().count(RTLIL::Sx)) {
+ module->connect(cell->getPort("\\Y"), RTLIL::SigSpec(RTLIL::Sx, width));
+ return;
+ }
+
+ for (auto &port : macc.ports)
+ if (GetSize(port.in_b) == 0)
+ log(" %s %s (%d bits, %s)\n", port.do_subtract ? "sub" : "add", log_signal(port.in_a),
+ GetSize(port.in_a), port.is_signed ? "signed" : "unsigned");
+ else
+ log(" %s %s * %s (%dx%d bits, %s)\n", port.do_subtract ? "sub" : "add", log_signal(port.in_a), log_signal(port.in_b),
+ GetSize(port.in_a), GetSize(port.in_b), port.is_signed ? "signed" : "unsigned");
+
+ if (GetSize(macc.bit_ports) != 0)
+ log(" add bits %s (%d bits)\n", log_signal(macc.bit_ports), GetSize(macc.bit_ports));
+
+ if (unmap)
+ {
+ typedef std::pair<RTLIL::SigSpec, bool> summand_t;
+ std::vector<summand_t> summands;
+
+ for (auto &port : macc.ports) {
+ summand_t this_summand;
+ if (GetSize(port.in_b)) {
+ this_summand.first = module->addWire(NEW_ID, width);
+ module->addMul(NEW_ID, port.in_a, port.in_b, this_summand.first, port.is_signed);
+ } else if (GetSize(port.in_a) != width) {
+ this_summand.first = module->addWire(NEW_ID, width);
+ module->addPos(NEW_ID, port.in_a, this_summand.first, port.is_signed);
+ } else {
+ this_summand.first = port.in_a;
+ }
+ this_summand.second = port.do_subtract;
+ summands.push_back(this_summand);
+ }
+
+ for (auto &bit : macc.bit_ports)
+ summands.push_back(summand_t(bit, false));
+
+ if (GetSize(summands) == 0)
+ summands.push_back(summand_t(RTLIL::SigSpec(0, width), false));
+
+ while (GetSize(summands) > 1)
+ {
+ std::vector<summand_t> new_summands;
+ for (int i = 0; i < GetSize(summands); i += 2) {
+ if (i+1 < GetSize(summands)) {
+ summand_t this_summand;
+ this_summand.first = module->addWire(NEW_ID, width);
+ this_summand.second = summands[i].second && summands[i+1].second;
+ if (summands[i].second == summands[i+1].second)
+ module->addAdd(NEW_ID, summands[i].first, summands[i+1].first, this_summand.first);
+ else if (summands[i].second)
+ module->addSub(NEW_ID, summands[i+1].first, summands[i].first, this_summand.first);
+ else if (summands[i+1].second)
+ module->addSub(NEW_ID, summands[i].first, summands[i+1].first, this_summand.first);
+ else
+ log_abort();
+ new_summands.push_back(this_summand);
+ } else
+ new_summands.push_back(summands[i]);
+ }
+ summands.swap(new_summands);
+ }
+
+ if (summands.front().second)
+ module->addNeg(NEW_ID, summands.front().first, cell->getPort("\\Y"));
+ else
+ module->connect(cell->getPort("\\Y"), summands.front().first);
+ }
+ else
+ {
+ MaccmapWorker worker(module, width);
+
+ for (auto &port : macc.ports)
+ if (GetSize(port.in_b) == 0)
+ worker.add(port.in_a, port.is_signed, port.do_subtract);
+ else
+ worker.add(port.in_a, port.in_b, port.is_signed, port.do_subtract);
+
+ for (auto &bit : macc.bit_ports)
+ worker.add(bit, 0);
+
+ module->connect(cell->getPort("\\Y"), worker.synth());
+ }
+}
+
+YOSYS_NAMESPACE_END
+PRIVATE_NAMESPACE_BEGIN
+
+struct MaccmapPass : public Pass {
+ MaccmapPass() : Pass("maccmap", "mapping macc cells") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" maccmap [-unmap] [selection]\n");
+ log("\n");
+ log("This pass maps $macc cells to yosys $fa and $alu cells. When the -unmap option\n");
+ log("is used then the $macc cell is mapped to $add, $sub, etc. cells instead.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool unmap_mode = false;
+
+ log_header(design, "Executing MACCMAP pass (map $macc cells).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-unmap") {
+ unmap_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto mod : design->selected_modules())
+ for (auto cell : mod->selected_cells())
+ if (cell->type == "$macc") {
+ log("Mapping %s.%s (%s).\n", log_id(mod), log_id(cell), log_id(cell->type));
+ maccmap(mod, cell, unmap_mode);
+ mod->remove(cell);
+ }
+ }
+} MaccmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/muxcover.cc b/passes/techmap/muxcover.cc
new file mode 100644
index 00000000..1dc64958
--- /dev/null
+++ b/passes/techmap/muxcover.cc
@@ -0,0 +1,632 @@
+/*
+ * 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
+
+#define COST_MUX2 100
+#define COST_MUX4 220
+#define COST_MUX8 460
+#define COST_MUX16 940
+
+struct MuxcoverWorker
+{
+ Module *module;
+ SigMap sigmap;
+
+ struct newmux_t
+ {
+ int cost;
+ vector<SigBit> inputs, selects;
+ newmux_t() : cost(0) {}
+ };
+
+ struct tree_t
+ {
+ SigBit root;
+ dict<SigBit, Cell*> muxes;
+ dict<SigBit, newmux_t> newmuxes;
+ };
+
+ vector<tree_t> tree_list;
+
+ dict<tuple<SigBit, SigBit, SigBit>, tuple<SigBit, pool<SigBit>, bool>> decode_mux_cache;
+ dict<SigBit, tuple<SigBit, SigBit, SigBit>> decode_mux_reverse_cache;
+ int decode_mux_counter;
+
+ bool use_mux4;
+ bool use_mux8;
+ bool use_mux16;
+ bool nodecode;
+
+ MuxcoverWorker(Module *module) : module(module), sigmap(module)
+ {
+ use_mux4 = false;
+ use_mux8 = false;
+ use_mux16 = false;
+ nodecode = false;
+ decode_mux_counter = 0;
+ }
+
+ void treeify()
+ {
+ pool<SigBit> roots;
+ pool<SigBit> used_once;
+ dict<SigBit, Cell*> sig_to_mux;
+
+ for (auto wire : module->wires()) {
+ if (!wire->port_output)
+ continue;
+ for (auto bit : sigmap(wire))
+ roots.insert(bit);
+ }
+
+ for (auto cell : module->cells()) {
+ for (auto conn : cell->connections()) {
+ if (!cell->input(conn.first))
+ continue;
+ for (auto bit : sigmap(conn.second)) {
+ if (used_once.count(bit) || cell->type != "$_MUX_" || conn.first == "\\S")
+ roots.insert(bit);
+ used_once.insert(bit);
+ }
+ }
+ if (cell->type == "$_MUX_")
+ sig_to_mux[sigmap(cell->getPort("\\Y"))] = cell;
+ }
+
+ log(" Treeifying %d MUXes:\n", GetSize(sig_to_mux));
+
+ roots.sort();
+ for (auto rootsig : roots)
+ {
+ tree_t tree;
+ tree.root = rootsig;
+
+ pool<SigBit> wavefront;
+ wavefront.insert(rootsig);
+
+ while (!wavefront.empty()) {
+ SigBit bit = wavefront.pop();
+ if (sig_to_mux.count(bit) && (bit == rootsig || !roots.count(bit))) {
+ Cell *c = sig_to_mux.at(bit);
+ tree.muxes[bit] = c;
+ wavefront.insert(sigmap(c->getPort("\\A")));
+ wavefront.insert(sigmap(c->getPort("\\B")));
+ }
+ }
+
+ if (!tree.muxes.empty()) {
+ log(" Found tree with %d MUXes at root %s.\n", GetSize(tree.muxes), log_signal(tree.root));
+ tree_list.push_back(tree);
+ }
+ }
+
+ log(" Finished treeification: Found %d trees.\n", GetSize(tree_list));
+ }
+
+ bool follow_muxtree(SigBit &ret_bit, tree_t &tree, SigBit bit, const char *path)
+ {
+ if (*path) {
+ if (tree.muxes.count(bit) == 0)
+ return false;
+ char port_name[3] = {'\\', *path, 0};
+ return follow_muxtree(ret_bit, tree, sigmap(tree.muxes.at(bit)->getPort(port_name)), path+1);
+ } else {
+ ret_bit = bit;
+ return true;
+ }
+ }
+
+ int prepare_decode_mux(SigBit &A, SigBit B, SigBit sel, SigBit bit)
+ {
+ if (A == B)
+ return 0;
+
+ tuple<SigBit, SigBit, SigBit> key(A, B, sel);
+ if (decode_mux_cache.count(key) == 0) {
+ auto &entry = decode_mux_cache[key];
+ std::get<0>(entry) = module->addWire(NEW_ID);
+ std::get<2>(entry) = false;
+ decode_mux_reverse_cache[std::get<0>(entry)] = key;
+ }
+
+ auto &entry = decode_mux_cache[key];
+ A = std::get<0>(entry);
+ std::get<1>(entry).insert(bit);
+
+ if (std::get<2>(entry))
+ return 0;
+
+ return COST_MUX2 / GetSize(std::get<1>(entry));
+ }
+
+ void implement_decode_mux(SigBit ctrl_bit)
+ {
+ if (decode_mux_reverse_cache.count(ctrl_bit) == 0)
+ return;
+
+ auto &key = decode_mux_reverse_cache.at(ctrl_bit);
+ auto &entry = decode_mux_cache[key];
+
+ if (std::get<2>(entry))
+ return;
+
+ implement_decode_mux(std::get<0>(key));
+ implement_decode_mux(std::get<1>(key));
+
+ module->addMuxGate(NEW_ID, std::get<0>(key), std::get<1>(key), std::get<2>(key), ctrl_bit);
+ std::get<2>(entry) = true;
+ decode_mux_counter++;
+ }
+
+ int find_best_cover(tree_t &tree, SigBit bit)
+ {
+ if (tree.newmuxes.count(bit)) {
+ return tree.newmuxes.at(bit).cost;
+ }
+
+ SigBit A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P;
+ SigBit S1, S2, S3, S4, S5, S6, S7, S8;
+ SigBit T1, T2, T3, T4;
+ SigBit U1, U2;
+ SigBit V1;
+
+ newmux_t best_mux;
+ bool ok = true;
+
+ // 2-Input MUX
+
+ ok = ok && follow_muxtree(A, tree, bit, "A");
+ ok = ok && follow_muxtree(B, tree, bit, "B");
+
+ ok = ok && follow_muxtree(S1, tree, bit, "S");
+
+ if (ok)
+ {
+ newmux_t mux;
+
+ mux.inputs.push_back(A);
+ mux.inputs.push_back(B);
+ mux.selects.push_back(S1);
+
+ mux.cost += COST_MUX2;
+ mux.cost += find_best_cover(tree, A);
+ mux.cost += find_best_cover(tree, B);
+
+ best_mux = mux;
+ }
+
+ // 4-Input MUX
+
+ if (use_mux4)
+ {
+ ok = ok && follow_muxtree(A, tree, bit, "AA");
+ ok = ok && follow_muxtree(B, tree, bit, "AB");
+ ok = ok && follow_muxtree(C, tree, bit, "BA");
+ ok = ok && follow_muxtree(D, tree, bit, "BB");
+
+ ok = ok && follow_muxtree(S1, tree, bit, "AS");
+ ok = ok && follow_muxtree(S2, tree, bit, "BS");
+
+ if (nodecode)
+ ok = ok && S1 == S2;
+
+ ok = ok && follow_muxtree(T1, tree, bit, "S");
+
+ if (ok)
+ {
+ newmux_t mux;
+
+ mux.inputs.push_back(A);
+ mux.inputs.push_back(B);
+ mux.inputs.push_back(C);
+ mux.inputs.push_back(D);
+
+ mux.cost += prepare_decode_mux(S1, S2, T1, bit);
+
+ mux.selects.push_back(S1);
+ mux.selects.push_back(T1);
+
+ mux.cost += COST_MUX4;
+ mux.cost += find_best_cover(tree, A);
+ mux.cost += find_best_cover(tree, B);
+ mux.cost += find_best_cover(tree, C);
+ mux.cost += find_best_cover(tree, D);
+
+ if (best_mux.cost > mux.cost)
+ best_mux = mux;
+ }
+ }
+
+ // 8-Input MUX
+
+ if (use_mux8)
+ {
+ ok = ok && follow_muxtree(A, tree, bit, "AAA");
+ ok = ok && follow_muxtree(B, tree, bit, "AAB");
+ ok = ok && follow_muxtree(C, tree, bit, "ABA");
+ ok = ok && follow_muxtree(D, tree, bit, "ABB");
+ ok = ok && follow_muxtree(E, tree, bit, "BAA");
+ ok = ok && follow_muxtree(F, tree, bit, "BAB");
+ ok = ok && follow_muxtree(G, tree, bit, "BBA");
+ ok = ok && follow_muxtree(H, tree, bit, "BBB");
+
+ ok = ok && follow_muxtree(S1, tree, bit, "AAS");
+ ok = ok && follow_muxtree(S2, tree, bit, "ABS");
+ ok = ok && follow_muxtree(S3, tree, bit, "BAS");
+ ok = ok && follow_muxtree(S4, tree, bit, "BBS");
+
+ if (nodecode)
+ ok = ok && S1 == S2 && S2 == S3 && S3 == S4;
+
+ ok = ok && follow_muxtree(T1, tree, bit, "AS");
+ ok = ok && follow_muxtree(T2, tree, bit, "BS");
+
+ if (nodecode)
+ ok = ok && T1 == T2;
+
+ ok = ok && follow_muxtree(U1, tree, bit, "S");
+
+ if (ok)
+ {
+ newmux_t mux;
+
+ mux.inputs.push_back(A);
+ mux.inputs.push_back(B);
+ mux.inputs.push_back(C);
+ mux.inputs.push_back(D);
+ mux.inputs.push_back(E);
+ mux.inputs.push_back(F);
+ mux.inputs.push_back(G);
+ mux.inputs.push_back(H);
+
+ mux.cost += prepare_decode_mux(S1, S2, T1, bit);
+ mux.cost += prepare_decode_mux(S3, S4, T2, bit);
+ mux.cost += prepare_decode_mux(S1, S3, U1, bit);
+
+ mux.cost += prepare_decode_mux(T1, T2, U1, bit);
+
+ mux.selects.push_back(S1);
+ mux.selects.push_back(T1);
+ mux.selects.push_back(U1);
+
+ mux.cost += COST_MUX8;
+ mux.cost += find_best_cover(tree, A);
+ mux.cost += find_best_cover(tree, B);
+ mux.cost += find_best_cover(tree, C);
+ mux.cost += find_best_cover(tree, D);
+ mux.cost += find_best_cover(tree, E);
+ mux.cost += find_best_cover(tree, F);
+ mux.cost += find_best_cover(tree, G);
+ mux.cost += find_best_cover(tree, H);
+
+ if (best_mux.cost > mux.cost)
+ best_mux = mux;
+ }
+ }
+
+ // 16-Input MUX
+
+ if (use_mux16)
+ {
+ ok = ok && follow_muxtree(A, tree, bit, "AAAA");
+ ok = ok && follow_muxtree(B, tree, bit, "AAAB");
+ ok = ok && follow_muxtree(C, tree, bit, "AABA");
+ ok = ok && follow_muxtree(D, tree, bit, "AABB");
+ ok = ok && follow_muxtree(E, tree, bit, "ABAA");
+ ok = ok && follow_muxtree(F, tree, bit, "ABAB");
+ ok = ok && follow_muxtree(G, tree, bit, "ABBA");
+ ok = ok && follow_muxtree(H, tree, bit, "ABBB");
+ ok = ok && follow_muxtree(I, tree, bit, "BAAA");
+ ok = ok && follow_muxtree(J, tree, bit, "BAAB");
+ ok = ok && follow_muxtree(K, tree, bit, "BABA");
+ ok = ok && follow_muxtree(L, tree, bit, "BABB");
+ ok = ok && follow_muxtree(M, tree, bit, "BBAA");
+ ok = ok && follow_muxtree(N, tree, bit, "BBAB");
+ ok = ok && follow_muxtree(O, tree, bit, "BBBA");
+ ok = ok && follow_muxtree(P, tree, bit, "BBBB");
+
+ ok = ok && follow_muxtree(S1, tree, bit, "AAAS");
+ ok = ok && follow_muxtree(S2, tree, bit, "AABS");
+ ok = ok && follow_muxtree(S3, tree, bit, "ABAS");
+ ok = ok && follow_muxtree(S4, tree, bit, "ABBS");
+ ok = ok && follow_muxtree(S5, tree, bit, "BAAS");
+ ok = ok && follow_muxtree(S6, tree, bit, "BABS");
+ ok = ok && follow_muxtree(S7, tree, bit, "BBAS");
+ ok = ok && follow_muxtree(S8, tree, bit, "BBBS");
+
+ if (nodecode)
+ ok = ok && S1 == S2 && S2 == S3 && S3 == S4 && S4 == S5 && S5 == S6 && S6 == S7 && S7 == S8;
+
+ ok = ok && follow_muxtree(T1, tree, bit, "AAS");
+ ok = ok && follow_muxtree(T2, tree, bit, "ABS");
+ ok = ok && follow_muxtree(T3, tree, bit, "BAS");
+ ok = ok && follow_muxtree(T4, tree, bit, "BBS");
+
+ if (nodecode)
+ ok = ok && T1 == T2 && T2 == T3 && T3 == T4;
+
+ ok = ok && follow_muxtree(U1, tree, bit, "AS");
+ ok = ok && follow_muxtree(U2, tree, bit, "BS");
+
+ if (nodecode)
+ ok = ok && U1 == U2;
+
+ ok = ok && follow_muxtree(V1, tree, bit, "S");
+
+ if (ok)
+ {
+ newmux_t mux;
+
+ mux.inputs.push_back(A);
+ mux.inputs.push_back(B);
+ mux.inputs.push_back(C);
+ mux.inputs.push_back(D);
+ mux.inputs.push_back(E);
+ mux.inputs.push_back(F);
+ mux.inputs.push_back(G);
+ mux.inputs.push_back(H);
+ mux.inputs.push_back(I);
+ mux.inputs.push_back(J);
+ mux.inputs.push_back(K);
+ mux.inputs.push_back(L);
+ mux.inputs.push_back(M);
+ mux.inputs.push_back(N);
+ mux.inputs.push_back(O);
+ mux.inputs.push_back(P);
+
+ mux.cost += prepare_decode_mux(S1, S2, T1, bit);
+ mux.cost += prepare_decode_mux(S3, S4, T2, bit);
+ mux.cost += prepare_decode_mux(S5, S6, T3, bit);
+ mux.cost += prepare_decode_mux(S7, S8, T4, bit);
+ mux.cost += prepare_decode_mux(S1, S3, U1, bit);
+ mux.cost += prepare_decode_mux(S5, S7, U2, bit);
+ mux.cost += prepare_decode_mux(S1, S5, V1, bit);
+
+ mux.cost += prepare_decode_mux(T1, T2, U1, bit);
+ mux.cost += prepare_decode_mux(T3, T4, U2, bit);
+ mux.cost += prepare_decode_mux(T1, T3, V1, bit);
+
+ mux.cost += prepare_decode_mux(U1, U2, V1, bit);
+
+ mux.selects.push_back(S1);
+ mux.selects.push_back(T1);
+ mux.selects.push_back(U1);
+ mux.selects.push_back(V1);
+
+ mux.cost += COST_MUX16;
+ mux.cost += find_best_cover(tree, A);
+ mux.cost += find_best_cover(tree, B);
+ mux.cost += find_best_cover(tree, C);
+ mux.cost += find_best_cover(tree, D);
+ mux.cost += find_best_cover(tree, E);
+ mux.cost += find_best_cover(tree, F);
+ mux.cost += find_best_cover(tree, G);
+ mux.cost += find_best_cover(tree, H);
+ mux.cost += find_best_cover(tree, I);
+ mux.cost += find_best_cover(tree, J);
+ mux.cost += find_best_cover(tree, K);
+ mux.cost += find_best_cover(tree, L);
+ mux.cost += find_best_cover(tree, M);
+ mux.cost += find_best_cover(tree, N);
+ mux.cost += find_best_cover(tree, O);
+ mux.cost += find_best_cover(tree, P);
+
+ if (best_mux.cost > mux.cost)
+ best_mux = mux;
+ }
+ }
+
+ tree.newmuxes[bit] = best_mux;
+ return best_mux.cost;
+ }
+
+ void implement_best_cover(tree_t &tree, SigBit bit, int count_muxes_by_type[4])
+ {
+ newmux_t mux = tree.newmuxes.at(bit);
+
+ for (auto inbit : mux.inputs)
+ implement_best_cover(tree, inbit, count_muxes_by_type);
+
+ for (auto selbit : mux.selects)
+ implement_decode_mux(selbit);
+
+ if (GetSize(mux.inputs) == 0)
+ return;
+
+ if (GetSize(mux.inputs) == 2) {
+ count_muxes_by_type[0]++;
+ Cell *cell = module->addCell(NEW_ID, "$_MUX_");
+ cell->setPort("\\A", mux.inputs[0]);
+ cell->setPort("\\B", mux.inputs[1]);
+ cell->setPort("\\S", mux.selects[0]);
+ cell->setPort("\\Y", bit);
+ return;
+ }
+
+ if (GetSize(mux.inputs) == 4) {
+ count_muxes_by_type[1]++;
+ Cell *cell = module->addCell(NEW_ID, "$_MUX4_");
+ cell->setPort("\\A", mux.inputs[0]);
+ cell->setPort("\\B", mux.inputs[1]);
+ cell->setPort("\\C", mux.inputs[2]);
+ cell->setPort("\\D", mux.inputs[3]);
+ cell->setPort("\\S", mux.selects[0]);
+ cell->setPort("\\T", mux.selects[1]);
+ cell->setPort("\\Y", bit);
+ return;
+ }
+
+ if (GetSize(mux.inputs) == 8) {
+ count_muxes_by_type[2]++;
+ Cell *cell = module->addCell(NEW_ID, "$_MUX8_");
+ cell->setPort("\\A", mux.inputs[0]);
+ cell->setPort("\\B", mux.inputs[1]);
+ cell->setPort("\\C", mux.inputs[2]);
+ cell->setPort("\\D", mux.inputs[3]);
+ cell->setPort("\\E", mux.inputs[4]);
+ cell->setPort("\\F", mux.inputs[5]);
+ cell->setPort("\\G", mux.inputs[6]);
+ cell->setPort("\\H", mux.inputs[7]);
+ cell->setPort("\\S", mux.selects[0]);
+ cell->setPort("\\T", mux.selects[1]);
+ cell->setPort("\\U", mux.selects[2]);
+ cell->setPort("\\Y", bit);
+ return;
+ }
+
+ if (GetSize(mux.inputs) == 16) {
+ count_muxes_by_type[3]++;
+ Cell *cell = module->addCell(NEW_ID, "$_MUX16_");
+ cell->setPort("\\A", mux.inputs[0]);
+ cell->setPort("\\B", mux.inputs[1]);
+ cell->setPort("\\C", mux.inputs[2]);
+ cell->setPort("\\D", mux.inputs[3]);
+ cell->setPort("\\E", mux.inputs[4]);
+ cell->setPort("\\F", mux.inputs[5]);
+ cell->setPort("\\G", mux.inputs[6]);
+ cell->setPort("\\H", mux.inputs[7]);
+ cell->setPort("\\I", mux.inputs[8]);
+ cell->setPort("\\J", mux.inputs[9]);
+ cell->setPort("\\K", mux.inputs[10]);
+ cell->setPort("\\L", mux.inputs[11]);
+ cell->setPort("\\M", mux.inputs[12]);
+ cell->setPort("\\N", mux.inputs[13]);
+ cell->setPort("\\O", mux.inputs[14]);
+ cell->setPort("\\P", mux.inputs[15]);
+ cell->setPort("\\S", mux.selects[0]);
+ cell->setPort("\\T", mux.selects[1]);
+ cell->setPort("\\U", mux.selects[2]);
+ cell->setPort("\\V", mux.selects[3]);
+ cell->setPort("\\Y", bit);
+ return;
+ }
+
+ log_abort();
+ }
+
+ void treecover(tree_t &tree)
+ {
+ int count_muxes_by_type[4] = {0, 0, 0, 0};
+ find_best_cover(tree, tree.root);
+ implement_best_cover(tree, tree.root, count_muxes_by_type);
+ log(" Replaced tree at %s: %d MUX2, %d MUX4, %d MUX8, %d MUX16\n", log_signal(tree.root),
+ count_muxes_by_type[0], count_muxes_by_type[1], count_muxes_by_type[2], count_muxes_by_type[3]);
+ for (auto &it : tree.muxes)
+ module->remove(it.second);
+ }
+
+ void run()
+ {
+ log("Covering MUX trees in module %s..\n", log_id(module));
+
+ treeify();
+
+ log(" Covering trees:\n");
+
+ // pre-fill cache of decoder muxes
+ if (!nodecode)
+ for (auto &tree : tree_list) {
+ find_best_cover(tree, tree.root);
+ tree.newmuxes.clear();
+ }
+
+ for (auto &tree : tree_list)
+ treecover(tree);
+
+ if (!nodecode)
+ log(" Added a total of %d decoder MUXes.\n", decode_mux_counter);
+ }
+};
+
+struct MuxcoverPass : public Pass {
+ MuxcoverPass() : Pass("muxcover", "cover trees of MUX cells with wider MUXes") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" muxcover [options] [selection]\n");
+ log("\n");
+ log("Cover trees of $_MUX_ cells with $_MUX{4,8,16}_ cells\n");
+ log("\n");
+ log(" -mux4, -mux8, -mux16\n");
+ log(" Use the specified types of MUXes. If none of those options are used,\n");
+ log(" the effect is the same as if all of them where used.\n");
+ log("\n");
+ log(" -nodecode\n");
+ log(" Do not insert decoder logic. This reduces the number of possible\n");
+ log(" substitutions, but guarantees that the resulting circuit is not\n");
+ log(" less efficient than the original circuit.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing MUXCOVER pass (mapping to wider MUXes).\n");
+
+ bool use_mux4 = false;
+ bool use_mux8 = false;
+ bool use_mux16 = false;
+ bool nodecode = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-mux4") {
+ use_mux4 = true;
+ continue;
+ }
+ if (args[argidx] == "-mux8") {
+ use_mux8 = true;
+ continue;
+ }
+ if (args[argidx] == "-mux16") {
+ use_mux16 = true;
+ continue;
+ }
+ if (args[argidx] == "-nodecode") {
+ nodecode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!use_mux4 && !use_mux8 && !use_mux16) {
+ use_mux4 = true;
+ use_mux8 = true;
+ use_mux16 = true;
+ }
+
+ for (auto module : design->selected_modules())
+ {
+ MuxcoverWorker worker(module);
+ worker.use_mux4 = use_mux4;
+ worker.use_mux8 = use_mux8;
+ worker.use_mux16 = use_mux16;
+ worker.nodecode = nodecode;
+ worker.run();
+ }
+ }
+} MuxcoverPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/nlutmap.cc b/passes/techmap/nlutmap.cc
new file mode 100644
index 00000000..6fcdf82b
--- /dev/null
+++ b/passes/techmap/nlutmap.cc
@@ -0,0 +1,187 @@
+/*
+ * 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 NlutmapConfig
+{
+ vector<int> luts;
+ bool assert_mode = false;
+};
+
+struct NlutmapWorker
+{
+ const NlutmapConfig &config;
+ pool<Cell*> mapped_cells;
+ Module *module;
+
+ NlutmapWorker(const NlutmapConfig &config, Module *module) :
+ config(config), module(module)
+ {
+ }
+
+ RTLIL::Selection get_selection()
+ {
+ RTLIL::Selection sel(false);
+ for (auto cell : module->cells())
+ if (!mapped_cells.count(cell))
+ sel.select(module, cell);
+ return sel;
+ }
+
+ void run_abc(int lut_size)
+ {
+ Pass::call_on_selection(module->design, get_selection(), "lut2mux");
+
+ if (lut_size > 0)
+ Pass::call_on_selection(module->design, get_selection(), stringf("abc -lut 1:%d", lut_size));
+ else
+ Pass::call_on_selection(module->design, get_selection(), "abc");
+
+ Pass::call_on_module(module->design, module, "opt_clean");
+ }
+
+ void run()
+ {
+ vector<int> available_luts = config.luts;
+
+ while (GetSize(available_luts) > 1)
+ {
+ int n_luts = available_luts.back();
+ int lut_size = GetSize(available_luts);
+ available_luts.pop_back();
+
+ if (n_luts == 0)
+ continue;
+
+ run_abc(lut_size);
+
+ SigMap sigmap(module);
+ dict<Cell*, int> candidate_ratings;
+ dict<SigBit, int> bit_lut_count;
+
+ for (auto cell : module->cells())
+ {
+ if (cell->type != "$lut" || mapped_cells.count(cell))
+ continue;
+
+ if (GetSize(cell->getPort("\\A")) == lut_size || lut_size == 2)
+ candidate_ratings[cell] = 0;
+
+ for (auto &conn : cell->connections())
+ for (auto bit : sigmap(conn.second))
+ bit_lut_count[bit]++;
+ }
+
+ for (auto &cand : candidate_ratings)
+ {
+ for (auto &conn : cand.first->connections())
+ for (auto bit : sigmap(conn.second))
+ cand.second -= bit_lut_count[bit];
+ }
+
+ vector<pair<int, IdString>> rated_candidates;
+
+ for (auto &cand : candidate_ratings)
+ rated_candidates.push_back(pair<int, IdString>(cand.second, cand.first->name));
+
+ std::sort(rated_candidates.begin(), rated_candidates.end());
+
+ while (n_luts > 0 && !rated_candidates.empty()) {
+ mapped_cells.insert(module->cell(rated_candidates.back().second));
+ rated_candidates.pop_back();
+ n_luts--;
+ }
+
+ if (!available_luts.empty())
+ available_luts.back() += n_luts;
+ }
+
+ if (config.assert_mode) {
+ for (auto cell : module->cells())
+ if (cell->type == "$lut" && !mapped_cells.count(cell))
+ log_error("Insufficient number of LUTs to map all logic cells!\n");
+ }
+
+ run_abc(0);
+ }
+};
+
+struct NlutmapPass : public Pass {
+ NlutmapPass() : Pass("nlutmap", "map to LUTs of different sizes") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" nlutmap [options] [selection]\n");
+ log("\n");
+ log("This pass uses successive calls to 'abc' to map to an architecture. That\n");
+ log("provides a small number of differently sized LUTs.\n");
+ log("\n");
+ log(" -luts N_1,N_2,N_3,...\n");
+ log(" The number of LUTs with 1, 2, 3, ... inputs that are\n");
+ log(" available in the target architecture.\n");
+ log("\n");
+ log(" -assert\n");
+ log(" Create an error if not all logic can be mapped\n");
+ log("\n");
+ log("Excess logic that does not fit into the specified LUTs is mapped back\n");
+ log("to generic logic gates ($_AND_, etc.).\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ NlutmapConfig config;
+
+ log_header(design, "Executing NLUTMAP pass (mapping to constant drivers).\n");
+ log_push();
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-luts" && argidx+1 < args.size()) {
+ vector<string> tokens = split_tokens(args[++argidx], ",");
+ config.luts.clear();
+ for (auto &token : tokens)
+ config.luts.push_back(atoi(token.c_str()));
+ continue;
+ }
+ if (args[argidx] == "-assert") {
+ config.assert_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_whole_modules_warn())
+ {
+ NlutmapWorker worker(config, module);
+ worker.run();
+ }
+
+ log_pop();
+ }
+} NlutmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/pmuxtree.cc b/passes/techmap/pmuxtree.cc
new file mode 100644
index 00000000..c626dbcc
--- /dev/null
+++ b/passes/techmap/pmuxtree.cc
@@ -0,0 +1,112 @@
+/*
+ * 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
+
+static SigSpec or_generator(Module *module, const SigSpec &sig)
+{
+ switch (GetSize(sig))
+ {
+ case 0:
+ return State::S0;
+ case 1:
+ return sig;
+ case 2:
+ return module->Or(NEW_ID, sig[0], sig[1]);
+ default:
+ return module->ReduceOr(NEW_ID, sig);
+ }
+}
+
+static SigSpec recursive_mux_generator(Module *module, const SigSpec &sig_data, const SigSpec &sig_sel, SigSpec &sig_or)
+{
+ if (GetSize(sig_sel) == 1) {
+ sig_or.append(sig_sel);
+ return sig_data;
+ }
+
+ int left_size = GetSize(sig_sel) / 2;
+ int right_size = GetSize(sig_sel) - left_size;
+ int stride = GetSize(sig_data) / GetSize(sig_sel);
+
+ SigSpec left_data = sig_data.extract(0, stride*left_size);
+ SigSpec right_data = sig_data.extract(stride*left_size, stride*right_size);
+
+ SigSpec left_sel = sig_sel.extract(0, left_size);
+ SigSpec right_sel = sig_sel.extract(left_size, right_size);
+
+ SigSpec left_or, left_result, right_result;
+
+ left_result = recursive_mux_generator(module, left_data, left_sel, left_or);
+ right_result = recursive_mux_generator(module, right_data, right_sel, sig_or);
+ left_or = or_generator(module, left_or);
+ sig_or.append(left_or);
+
+ return module->Mux(NEW_ID, right_result, left_result, left_or);
+}
+
+struct PmuxtreePass : public Pass {
+ PmuxtreePass() : Pass("pmuxtree", "transform $pmux cells to trees of $mux cells") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" pmuxtree [options] [selection]\n");
+ log("\n");
+ log("This pass transforms $pmux cells to a trees of $mux cells.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing PMUXTREE pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ for (auto cell : module->selected_cells())
+ {
+ if (cell->type != "$pmux")
+ continue;
+
+ SigSpec sig_data = cell->getPort("\\B");
+ SigSpec sig_sel = cell->getPort("\\S");
+
+ if (!cell->getPort("\\A").is_fully_undef()) {
+ sig_data.append(cell->getPort("\\A"));
+ SigSpec sig_sel_or = module->ReduceOr(NEW_ID, sig_sel);
+ sig_sel.append(module->Not(NEW_ID, sig_sel_or));
+ }
+
+ SigSpec result, result_or;
+ result = recursive_mux_generator(module, sig_data, sig_sel, result_or);
+ module->connect(cell->getPort("\\Y"), result);
+ module->remove(cell);
+ }
+ }
+} PmuxtreePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc
new file mode 100644
index 00000000..6936b499
--- /dev/null
+++ b/passes/techmap/shregmap.cc
@@ -0,0 +1,584 @@
+/*
+ * 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 ShregmapTech
+{
+ virtual ~ShregmapTech() { }
+ virtual bool analyze(vector<int> &taps) = 0;
+ virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0;
+};
+
+struct ShregmapOptions
+{
+ int minlen, maxlen;
+ int keep_before, keep_after;
+ bool zinit, init, params, ffe;
+ dict<IdString, pair<IdString, IdString>> ffcells;
+ ShregmapTech *tech;
+
+ ShregmapOptions()
+ {
+ minlen = 2;
+ maxlen = 0;
+ keep_before = 0;
+ keep_after = 0;
+ zinit = false;
+ init = false;
+ params = false;
+ ffe = false;
+ tech = nullptr;
+ }
+};
+
+struct ShregmapTechGreenpak4 : ShregmapTech
+{
+ bool analyze(vector<int> &taps)
+ {
+ if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) {
+ taps.clear();
+ return true;
+ }
+
+ if (GetSize(taps) > 2)
+ return false;
+
+ if (taps.back() > 16) return false;
+
+ return true;
+ }
+
+ bool fixup(Cell *cell, dict<int, SigBit> &taps)
+ {
+ auto D = cell->getPort("\\D");
+ auto C = cell->getPort("\\C");
+
+ auto newcell = cell->module->addCell(NEW_ID, "\\GP_SHREG");
+ newcell->setPort("\\nRST", State::S1);
+ newcell->setPort("\\CLK", C);
+ newcell->setPort("\\IN", D);
+
+ int i = 0;
+ for (auto tap : taps) {
+ newcell->setPort(i ? "\\OUTB" : "\\OUTA", tap.second);
+ newcell->setParam(i ? "\\OUTB_TAP" : "\\OUTA_TAP", tap.first + 1);
+ i++;
+ }
+
+ cell->setParam("\\OUTA_INVERT", 0);
+ return false;
+ }
+};
+
+struct ShregmapWorker
+{
+ Module *module;
+ SigMap sigmap;
+
+ const ShregmapOptions &opts;
+ int dff_count, shreg_count;
+
+ pool<Cell*> remove_cells;
+ pool<SigBit> remove_init;
+
+ dict<SigBit, bool> sigbit_init;
+ dict<SigBit, Cell*> sigbit_chain_next;
+ dict<SigBit, Cell*> sigbit_chain_prev;
+ pool<SigBit> sigbit_with_non_chain_users;
+ pool<Cell*> chain_start_cells;
+
+ void make_sigbit_chain_next_prev()
+ {
+ for (auto wire : module->wires())
+ {
+ if (wire->port_output || wire->get_bool_attribute("\\keep")) {
+ for (auto bit : sigmap(wire))
+ sigbit_with_non_chain_users.insert(bit);
+ }
+
+ if (wire->attributes.count("\\init")) {
+ SigSpec initsig = sigmap(wire);
+ Const initval = wire->attributes.at("\\init");
+ for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
+ if (initval[i] == State::S0 && !opts.zinit)
+ sigbit_init[initsig[i]] = false;
+ else if (initval[i] == State::S1)
+ sigbit_init[initsig[i]] = true;
+ }
+ }
+
+ for (auto cell : module->cells())
+ {
+ if (opts.ffcells.count(cell->type) && !cell->get_bool_attribute("\\keep"))
+ {
+ IdString d_port = opts.ffcells.at(cell->type).first;
+ IdString q_port = opts.ffcells.at(cell->type).second;
+
+ SigBit d_bit = sigmap(cell->getPort(d_port).as_bit());
+ SigBit q_bit = sigmap(cell->getPort(q_port).as_bit());
+
+ if (opts.init || sigbit_init.count(q_bit) == 0)
+ {
+ if (sigbit_chain_next.count(d_bit)) {
+ sigbit_with_non_chain_users.insert(d_bit);
+ } else
+ sigbit_chain_next[d_bit] = cell;
+
+ sigbit_chain_prev[q_bit] = cell;
+ continue;
+ }
+ }
+
+ for (auto conn : cell->connections())
+ if (cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ sigbit_with_non_chain_users.insert(bit);
+ }
+ }
+
+ void find_chain_start_cells()
+ {
+ for (auto it : sigbit_chain_next)
+ {
+ if (opts.tech == nullptr && sigbit_with_non_chain_users.count(it.first))
+ goto start_cell;
+
+ if (sigbit_chain_prev.count(it.first) != 0)
+ {
+ Cell *c1 = sigbit_chain_prev.at(it.first);
+ Cell *c2 = it.second;
+
+ if (c1->type != c2->type)
+ goto start_cell;
+
+ if (c1->parameters != c2->parameters)
+ goto start_cell;
+
+ IdString d_port = opts.ffcells.at(c1->type).first;
+ IdString q_port = opts.ffcells.at(c1->type).second;
+
+ auto c1_conn = c1->connections();
+ auto c2_conn = c1->connections();
+
+ c1_conn.erase(d_port);
+ c1_conn.erase(q_port);
+
+ c2_conn.erase(d_port);
+ c2_conn.erase(q_port);
+
+ if (c1_conn != c2_conn)
+ goto start_cell;
+
+ continue;
+ }
+
+ start_cell:
+ chain_start_cells.insert(it.second);
+ }
+ }
+
+ vector<Cell*> create_chain(Cell *start_cell)
+ {
+ vector<Cell*> chain;
+
+ Cell *c = start_cell;
+ while (c != nullptr)
+ {
+ chain.push_back(c);
+
+ IdString q_port = opts.ffcells.at(c->type).second;
+ SigBit q_bit = sigmap(c->getPort(q_port).as_bit());
+
+ if (sigbit_chain_next.count(q_bit) == 0)
+ break;
+
+ c = sigbit_chain_next.at(q_bit);
+ if (chain_start_cells.count(c) != 0)
+ break;
+ }
+
+ return chain;
+ }
+
+ void process_chain(vector<Cell*> &chain)
+ {
+ if (GetSize(chain) < opts.keep_before + opts.minlen + opts.keep_after)
+ return;
+
+ int cursor = opts.keep_before;
+ while (cursor < GetSize(chain) - opts.keep_after)
+ {
+ int depth = GetSize(chain) - opts.keep_after - cursor;
+
+ if (opts.maxlen > 0)
+ depth = std::min(opts.maxlen, depth);
+
+ Cell *first_cell = chain[cursor];
+ IdString q_port = opts.ffcells.at(first_cell->type).second;
+ dict<int, SigBit> taps_dict;
+
+ if (opts.tech)
+ {
+ vector<SigBit> qbits;
+ vector<int> taps;
+
+ for (int i = 0; i < depth; i++)
+ {
+ Cell *cell = chain[cursor+i];
+ auto qbit = sigmap(cell->getPort(q_port));
+ qbits.push_back(qbit);
+
+ if (sigbit_with_non_chain_users.count(qbit))
+ taps.push_back(i);
+ }
+
+ while (depth > 0)
+ {
+ if (taps.empty() || taps.back() < depth-1)
+ taps.push_back(depth-1);
+
+ if (opts.tech->analyze(taps))
+ break;
+
+ taps.pop_back();
+ depth--;
+ }
+
+ depth = 0;
+ for (auto tap : taps) {
+ taps_dict[tap] = qbits.at(tap);
+ log_assert(depth < tap+1);
+ depth = tap+1;
+ }
+ }
+
+ if (depth < 2) {
+ cursor++;
+ continue;
+ }
+
+ Cell *last_cell = chain[cursor+depth-1];
+
+ log("Converting %s.%s ... %s.%s to a shift register with depth %d.\n",
+ log_id(module), log_id(first_cell), log_id(module), log_id(last_cell), depth);
+
+ dff_count += depth;
+ shreg_count += 1;
+
+ string shreg_cell_type_str = "$__SHREG";
+ if (opts.params) {
+ shreg_cell_type_str += "_";
+ } else {
+ if (first_cell->type[1] != '_')
+ shreg_cell_type_str += "_";
+ shreg_cell_type_str += first_cell->type.substr(1);
+ }
+
+ if (opts.init) {
+ vector<State> initval;
+ for (int i = depth-1; i >= 0; i--) {
+ SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit());
+ if (sigbit_init.count(bit) == 0)
+ initval.push_back(State::Sx);
+ else if (sigbit_init.at(bit))
+ initval.push_back(State::S1);
+ else
+ initval.push_back(State::S0);
+ remove_init.insert(bit);
+ }
+ first_cell->setParam("\\INIT", initval);
+ }
+
+ if (opts.zinit)
+ for (int i = depth-1; i >= 0; i--) {
+ SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit());
+ remove_init.insert(bit);
+ }
+
+ if (opts.params)
+ {
+ int param_clkpol = -1;
+ int param_enpol = 2;
+
+ if (first_cell->type == "$_DFF_N_") param_clkpol = 0;
+ if (first_cell->type == "$_DFF_P_") param_clkpol = 1;
+
+ if (first_cell->type == "$_DFFE_NN_") param_clkpol = 0, param_enpol = 0;
+ if (first_cell->type == "$_DFFE_NP_") param_clkpol = 0, param_enpol = 1;
+ if (first_cell->type == "$_DFFE_PN_") param_clkpol = 1, param_enpol = 0;
+ if (first_cell->type == "$_DFFE_PP_") param_clkpol = 1, param_enpol = 1;
+
+ log_assert(param_clkpol >= 0);
+ first_cell->setParam("\\CLKPOL", param_clkpol);
+ if (opts.ffe) first_cell->setParam("\\ENPOL", param_enpol);
+ }
+
+ first_cell->type = shreg_cell_type_str;
+ first_cell->setPort(q_port, last_cell->getPort(q_port));
+ first_cell->setParam("\\DEPTH", depth);
+
+ if (opts.tech != nullptr && !opts.tech->fixup(first_cell, taps_dict))
+ remove_cells.insert(first_cell);
+
+ for (int i = 1; i < depth; i++)
+ remove_cells.insert(chain[cursor+i]);
+ cursor += depth;
+ }
+ }
+
+ void cleanup()
+ {
+ for (auto cell : remove_cells)
+ module->remove(cell);
+
+ for (auto wire : module->wires())
+ {
+ if (wire->attributes.count("\\init") == 0)
+ continue;
+
+ SigSpec initsig = sigmap(wire);
+ Const &initval = wire->attributes.at("\\init");
+
+ for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
+ if (remove_init.count(initsig[i]))
+ initval[i] = State::Sx;
+
+ if (SigSpec(initval).is_fully_undef())
+ wire->attributes.erase("\\init");
+ }
+
+ remove_cells.clear();
+ sigbit_chain_next.clear();
+ sigbit_chain_prev.clear();
+ chain_start_cells.clear();
+ }
+
+ ShregmapWorker(Module *module, const ShregmapOptions &opts) :
+ module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0)
+ {
+ make_sigbit_chain_next_prev();
+ find_chain_start_cells();
+
+ for (auto c : chain_start_cells) {
+ vector<Cell*> chain = create_chain(c);
+ process_chain(chain);
+ }
+
+ cleanup();
+ }
+};
+
+struct ShregmapPass : public Pass {
+ ShregmapPass() : Pass("shregmap", "map shift registers") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" shregmap [options] [selection]\n");
+ log("\n");
+ log("This pass converts chains of $_DFF_[NP]_ gates to target specific shift register\n");
+ log("primitives. The generated shift register will be of type $__SHREG_DFF_[NP]_ and\n");
+ log("will use the same interface as the original $_DFF_*_ cells. The cell parameter\n");
+ log("'DEPTH' will contain the depth of the shift register. Use a target-specific\n");
+ log("'techmap' map file to convert those cells to the actual target cells.\n");
+ log("\n");
+ log(" -minlen N\n");
+ log(" minimum length of shift register (default = 2)\n");
+ log(" (this is the length after -keep_before and -keep_after)\n");
+ log("\n");
+ log(" -maxlen N\n");
+ log(" maximum length of shift register (default = no limit)\n");
+ log(" larger chains will be mapped to multiple shift register instances\n");
+ log("\n");
+ log(" -keep_before N\n");
+ log(" number of DFFs to keep before the shift register (default = 0)\n");
+ log("\n");
+ log(" -keep_after N\n");
+ log(" number of DFFs to keep after the shift register (default = 0)\n");
+ log("\n");
+ log(" -clkpol pos|neg|any\n");
+ log(" limit match to only positive or negative edge clocks. (default = any)\n");
+ log("\n");
+ log(" -enpol pos|neg|none|any_or_none|any\n");
+ log(" limit match to FFs with the specified enable polarity. (default = none)\n");
+ log("\n");
+ log(" -match <cell_type>[:<d_port_name>:<q_port_name>]\n");
+ log(" match the specified cells instead of $_DFF_N_ and $_DFF_P_. If\n");
+ log(" ':<d_port_name>:<q_port_name>' is omitted then 'D' and 'Q' is used\n");
+ log(" by default. E.g. the option '-clkpol pos' is just an alias for\n");
+ log(" '-match $_DFF_P_', which is an alias for '-match $_DFF_P_:D:Q'.\n");
+ log("\n");
+ log(" -params\n");
+ log(" instead of encoding the clock and enable polarity in the cell name by\n");
+ log(" deriving from the original cell name, simply name all generated cells\n");
+ log(" $__SHREG_ and use CLKPOL and ENPOL parameters. An ENPOL value of 2 is\n");
+ log(" used to denote cells without enable input. The ENPOL parameter is\n");
+ log(" omitted when '-enpol none' (or no -enpol option) is passed.\n");
+ log("\n");
+ log(" -zinit\n");
+ log(" assume the shift register is automatically zero-initialized, so it\n");
+ log(" becomes legal to merge zero initialized FFs into the shift register.\n");
+ log("\n");
+ log(" -init\n");
+ log(" map initialized registers to the shift reg, add an INIT parameter to\n");
+ log(" generated cells with the initialization value. (first bit to shift out\n");
+ log(" in LSB position)\n");
+ log("\n");
+ log(" -tech greenpak4\n");
+ log(" map to greenpak4 shift registers.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ ShregmapOptions opts;
+ string clkpol, enpol;
+
+ log_header(design, "Executing SHREGMAP pass (map shift registers).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-clkpol" && argidx+1 < args.size()) {
+ clkpol = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-enpol" && argidx+1 < args.size()) {
+ enpol = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-match" && argidx+1 < args.size()) {
+ vector<string> match_args = split_tokens(args[++argidx], ":");
+ if (GetSize(match_args) < 2)
+ match_args.push_back("D");
+ if (GetSize(match_args) < 3)
+ match_args.push_back("Q");
+ IdString id_cell_type(RTLIL::escape_id(match_args[0]));
+ IdString id_d_port_name(RTLIL::escape_id(match_args[1]));
+ IdString id_q_port_name(RTLIL::escape_id(match_args[2]));
+ opts.ffcells[id_cell_type] = make_pair(id_d_port_name, id_q_port_name);
+ continue;
+ }
+ if (args[argidx] == "-minlen" && argidx+1 < args.size()) {
+ opts.minlen = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-maxlen" && argidx+1 < args.size()) {
+ opts.maxlen = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-keep_before" && argidx+1 < args.size()) {
+ opts.keep_before = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-keep_after" && argidx+1 < args.size()) {
+ opts.keep_after = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-tech" && argidx+1 < args.size() && opts.tech == nullptr) {
+ string tech = args[++argidx];
+ if (tech == "greenpak4") {
+ clkpol = "pos";
+ opts.zinit = true;
+ opts.tech = new ShregmapTechGreenpak4;
+ } else {
+ argidx--;
+ break;
+ }
+ continue;
+ }
+ if (args[argidx] == "-zinit") {
+ opts.zinit = true;
+ continue;
+ }
+ if (args[argidx] == "-init") {
+ opts.init = true;
+ continue;
+ }
+ if (args[argidx] == "-params") {
+ opts.params = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (opts.zinit && opts.init)
+ log_cmd_error("Options -zinit and -init are exclusive!\n");
+
+ if (opts.ffcells.empty())
+ {
+ bool clk_pos = clkpol == "" || clkpol == "pos" || clkpol == "any";
+ bool clk_neg = clkpol == "" || clkpol == "neg" || clkpol == "any";
+
+ bool en_none = enpol == "" || enpol == "none" || enpol == "any_or_none";
+ bool en_pos = enpol == "pos" || enpol == "any" || enpol == "any_or_none";
+ bool en_neg = enpol == "neg" || enpol == "any" || enpol == "any_or_none";
+
+ if (clk_pos && en_none)
+ opts.ffcells["$_DFF_P_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+ if (clk_neg && en_none)
+ opts.ffcells["$_DFF_N_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+
+ if (clk_pos && en_pos)
+ opts.ffcells["$_DFFE_PP_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+ if (clk_pos && en_neg)
+ opts.ffcells["$_DFFE_PN_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+
+ if (clk_neg && en_pos)
+ opts.ffcells["$_DFFE_NP_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+ if (clk_neg && en_neg)
+ opts.ffcells["$_DFFE_NN_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+
+ if (en_pos || en_neg)
+ opts.ffe = true;
+ }
+ else
+ {
+ if (!clkpol.empty())
+ log_cmd_error("Options -clkpol and -match are exclusive!\n");
+ if (!enpol.empty())
+ log_cmd_error("Options -enpol and -match are exclusive!\n");
+ if (opts.params)
+ log_cmd_error("Options -params and -match are exclusive!\n");
+ }
+
+ int dff_count = 0;
+ int shreg_count = 0;
+
+ for (auto module : design->selected_modules()) {
+ ShregmapWorker worker(module, opts);
+ dff_count += worker.dff_count;
+ shreg_count += worker.shreg_count;
+ }
+
+ log("Converted %d dff cells into %d shift registers.\n", dff_count, shreg_count);
+
+ if (opts.tech != nullptr) {
+ delete opts.tech;
+ opts.tech = nullptr;
+ }
+ }
+} ShregmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc
new file mode 100644
index 00000000..c6b932bd
--- /dev/null
+++ b/passes/techmap/simplemap.cc
@@ -0,0 +1,618 @@
+/*
+ * 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 "simplemap.h"
+#include "kernel/sigtools.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+USING_YOSYS_NAMESPACE
+YOSYS_NAMESPACE_BEGIN
+
+void simplemap_not(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ RTLIL::SigSpec sig_a = cell->getPort("\\A");
+ RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+
+ sig_a.extend_u0(GetSize(sig_y), cell->parameters.at("\\A_SIGNED").as_bool());
+
+ for (int i = 0; i < GetSize(sig_y); i++) {
+ RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_");
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\A", sig_a[i]);
+ gate->setPort("\\Y", sig_y[i]);
+ }
+}
+
+void simplemap_pos(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ RTLIL::SigSpec sig_a = cell->getPort("\\A");
+ RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+
+ sig_a.extend_u0(GetSize(sig_y), cell->parameters.at("\\A_SIGNED").as_bool());
+
+ module->connect(RTLIL::SigSig(sig_y, sig_a));
+}
+
+void simplemap_bitop(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ RTLIL::SigSpec sig_a = cell->getPort("\\A");
+ RTLIL::SigSpec sig_b = cell->getPort("\\B");
+ RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+
+ sig_a.extend_u0(GetSize(sig_y), cell->parameters.at("\\A_SIGNED").as_bool());
+ sig_b.extend_u0(GetSize(sig_y), cell->parameters.at("\\B_SIGNED").as_bool());
+
+ if (cell->type == "$xnor")
+ {
+ RTLIL::SigSpec sig_t = module->addWire(NEW_ID, GetSize(sig_y));
+
+ for (int i = 0; i < GetSize(sig_y); i++) {
+ RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_");
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\A", sig_t[i]);
+ gate->setPort("\\Y", sig_y[i]);
+ }
+
+ sig_y = sig_t;
+ }
+
+ std::string gate_type;
+ if (cell->type == "$and") gate_type = "$_AND_";
+ if (cell->type == "$or") gate_type = "$_OR_";
+ if (cell->type == "$xor") gate_type = "$_XOR_";
+ if (cell->type == "$xnor") gate_type = "$_XOR_";
+ log_assert(!gate_type.empty());
+
+ for (int i = 0; i < GetSize(sig_y); i++) {
+ RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\A", sig_a[i]);
+ gate->setPort("\\B", sig_b[i]);
+ gate->setPort("\\Y", sig_y[i]);
+ }
+}
+
+void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ RTLIL::SigSpec sig_a = cell->getPort("\\A");
+ RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+
+ if (sig_y.size() == 0)
+ return;
+
+ if (sig_a.size() == 0) {
+ if (cell->type == "$reduce_and") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(1, sig_y.size())));
+ if (cell->type == "$reduce_or") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size())));
+ if (cell->type == "$reduce_xor") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size())));
+ if (cell->type == "$reduce_xnor") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(1, sig_y.size())));
+ if (cell->type == "$reduce_bool") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size())));
+ return;
+ }
+
+ if (sig_y.size() > 1) {
+ module->connect(RTLIL::SigSig(sig_y.extract(1, sig_y.size()-1), RTLIL::SigSpec(0, sig_y.size()-1)));
+ sig_y = sig_y.extract(0, 1);
+ }
+
+ std::string gate_type;
+ if (cell->type == "$reduce_and") gate_type = "$_AND_";
+ if (cell->type == "$reduce_or") gate_type = "$_OR_";
+ if (cell->type == "$reduce_xor") gate_type = "$_XOR_";
+ if (cell->type == "$reduce_xnor") gate_type = "$_XOR_";
+ if (cell->type == "$reduce_bool") gate_type = "$_OR_";
+ log_assert(!gate_type.empty());
+
+ RTLIL::Cell *last_output_cell = NULL;
+
+ while (sig_a.size() > 1)
+ {
+ RTLIL::SigSpec sig_t = module->addWire(NEW_ID, sig_a.size() / 2);
+
+ for (int i = 0; i < sig_a.size(); i += 2)
+ {
+ if (i+1 == sig_a.size()) {
+ sig_t.append(sig_a[i]);
+ continue;
+ }
+
+ RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\A", sig_a[i]);
+ gate->setPort("\\B", sig_a[i+1]);
+ gate->setPort("\\Y", sig_t[i/2]);
+ last_output_cell = gate;
+ }
+
+ sig_a = sig_t;
+ }
+
+ if (cell->type == "$reduce_xnor") {
+ RTLIL::SigSpec sig_t = module->addWire(NEW_ID);
+ RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_");
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\A", sig_a);
+ gate->setPort("\\Y", sig_t);
+ last_output_cell = gate;
+ sig_a = sig_t;
+ }
+
+ if (last_output_cell == NULL) {
+ module->connect(RTLIL::SigSig(sig_y, sig_a));
+ } else {
+ last_output_cell->setPort("\\Y", sig_y);
+ }
+}
+
+static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig, RTLIL::Cell *cell)
+{
+ while (sig.size() > 1)
+ {
+ RTLIL::SigSpec sig_t = module->addWire(NEW_ID, sig.size() / 2);
+
+ for (int i = 0; i < sig.size(); i += 2)
+ {
+ if (i+1 == sig.size()) {
+ sig_t.append(sig[i]);
+ continue;
+ }
+
+ RTLIL::Cell *gate = module->addCell(NEW_ID, "$_OR_");
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\A", sig[i]);
+ gate->setPort("\\B", sig[i+1]);
+ gate->setPort("\\Y", sig_t[i/2]);
+ }
+
+ sig = sig_t;
+ }
+
+ if (sig.size() == 0)
+ sig = RTLIL::SigSpec(0, 1);
+}
+
+void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ RTLIL::SigSpec sig_a = cell->getPort("\\A");
+ logic_reduce(module, sig_a, cell);
+
+ RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+
+ if (sig_y.size() == 0)
+ return;
+
+ if (sig_y.size() > 1) {
+ module->connect(RTLIL::SigSig(sig_y.extract(1, sig_y.size()-1), RTLIL::SigSpec(0, sig_y.size()-1)));
+ sig_y = sig_y.extract(0, 1);
+ }
+
+ RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_");
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\A", sig_a);
+ gate->setPort("\\Y", sig_y);
+}
+
+void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ RTLIL::SigSpec sig_a = cell->getPort("\\A");
+ logic_reduce(module, sig_a, cell);
+
+ RTLIL::SigSpec sig_b = cell->getPort("\\B");
+ logic_reduce(module, sig_b, cell);
+
+ RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+
+ if (sig_y.size() == 0)
+ return;
+
+ if (sig_y.size() > 1) {
+ module->connect(RTLIL::SigSig(sig_y.extract(1, sig_y.size()-1), RTLIL::SigSpec(0, sig_y.size()-1)));
+ sig_y = sig_y.extract(0, 1);
+ }
+
+ std::string gate_type;
+ if (cell->type == "$logic_and") gate_type = "$_AND_";
+ if (cell->type == "$logic_or") gate_type = "$_OR_";
+ log_assert(!gate_type.empty());
+
+ RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\A", sig_a);
+ gate->setPort("\\B", sig_b);
+ gate->setPort("\\Y", sig_y);
+}
+
+void simplemap_eqne(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ RTLIL::SigSpec sig_a = cell->getPort("\\A");
+ RTLIL::SigSpec sig_b = cell->getPort("\\B");
+ RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+ bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool();
+ bool is_ne = cell->type == "$ne" || cell->type == "$nex";
+
+ RTLIL::SigSpec xor_out = module->addWire(NEW_ID, max(GetSize(sig_a), GetSize(sig_b)));
+ RTLIL::Cell *xor_cell = module->addXor(NEW_ID, sig_a, sig_b, xor_out, is_signed);
+ xor_cell->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ simplemap_bitop(module, xor_cell);
+ module->remove(xor_cell);
+
+ RTLIL::SigSpec reduce_out = is_ne ? sig_y : module->addWire(NEW_ID);
+ RTLIL::Cell *reduce_cell = module->addReduceOr(NEW_ID, xor_out, reduce_out);
+ reduce_cell->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ simplemap_reduce(module, reduce_cell);
+ module->remove(reduce_cell);
+
+ if (!is_ne) {
+ RTLIL::Cell *not_cell = module->addLogicNot(NEW_ID, reduce_out, sig_y);
+ not_cell->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ simplemap_lognot(module, not_cell);
+ module->remove(not_cell);
+ }
+}
+
+void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ RTLIL::SigSpec sig_a = cell->getPort("\\A");
+ RTLIL::SigSpec sig_b = cell->getPort("\\B");
+ RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+
+ for (int i = 0; i < GetSize(sig_y); i++) {
+ RTLIL::Cell *gate = module->addCell(NEW_ID, "$_MUX_");
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\A", sig_a[i]);
+ gate->setPort("\\B", sig_b[i]);
+ gate->setPort("\\S", cell->getPort("\\S"));
+ gate->setPort("\\Y", sig_y[i]);
+ }
+}
+
+void simplemap_tribuf(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ RTLIL::SigSpec sig_a = cell->getPort("\\A");
+ RTLIL::SigSpec sig_e = cell->getPort("\\EN");
+ RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+
+ for (int i = 0; i < GetSize(sig_y); i++) {
+ RTLIL::Cell *gate = module->addCell(NEW_ID, "$_TBUF_");
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\A", sig_a[i]);
+ gate->setPort("\\E", sig_e);
+ gate->setPort("\\Y", sig_y[i]);
+ }
+}
+
+void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ SigSpec lut_ctrl = cell->getPort("\\A");
+ SigSpec lut_data = cell->getParam("\\LUT");
+ lut_data.extend_u0(1 << cell->getParam("\\WIDTH").as_int());
+
+ for (int idx = 0; GetSize(lut_data) > 1; idx++) {
+ SigSpec sig_s = lut_ctrl[idx];
+ SigSpec new_lut_data = module->addWire(NEW_ID, GetSize(lut_data)/2);
+ for (int i = 0; i < GetSize(lut_data); i += 2) {
+ RTLIL::Cell *gate = module->addCell(NEW_ID, "$_MUX_");
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\A", lut_data[i]);
+ gate->setPort("\\B", lut_data[i+1]);
+ gate->setPort("\\S", lut_ctrl[idx]);
+ gate->setPort("\\Y", new_lut_data[i/2]);
+ }
+ lut_data = new_lut_data;
+ }
+
+ module->connect(cell->getPort("\\Y"), lut_data);
+}
+
+void simplemap_sop(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ SigSpec ctrl = cell->getPort("\\A");
+ SigSpec table = cell->getParam("\\TABLE");
+
+ int width = cell->getParam("\\WIDTH").as_int();
+ int depth = cell->getParam("\\DEPTH").as_int();
+ table.extend_u0(2 * width * depth);
+
+ SigSpec products;
+
+ for (int i = 0; i < depth; i++) {
+ SigSpec in, pat;
+ for (int j = 0; j < width; j++) {
+ if (table[2*i*width + 2*j + 0] == State::S1) {
+ in.append(ctrl[j]);
+ pat.append(State::S0);
+ }
+ if (table[2*i*width + 2*j + 1] == State::S1) {
+ in.append(ctrl[j]);
+ pat.append(State::S1);
+ }
+ }
+
+ products.append(GetSize(in) > 0 ? module->Eq(NEW_ID, in, pat) : State::S1);
+ }
+
+ module->connect(cell->getPort("\\Y"), module->ReduceOr(NEW_ID, products));
+}
+
+void simplemap_slice(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ int offset = cell->parameters.at("\\OFFSET").as_int();
+ RTLIL::SigSpec sig_a = cell->getPort("\\A");
+ RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+ module->connect(RTLIL::SigSig(sig_y, sig_a.extract(offset, sig_y.size())));
+}
+
+void simplemap_concat(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ RTLIL::SigSpec sig_ab = cell->getPort("\\A");
+ sig_ab.append(cell->getPort("\\B"));
+ RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+ module->connect(RTLIL::SigSig(sig_y, sig_ab));
+}
+
+void simplemap_sr(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ int width = cell->parameters.at("\\WIDTH").as_int();
+ char set_pol = cell->parameters.at("\\SET_POLARITY").as_bool() ? 'P' : 'N';
+ char clr_pol = cell->parameters.at("\\CLR_POLARITY").as_bool() ? 'P' : 'N';
+
+ RTLIL::SigSpec sig_s = cell->getPort("\\SET");
+ RTLIL::SigSpec sig_r = cell->getPort("\\CLR");
+ RTLIL::SigSpec sig_q = cell->getPort("\\Q");
+
+ std::string gate_type = stringf("$_SR_%c%c_", set_pol, clr_pol);
+
+ for (int i = 0; i < width; i++) {
+ RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\S", sig_s[i]);
+ gate->setPort("\\R", sig_r[i]);
+ gate->setPort("\\Q", sig_q[i]);
+ }
+}
+
+void simplemap_ff(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ int width = cell->parameters.at("\\WIDTH").as_int();
+
+ RTLIL::SigSpec sig_d = cell->getPort("\\D");
+ RTLIL::SigSpec sig_q = cell->getPort("\\Q");
+
+ std::string gate_type = "$_FF_";
+
+ for (int i = 0; i < width; i++) {
+ RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\D", sig_d[i]);
+ gate->setPort("\\Q", sig_q[i]);
+ }
+}
+
+void simplemap_dff(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ int width = cell->parameters.at("\\WIDTH").as_int();
+ char clk_pol = cell->parameters.at("\\CLK_POLARITY").as_bool() ? 'P' : 'N';
+
+ RTLIL::SigSpec sig_clk = cell->getPort("\\CLK");
+ RTLIL::SigSpec sig_d = cell->getPort("\\D");
+ RTLIL::SigSpec sig_q = cell->getPort("\\Q");
+
+ std::string gate_type = stringf("$_DFF_%c_", clk_pol);
+
+ for (int i = 0; i < width; i++) {
+ RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\C", sig_clk);
+ gate->setPort("\\D", sig_d[i]);
+ gate->setPort("\\Q", sig_q[i]);
+ }
+}
+
+void simplemap_dffe(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ int width = cell->parameters.at("\\WIDTH").as_int();
+ char clk_pol = cell->parameters.at("\\CLK_POLARITY").as_bool() ? 'P' : 'N';
+ char en_pol = cell->parameters.at("\\EN_POLARITY").as_bool() ? 'P' : 'N';
+
+ RTLIL::SigSpec sig_clk = cell->getPort("\\CLK");
+ RTLIL::SigSpec sig_en = cell->getPort("\\EN");
+ RTLIL::SigSpec sig_d = cell->getPort("\\D");
+ RTLIL::SigSpec sig_q = cell->getPort("\\Q");
+
+ std::string gate_type = stringf("$_DFFE_%c%c_", clk_pol, en_pol);
+
+ for (int i = 0; i < width; i++) {
+ RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\C", sig_clk);
+ gate->setPort("\\E", sig_en);
+ gate->setPort("\\D", sig_d[i]);
+ gate->setPort("\\Q", sig_q[i]);
+ }
+}
+
+void simplemap_dffsr(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ int width = cell->parameters.at("\\WIDTH").as_int();
+ char clk_pol = cell->parameters.at("\\CLK_POLARITY").as_bool() ? 'P' : 'N';
+ char set_pol = cell->parameters.at("\\SET_POLARITY").as_bool() ? 'P' : 'N';
+ char clr_pol = cell->parameters.at("\\CLR_POLARITY").as_bool() ? 'P' : 'N';
+
+ RTLIL::SigSpec sig_clk = cell->getPort("\\CLK");
+ RTLIL::SigSpec sig_s = cell->getPort("\\SET");
+ RTLIL::SigSpec sig_r = cell->getPort("\\CLR");
+ RTLIL::SigSpec sig_d = cell->getPort("\\D");
+ RTLIL::SigSpec sig_q = cell->getPort("\\Q");
+
+ std::string gate_type = stringf("$_DFFSR_%c%c%c_", clk_pol, set_pol, clr_pol);
+
+ for (int i = 0; i < width; i++) {
+ RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\C", sig_clk);
+ gate->setPort("\\S", sig_s[i]);
+ gate->setPort("\\R", sig_r[i]);
+ gate->setPort("\\D", sig_d[i]);
+ gate->setPort("\\Q", sig_q[i]);
+ }
+}
+
+void simplemap_adff(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ int width = cell->parameters.at("\\WIDTH").as_int();
+ char clk_pol = cell->parameters.at("\\CLK_POLARITY").as_bool() ? 'P' : 'N';
+ char rst_pol = cell->parameters.at("\\ARST_POLARITY").as_bool() ? 'P' : 'N';
+
+ std::vector<RTLIL::State> rst_val = cell->parameters.at("\\ARST_VALUE").bits;
+ while (int(rst_val.size()) < width)
+ rst_val.push_back(RTLIL::State::S0);
+
+ RTLIL::SigSpec sig_clk = cell->getPort("\\CLK");
+ RTLIL::SigSpec sig_rst = cell->getPort("\\ARST");
+ RTLIL::SigSpec sig_d = cell->getPort("\\D");
+ RTLIL::SigSpec sig_q = cell->getPort("\\Q");
+
+ std::string gate_type_0 = stringf("$_DFF_%c%c0_", clk_pol, rst_pol);
+ std::string gate_type_1 = stringf("$_DFF_%c%c1_", clk_pol, rst_pol);
+
+ for (int i = 0; i < width; i++) {
+ RTLIL::Cell *gate = module->addCell(NEW_ID, rst_val.at(i) == RTLIL::State::S1 ? gate_type_1 : gate_type_0);
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\C", sig_clk);
+ gate->setPort("\\R", sig_rst);
+ gate->setPort("\\D", sig_d[i]);
+ gate->setPort("\\Q", sig_q[i]);
+ }
+}
+
+void simplemap_dlatch(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ int width = cell->parameters.at("\\WIDTH").as_int();
+ char en_pol = cell->parameters.at("\\EN_POLARITY").as_bool() ? 'P' : 'N';
+
+ RTLIL::SigSpec sig_en = cell->getPort("\\EN");
+ RTLIL::SigSpec sig_d = cell->getPort("\\D");
+ RTLIL::SigSpec sig_q = cell->getPort("\\Q");
+
+ std::string gate_type = stringf("$_DLATCH_%c_", en_pol);
+
+ for (int i = 0; i < width; i++) {
+ RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\E", sig_en);
+ gate->setPort("\\D", sig_d[i]);
+ gate->setPort("\\Q", sig_q[i]);
+ }
+}
+
+void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers)
+{
+ mappers["$not"] = simplemap_not;
+ mappers["$pos"] = simplemap_pos;
+ mappers["$and"] = simplemap_bitop;
+ mappers["$or"] = simplemap_bitop;
+ mappers["$xor"] = simplemap_bitop;
+ mappers["$xnor"] = simplemap_bitop;
+ mappers["$reduce_and"] = simplemap_reduce;
+ mappers["$reduce_or"] = simplemap_reduce;
+ mappers["$reduce_xor"] = simplemap_reduce;
+ mappers["$reduce_xnor"] = simplemap_reduce;
+ mappers["$reduce_bool"] = simplemap_reduce;
+ mappers["$logic_not"] = simplemap_lognot;
+ mappers["$logic_and"] = simplemap_logbin;
+ mappers["$logic_or"] = simplemap_logbin;
+ mappers["$eq"] = simplemap_eqne;
+ mappers["$eqx"] = simplemap_eqne;
+ mappers["$ne"] = simplemap_eqne;
+ mappers["$nex"] = simplemap_eqne;
+ mappers["$mux"] = simplemap_mux;
+ mappers["$tribuf"] = simplemap_tribuf;
+ mappers["$lut"] = simplemap_lut;
+ mappers["$sop"] = simplemap_sop;
+ mappers["$slice"] = simplemap_slice;
+ mappers["$concat"] = simplemap_concat;
+ mappers["$sr"] = simplemap_sr;
+ mappers["$ff"] = simplemap_ff;
+ mappers["$dff"] = simplemap_dff;
+ mappers["$dffe"] = simplemap_dffe;
+ mappers["$dffsr"] = simplemap_dffsr;
+ mappers["$adff"] = simplemap_adff;
+ mappers["$dlatch"] = simplemap_dlatch;
+}
+
+void simplemap(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ static std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> mappers;
+ static bool initialized_mappers = false;
+
+ if (!initialized_mappers) {
+ simplemap_get_mappers(mappers);
+ initialized_mappers = true;
+ }
+
+ mappers.at(cell->type)(module, cell);
+}
+
+YOSYS_NAMESPACE_END
+PRIVATE_NAMESPACE_BEGIN
+
+struct SimplemapPass : public Pass {
+ SimplemapPass() : Pass("simplemap", "mapping simple coarse-grain cells") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" simplemap [selection]\n");
+ log("\n");
+ log("This pass maps a small selection of simple coarse-grain cells to yosys gate\n");
+ log("primitives. The following internal cell types are mapped by this pass:\n");
+ log("\n");
+ log(" $not, $pos, $and, $or, $xor, $xnor\n");
+ log(" $reduce_and, $reduce_or, $reduce_xor, $reduce_xnor, $reduce_bool\n");
+ log(" $logic_not, $logic_and, $logic_or, $mux, $tribuf\n");
+ log(" $sr, $ff, $dff, $dffsr, $adff, $dlatch\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing SIMPLEMAP pass (map simple cells to gate primitives).\n");
+ extra_args(args, 1, design);
+
+ std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> mappers;
+ simplemap_get_mappers(mappers);
+
+ for (auto mod : design->modules()) {
+ if (!design->selected(mod))
+ continue;
+ std::vector<RTLIL::Cell*> cells = mod->cells();
+ for (auto cell : cells) {
+ if (mappers.count(cell->type) == 0)
+ continue;
+ if (!design->selected(mod, cell))
+ continue;
+ log("Mapping %s.%s (%s).\n", log_id(mod), log_id(cell), log_id(cell->type));
+ mappers.at(cell->type)(mod, cell);
+ mod->remove(cell);
+ }
+ }
+ }
+} SimplemapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/simplemap.h b/passes/techmap/simplemap.h
new file mode 100644
index 00000000..c2d73ea7
--- /dev/null
+++ b/passes/techmap/simplemap.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef SIMPLEMAP_H
+#define SIMPLEMAP_H
+
+#include "kernel/yosys.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+extern void simplemap_not(RTLIL::Module *module, RTLIL::Cell *cell);
+extern void simplemap_pos(RTLIL::Module *module, RTLIL::Cell *cell);
+extern void simplemap_bitop(RTLIL::Module *module, RTLIL::Cell *cell);
+extern void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell);
+extern void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell);
+extern void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell);
+extern void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell);
+extern void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell);
+extern void simplemap_slice(RTLIL::Module *module, RTLIL::Cell *cell);
+extern void simplemap_concat(RTLIL::Module *module, RTLIL::Cell *cell);
+extern void simplemap_sr(RTLIL::Module *module, RTLIL::Cell *cell);
+extern void simplemap_dff(RTLIL::Module *module, RTLIL::Cell *cell);
+extern void simplemap_dffe(RTLIL::Module *module, RTLIL::Cell *cell);
+extern void simplemap_dffsr(RTLIL::Module *module, RTLIL::Cell *cell);
+extern void simplemap_adff(RTLIL::Module *module, RTLIL::Cell *cell);
+extern void simplemap_dlatch(RTLIL::Module *module, RTLIL::Cell *cell);
+extern void simplemap(RTLIL::Module *module, RTLIL::Cell *cell);
+
+extern void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers);
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc
new file mode 100644
index 00000000..6784f48c
--- /dev/null
+++ b/passes/techmap/techmap.cc
@@ -0,0 +1,1210 @@
+/*
+ * 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/utils.h"
+#include "kernel/sigtools.h"
+#include "libs/sha1/sha1.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "simplemap.h"
+#include "passes/techmap/techmap.inc"
+
+YOSYS_NAMESPACE_BEGIN
+
+// see maccmap.cc
+extern void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap = false);
+
+YOSYS_NAMESPACE_END
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void apply_prefix(std::string prefix, std::string &id)
+{
+ if (id[0] == '\\')
+ id = prefix + "." + id.substr(1);
+ else
+ id = "$techmap" + prefix + "." + id;
+}
+
+void apply_prefix(std::string prefix, RTLIL::SigSpec &sig, RTLIL::Module *module)
+{
+ vector<SigChunk> chunks = sig;
+ for (auto &chunk : chunks)
+ if (chunk.wire != NULL) {
+ std::string wire_name = chunk.wire->name.str();
+ apply_prefix(prefix, wire_name);
+ log_assert(module->wires_.count(wire_name) > 0);
+ chunk.wire = module->wires_[wire_name];
+ }
+ sig = chunks;
+}
+
+struct TechmapWorker
+{
+ std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> simplemap_mappers;
+ std::map<std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>>, RTLIL::Module*> techmap_cache;
+ std::map<RTLIL::Module*, bool> techmap_do_cache;
+ std::set<RTLIL::Module*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Module>> module_queue;
+ dict<Module*, SigMap> sigmaps;
+
+ pool<IdString> flatten_do_list;
+ pool<IdString> flatten_done_list;
+ pool<Cell*> flatten_keep_list;
+
+ struct TechmapWireData {
+ RTLIL::Wire *wire;
+ RTLIL::SigSpec value;
+ };
+
+ typedef std::map<std::string, std::vector<TechmapWireData>> TechmapWires;
+
+ bool extern_mode;
+ bool assert_mode;
+ bool flatten_mode;
+ bool recursive_mode;
+ bool autoproc_mode;
+
+ TechmapWorker()
+ {
+ extern_mode = false;
+ assert_mode = false;
+ flatten_mode = false;
+ recursive_mode = false;
+ autoproc_mode = false;
+ }
+
+ std::string constmap_tpl_name(SigMap &sigmap, RTLIL::Module *tpl, RTLIL::Cell *cell, bool verbose)
+ {
+ std::string constmap_info;
+ std::map<RTLIL::SigBit, std::pair<RTLIL::IdString, int>> connbits_map;
+
+ for (auto conn : cell->connections())
+ for (int i = 0; i < GetSize(conn.second); i++) {
+ RTLIL::SigBit bit = sigmap(conn.second[i]);
+ if (bit.wire == nullptr) {
+ if (verbose)
+ log(" Constant input on bit %d of port %s: %s\n", i, log_id(conn.first), log_signal(bit));
+ constmap_info += stringf("|%s %d %d", log_id(conn.first), i, bit.data);
+ } else if (connbits_map.count(bit)) {
+ if (verbose)
+ log(" Bit %d of port %s and bit %d of port %s are connected.\n", i, log_id(conn.first),
+ connbits_map.at(bit).second, log_id(connbits_map.at(bit).first));
+ constmap_info += stringf("|%s %d %s %d", log_id(conn.first), i,
+ log_id(connbits_map.at(bit).first), connbits_map.at(bit).second);
+ } else {
+ connbits_map[bit] = std::pair<RTLIL::IdString, int>(conn.first, i);
+ constmap_info += stringf("|%s %d", log_id(conn.first), i);
+ }
+ }
+
+ return stringf("$paramod$constmap:%s%s", sha1(constmap_info).c_str(), tpl->name.c_str());
+ }
+
+ TechmapWires techmap_find_special_wires(RTLIL::Module *module)
+ {
+ TechmapWires result;
+
+ if (module == NULL)
+ return result;
+
+ for (auto &it : module->wires_) {
+ const char *p = it.first.c_str();
+ if (*p == '$')
+ continue;
+
+ const char *q = strrchr(p+1, '.');
+ p = q ? q+1 : p+1;
+
+ if (!strncmp(p, "_TECHMAP_", 9)) {
+ TechmapWireData record;
+ record.wire = it.second;
+ record.value = it.second;
+ result[p].push_back(record);
+ it.second->attributes["\\keep"] = RTLIL::Const(1);
+ it.second->attributes["\\_techmap_special_"] = RTLIL::Const(1);
+ }
+ }
+
+ if (!result.empty()) {
+ SigMap sigmap(module);
+ for (auto &it1 : result)
+ for (auto &it2 : it1.second)
+ sigmap.apply(it2.value);
+ }
+
+ return result;
+ }
+
+ void techmap_module_worker(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl)
+ {
+ if (tpl->processes.size() != 0) {
+ log("Technology map yielded processes:");
+ for (auto &it : tpl->processes)
+ log(" %s",RTLIL::id2cstr(it.first));
+ log("\n");
+ if (autoproc_mode) {
+ Pass::call_on_module(tpl->design, tpl, "proc");
+ log_assert(GetSize(tpl->processes) == 0);
+ } else
+ log_error("Technology map yielded processes -> this is not supported (use -autoproc to run 'proc' automatically).\n");
+ }
+
+ std::string orig_cell_name;
+ pool<string> extra_src_attrs;
+
+ if (!flatten_mode)
+ {
+ for (auto &it : tpl->cells_)
+ if (it.first == "\\_TECHMAP_REPLACE_") {
+ orig_cell_name = cell->name.str();
+ module->rename(cell, stringf("$techmap%d", autoidx++) + cell->name.str());
+ break;
+ }
+
+ extra_src_attrs = cell->get_strpool_attribute("\\src");
+ }
+
+ dict<IdString, IdString> memory_renames;
+
+ for (auto &it : tpl->memories) {
+ std::string m_name = it.first.str();
+ apply_prefix(cell->name.str(), m_name);
+ RTLIL::Memory *m = new RTLIL::Memory;
+ m->name = m_name;
+ m->width = it.second->width;
+ m->start_offset = it.second->start_offset;
+ m->size = it.second->size;
+ m->attributes = it.second->attributes;
+ if (m->attributes.count("\\src"))
+ m->add_strpool_attribute("\\src", extra_src_attrs);
+ module->memories[m->name] = m;
+ memory_renames[it.first] = m->name;
+ design->select(module, m);
+ }
+
+ std::map<RTLIL::IdString, RTLIL::IdString> positional_ports;
+
+ for (auto &it : tpl->wires_) {
+ if (it.second->port_id > 0)
+ positional_ports[stringf("$%d", it.second->port_id)] = it.first;
+ std::string w_name = it.second->name.str();
+ apply_prefix(cell->name.str(), w_name);
+ RTLIL::Wire *w = module->addWire(w_name, it.second);
+ w->port_input = false;
+ w->port_output = false;
+ w->port_id = 0;
+ if (it.second->get_bool_attribute("\\_techmap_special_"))
+ w->attributes.clear();
+ if (w->attributes.count("\\src"))
+ w->add_strpool_attribute("\\src", extra_src_attrs);
+ design->select(module, w);
+ }
+
+ SigMap tpl_sigmap(tpl);
+ pool<SigBit> tpl_written_bits;
+
+ for (auto &it1 : tpl->cells_)
+ for (auto &it2 : it1.second->connections_)
+ if (it1.second->output(it2.first))
+ for (auto bit : tpl_sigmap(it2.second))
+ tpl_written_bits.insert(bit);
+ for (auto &it1 : tpl->connections_)
+ for (auto bit : tpl_sigmap(it1.first))
+ tpl_written_bits.insert(bit);
+
+ SigMap port_signal_map;
+ SigSig port_signal_assign;
+
+ for (auto &it : cell->connections())
+ {
+ RTLIL::IdString portname = it.first;
+ if (positional_ports.count(portname) > 0)
+ portname = positional_ports.at(portname);
+ if (tpl->wires_.count(portname) == 0 || tpl->wires_.at(portname)->port_id == 0) {
+ if (portname.substr(0, 1) == "$")
+ log_error("Can't map port `%s' of cell `%s' to template `%s'!\n", portname.c_str(), cell->name.c_str(), tpl->name.c_str());
+ continue;
+ }
+
+ RTLIL::Wire *w = tpl->wires_.at(portname);
+ RTLIL::SigSig c, extra_connect;
+
+ if (w->port_output && !w->port_input) {
+ c.first = it.second;
+ c.second = RTLIL::SigSpec(w);
+ apply_prefix(cell->name.str(), c.second, module);
+ extra_connect.first = c.second;
+ extra_connect.second = c.first;
+ } else if (!w->port_output && w->port_input) {
+ c.first = RTLIL::SigSpec(w);
+ c.second = it.second;
+ apply_prefix(cell->name.str(), c.first, module);
+ extra_connect.first = c.first;
+ extra_connect.second = c.second;
+ } else {
+ SigSpec sig_tpl = w, sig_tpl_pf = w, sig_mod = it.second;
+ apply_prefix(cell->name.str(), sig_tpl_pf, module);
+ for (int i = 0; i < GetSize(sig_tpl) && i < GetSize(sig_mod); i++) {
+ if (tpl_written_bits.count(tpl_sigmap(sig_tpl[i]))) {
+ c.first.append(sig_mod[i]);
+ c.second.append(sig_tpl_pf[i]);
+ } else {
+ c.first.append(sig_tpl_pf[i]);
+ c.second.append(sig_mod[i]);
+ }
+ }
+ extra_connect.first = sig_tpl_pf;
+ extra_connect.second = sig_mod;
+ }
+
+ if (c.second.size() > c.first.size())
+ c.second.remove(c.first.size(), c.second.size() - c.first.size());
+
+ if (c.second.size() < c.first.size())
+ c.second.append(RTLIL::SigSpec(RTLIL::State::S0, c.first.size() - c.second.size()));
+
+ log_assert(c.first.size() == c.second.size());
+
+ if (flatten_mode)
+ {
+ // more conservative approach:
+ // connect internal and external wires
+
+ if (sigmaps.count(module) == 0)
+ sigmaps[module].set(module);
+
+ if (sigmaps.at(module)(c.first).has_const())
+ log_error("Mismatch in directionality for cell port %s.%s.%s: %s <= %s\n",
+ log_id(module), log_id(cell), log_id(it.first), log_signal(c.first), log_signal(c.second));
+
+ module->connect(c);
+ }
+ else
+ {
+ // approach that yields nicer outputs:
+ // replace internal wires that are connected to external wires
+
+ if (w->port_output)
+ port_signal_map.add(c.second, c.first);
+ else
+ port_signal_map.add(c.first, c.second);
+
+ for (auto &attr : w->attributes) {
+ if (attr.first == "\\src")
+ continue;
+ module->connect(extra_connect);
+ break;
+ }
+ }
+ }
+
+ for (auto &it : tpl->cells_)
+ {
+ std::string c_name = it.second->name.str();
+
+ if (!flatten_mode && c_name == "\\_TECHMAP_REPLACE_")
+ c_name = orig_cell_name;
+ else
+ apply_prefix(cell->name.str(), c_name);
+
+ RTLIL::Cell *c = module->addCell(c_name, it.second);
+ design->select(module, c);
+
+ if (!flatten_mode && c->type.substr(0, 2) == "\\$")
+ c->type = c->type.substr(1);
+
+ for (auto &it2 : c->connections_) {
+ apply_prefix(cell->name.str(), it2.second, module);
+ port_signal_map.apply(it2.second);
+ }
+
+ if (c->type == "$memrd" || c->type == "$memwr" || c->type == "$meminit") {
+ IdString memid = c->getParam("\\MEMID").decode_string();
+ log_assert(memory_renames.count(memid) != 0);
+ c->setParam("\\MEMID", Const(memory_renames[memid].str()));
+ }
+
+ if (c->type == "$mem") {
+ string memid = c->getParam("\\MEMID").decode_string();
+ apply_prefix(cell->name.str(), memid);
+ c->setParam("\\MEMID", Const(memid));
+ }
+
+ if (c->attributes.count("\\src"))
+ c->add_strpool_attribute("\\src", extra_src_attrs);
+ }
+
+ for (auto &it : tpl->connections()) {
+ RTLIL::SigSig c = it;
+ apply_prefix(cell->name.str(), c.first, module);
+ apply_prefix(cell->name.str(), c.second, module);
+ port_signal_map.apply(c.first);
+ port_signal_map.apply(c.second);
+ module->connect(c);
+ }
+
+ module->remove(cell);
+ }
+
+ bool techmap_module(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Design *map, std::set<RTLIL::Cell*> &handled_cells,
+ const std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> &celltypeMap, bool in_recursion)
+ {
+ std::string mapmsg_prefix = in_recursion ? "Recursively mapping" : "Mapping";
+
+ if (!design->selected(module))
+ return false;
+
+ bool log_continue = false;
+ bool did_something = false;
+
+ SigMap sigmap(module);
+
+ TopoSort<RTLIL::Cell*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Cell>> cells;
+ std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_inbit;
+ std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> outbit_to_cell;
+
+ for (auto cell : module->cells())
+ {
+ if (!design->selected(module, cell) || handled_cells.count(cell) > 0)
+ continue;
+
+ std::string cell_type = cell->type.str();
+ if (in_recursion && cell_type.substr(0, 2) == "\\$")
+ cell_type = cell_type.substr(1);
+
+ if (celltypeMap.count(cell_type) == 0) {
+ if (assert_mode && cell_type.back() != '_')
+ log_error("(ASSERT MODE) No matching template cell for type %s found.\n", log_id(cell_type));
+ continue;
+ }
+
+ if (flatten_mode) {
+ bool keepit = cell->get_bool_attribute("\\keep_hierarchy");
+ for (auto &tpl_name : celltypeMap.at(cell_type))
+ if (map->modules_[tpl_name]->get_bool_attribute("\\keep_hierarchy"))
+ keepit = true;
+ if (keepit) {
+ if (!flatten_keep_list[cell]) {
+ log("Keeping %s.%s (found keep_hierarchy property).\n", log_id(module), log_id(cell));
+ flatten_keep_list.insert(cell);
+ }
+ if (!flatten_done_list[cell->type])
+ flatten_do_list.insert(cell->type);
+ continue;
+ }
+ }
+
+ for (auto &conn : cell->connections())
+ {
+ RTLIL::SigSpec sig = sigmap(conn.second);
+ sig.remove_const();
+
+ if (GetSize(sig) == 0)
+ continue;
+
+ for (auto &tpl_name : celltypeMap.at(cell_type)) {
+ RTLIL::Module *tpl = map->modules_[tpl_name];
+ RTLIL::Wire *port = tpl->wire(conn.first);
+ if (port && port->port_input)
+ cell_to_inbit[cell].insert(sig.begin(), sig.end());
+ if (port && port->port_output)
+ for (auto &bit : sig)
+ outbit_to_cell[bit].insert(cell);
+ }
+ }
+
+ cells.node(cell);
+ }
+
+ for (auto &it_right : cell_to_inbit)
+ for (auto &it_sigbit : it_right.second)
+ for (auto &it_left : outbit_to_cell[it_sigbit])
+ cells.edge(it_left, it_right.first);
+
+ cells.sort();
+
+ for (auto cell : cells.sorted)
+ {
+ log_assert(handled_cells.count(cell) == 0);
+ log_assert(cell == module->cell(cell->name));
+ bool mapped_cell = false;
+
+ std::string cell_type = cell->type.str();
+ if (in_recursion && cell_type.substr(0, 2) == "\\$")
+ cell_type = cell_type.substr(1);
+
+ for (auto &tpl_name : celltypeMap.at(cell_type))
+ {
+ RTLIL::IdString derived_name = tpl_name;
+ RTLIL::Module *tpl = map->modules_[tpl_name];
+ std::map<RTLIL::IdString, RTLIL::Const> parameters(cell->parameters.begin(), cell->parameters.end());
+
+ if (tpl->get_bool_attribute("\\blackbox"))
+ continue;
+
+ if (!flatten_mode)
+ {
+ std::string extmapper_name;
+
+ if (tpl->get_bool_attribute("\\techmap_simplemap"))
+ extmapper_name = "simplemap";
+
+ if (tpl->get_bool_attribute("\\techmap_maccmap"))
+ extmapper_name = "maccmap";
+
+ if (tpl->attributes.count("\\techmap_wrap"))
+ extmapper_name = "wrap";
+
+ if (!extmapper_name.empty())
+ {
+ cell->type = cell_type;
+
+ if ((extern_mode && !in_recursion) || extmapper_name == "wrap")
+ {
+ std::string m_name = stringf("$extern:%s:%s", extmapper_name.c_str(), log_id(cell->type));
+
+ for (auto &c : cell->parameters)
+ m_name += stringf(":%s=%s", log_id(c.first), log_signal(c.second));
+
+ if (extmapper_name == "wrap")
+ m_name += ":" + sha1(tpl->attributes.at("\\techmap_wrap").decode_string());
+
+ RTLIL::Design *extmapper_design = extern_mode && !in_recursion ? design : tpl->design;
+ RTLIL::Module *extmapper_module = extmapper_design->module(m_name);
+
+ if (extmapper_module == nullptr)
+ {
+ extmapper_module = extmapper_design->addModule(m_name);
+ RTLIL::Cell *extmapper_cell = extmapper_module->addCell(cell->type, cell);
+
+ int port_counter = 1;
+ for (auto &c : extmapper_cell->connections_) {
+ RTLIL::Wire *w = extmapper_module->addWire(c.first, GetSize(c.second));
+ if (w->name == "\\Y" || w->name == "\\Q")
+ w->port_output = true;
+ else
+ w->port_input = true;
+ w->port_id = port_counter++;
+ c.second = w;
+ }
+
+ extmapper_module->fixup_ports();
+ extmapper_module->check();
+
+ if (extmapper_name == "simplemap") {
+ log("Creating %s with simplemap.\n", log_id(extmapper_module));
+ if (simplemap_mappers.count(extmapper_cell->type) == 0)
+ log_error("No simplemap mapper for cell type %s found!\n", log_id(extmapper_cell->type));
+ simplemap_mappers.at(extmapper_cell->type)(extmapper_module, extmapper_cell);
+ extmapper_module->remove(extmapper_cell);
+ }
+
+ if (extmapper_name == "maccmap") {
+ log("Creating %s with maccmap.\n", log_id(extmapper_module));
+ if (extmapper_cell->type != "$macc")
+ log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(extmapper_cell->type));
+ maccmap(extmapper_module, extmapper_cell);
+ extmapper_module->remove(extmapper_cell);
+ }
+
+ if (extmapper_name == "wrap") {
+ std::string cmd_string = tpl->attributes.at("\\techmap_wrap").decode_string();
+ log("Running \"%s\" on wrapper %s.\n", cmd_string.c_str(), log_id(extmapper_module));
+ Pass::call_on_module(extmapper_design, extmapper_module, cmd_string);
+ log_continue = true;
+ }
+ }
+
+ cell->type = extmapper_module->name;
+ cell->parameters.clear();
+
+ if (!extern_mode || in_recursion) {
+ tpl = extmapper_module;
+ goto use_wrapper_tpl;
+ }
+
+ log("%s %s.%s (%s) to %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), log_id(extmapper_module));
+ }
+ else
+ {
+ log("%s %s.%s (%s) with %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), extmapper_name.c_str());
+
+ if (extmapper_name == "simplemap") {
+ if (simplemap_mappers.count(cell->type) == 0)
+ log_error("No simplemap mapper for cell type %s found!\n", RTLIL::id2cstr(cell->type));
+ simplemap_mappers.at(cell->type)(module, cell);
+ }
+
+ if (extmapper_name == "maccmap") {
+ if (cell->type != "$macc")
+ log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(cell->type));
+ maccmap(module, cell);
+ }
+
+ module->remove(cell);
+ cell = NULL;
+ }
+
+ did_something = true;
+ mapped_cell = true;
+ break;
+ }
+
+ for (auto conn : cell->connections()) {
+ if (conn.first.substr(0, 1) == "$")
+ continue;
+ if (tpl->wires_.count(conn.first) > 0 && tpl->wires_.at(conn.first)->port_id > 0)
+ continue;
+ if (!conn.second.is_fully_const() || parameters.count(conn.first) > 0 || tpl->avail_parameters.count(conn.first) == 0)
+ goto next_tpl;
+ parameters[conn.first] = conn.second.as_const();
+ }
+
+ if (0) {
+ next_tpl:
+ continue;
+ }
+
+ if (tpl->avail_parameters.count("\\_TECHMAP_CELLTYPE_") != 0)
+ parameters["\\_TECHMAP_CELLTYPE_"] = RTLIL::unescape_id(cell->type);
+
+ for (auto conn : cell->connections()) {
+ if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONSTMSK_%s_", RTLIL::id2cstr(conn.first))) != 0) {
+ std::vector<RTLIL::SigBit> v = sigmap(conn.second).to_sigbit_vector();
+ for (auto &bit : v)
+ bit = RTLIL::SigBit(bit.wire == NULL ? RTLIL::State::S1 : RTLIL::State::S0);
+ parameters[stringf("\\_TECHMAP_CONSTMSK_%s_", RTLIL::id2cstr(conn.first))] = RTLIL::SigSpec(v).as_const();
+ }
+ if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONSTVAL_%s_", RTLIL::id2cstr(conn.first))) != 0) {
+ std::vector<RTLIL::SigBit> v = sigmap(conn.second).to_sigbit_vector();
+ for (auto &bit : v)
+ if (bit.wire != NULL)
+ bit = RTLIL::SigBit(RTLIL::State::Sx);
+ parameters[stringf("\\_TECHMAP_CONSTVAL_%s_", RTLIL::id2cstr(conn.first))] = RTLIL::SigSpec(v).as_const();
+ }
+ }
+
+ int unique_bit_id_counter = 0;
+ std::map<RTLIL::SigBit, int> unique_bit_id;
+ unique_bit_id[RTLIL::State::S0] = unique_bit_id_counter++;
+ unique_bit_id[RTLIL::State::S1] = unique_bit_id_counter++;
+ unique_bit_id[RTLIL::State::Sx] = unique_bit_id_counter++;
+ unique_bit_id[RTLIL::State::Sz] = unique_bit_id_counter++;
+
+ for (auto conn : cell->connections())
+ if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONNMAP_%s_", RTLIL::id2cstr(conn.first))) != 0) {
+ for (auto &bit : sigmap(conn.second).to_sigbit_vector())
+ if (unique_bit_id.count(bit) == 0)
+ unique_bit_id[bit] = unique_bit_id_counter++;
+ }
+
+ int bits = 0;
+ for (int i = 0; i < 32; i++)
+ if (((unique_bit_id_counter-1) & (1 << i)) != 0)
+ bits = i;
+ if (tpl->avail_parameters.count("\\_TECHMAP_BITS_CONNMAP_"))
+ parameters["\\_TECHMAP_BITS_CONNMAP_"] = bits;
+
+ for (auto conn : cell->connections())
+ if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONNMAP_%s_", RTLIL::id2cstr(conn.first))) != 0) {
+ RTLIL::Const value;
+ for (auto &bit : sigmap(conn.second).to_sigbit_vector()) {
+ RTLIL::Const chunk(unique_bit_id.at(bit), bits);
+ value.bits.insert(value.bits.end(), chunk.bits.begin(), chunk.bits.end());
+ }
+ parameters[stringf("\\_TECHMAP_CONNMAP_%s_", RTLIL::id2cstr(conn.first))] = value;
+ }
+ }
+
+ if (0) {
+ use_wrapper_tpl:;
+ // do not register techmap_wrap modules with techmap_cache
+ } else {
+ std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>> key(tpl_name, parameters);
+ if (techmap_cache.count(key) > 0) {
+ tpl = techmap_cache[key];
+ } else {
+ if (parameters.size() != 0) {
+ derived_name = tpl->derive(map, dict<RTLIL::IdString, RTLIL::Const>(parameters.begin(), parameters.end()));
+ tpl = map->module(derived_name);
+ log_continue = true;
+ }
+ techmap_cache[key] = tpl;
+ }
+ }
+
+ if (flatten_mode) {
+ techmap_do_cache[tpl] = true;
+ } else {
+ RTLIL::Module *constmapped_tpl = map->module(constmap_tpl_name(sigmap, tpl, cell, false));
+ if (constmapped_tpl != nullptr)
+ tpl = constmapped_tpl;
+ }
+
+ if (techmap_do_cache.count(tpl) == 0)
+ {
+ bool keep_running = true;
+ techmap_do_cache[tpl] = true;
+
+ std::set<std::string> techmap_wire_names;
+
+ while (keep_running)
+ {
+ TechmapWires twd = techmap_find_special_wires(tpl);
+ keep_running = false;
+
+ for (auto &it : twd)
+ techmap_wire_names.insert(it.first);
+
+ for (auto &it : twd["_TECHMAP_FAIL_"]) {
+ RTLIL::SigSpec value = it.value;
+ if (value.is_fully_const() && value.as_bool()) {
+ log("Not using module `%s' from techmap as it contains a %s marker wire with non-zero value %s.\n",
+ derived_name.c_str(), RTLIL::id2cstr(it.wire->name), log_signal(value));
+ techmap_do_cache[tpl] = false;
+ }
+ }
+
+ if (!techmap_do_cache[tpl])
+ break;
+
+ for (auto &it : twd)
+ {
+ if (it.first.substr(0, 12) != "_TECHMAP_DO_" || it.second.empty())
+ continue;
+
+ auto &data = it.second.front();
+
+ if (!data.value.is_fully_const())
+ log_error("Techmap yielded config wire %s with non-const value %s.\n", RTLIL::id2cstr(data.wire->name), log_signal(data.value));
+
+ techmap_wire_names.erase(it.first);
+
+ const char *p = data.wire->name.c_str();
+ const char *q = strrchr(p+1, '.');
+ q = q ? q : p+1;
+
+ std::string cmd_string = data.value.as_const().decode_string();
+
+ restart_eval_cmd_string:
+ if (cmd_string.rfind("CONSTMAP; ", 0) == 0)
+ {
+ cmd_string = cmd_string.substr(strlen("CONSTMAP; "));
+
+ log("Analyzing pattern of constant bits for this cell:\n");
+ RTLIL::IdString new_tpl_name = constmap_tpl_name(sigmap, tpl, cell, true);
+ log("Creating constmapped module `%s'.\n", log_id(new_tpl_name));
+ log_assert(map->module(new_tpl_name) == nullptr);
+
+ RTLIL::Module *new_tpl = map->addModule(new_tpl_name);
+ tpl->cloneInto(new_tpl);
+
+ techmap_do_cache.erase(tpl);
+ techmap_do_cache[new_tpl] = true;
+ tpl = new_tpl;
+
+ std::map<RTLIL::SigBit, RTLIL::SigBit> port_new2old_map;
+ std::map<RTLIL::SigBit, RTLIL::SigBit> port_connmap;
+ std::map<RTLIL::SigBit, RTLIL::SigBit> cellbits_to_tplbits;
+
+ for (auto wire : tpl->wires().to_vector())
+ {
+ if (!wire->port_input || wire->port_output)
+ continue;
+
+ RTLIL::IdString port_name = wire->name;
+ tpl->rename(wire, NEW_ID);
+
+ RTLIL::Wire *new_wire = tpl->addWire(port_name, wire);
+ wire->port_input = false;
+ wire->port_id = 0;
+
+ for (int i = 0; i < wire->width; i++) {
+ port_new2old_map[RTLIL::SigBit(new_wire, i)] = RTLIL::SigBit(wire, i);
+ port_connmap[RTLIL::SigBit(wire, i)] = RTLIL::SigBit(new_wire, i);
+ }
+ }
+
+ for (auto conn : cell->connections())
+ for (int i = 0; i < GetSize(conn.second); i++)
+ {
+ RTLIL::SigBit bit = sigmap(conn.second[i]);
+ RTLIL::SigBit tplbit(tpl->wire(conn.first), i);
+
+ if (bit.wire == nullptr)
+ {
+ RTLIL::SigBit oldbit = port_new2old_map.at(tplbit);
+ port_connmap.at(oldbit) = bit;
+ }
+ else if (cellbits_to_tplbits.count(bit))
+ {
+ RTLIL::SigBit oldbit = port_new2old_map.at(tplbit);
+ port_connmap.at(oldbit) = cellbits_to_tplbits[bit];
+ }
+ else
+ cellbits_to_tplbits[bit] = tplbit;
+ }
+
+ RTLIL::SigSig port_conn;
+ for (auto &it : port_connmap) {
+ port_conn.first.append_bit(it.first);
+ port_conn.second.append_bit(it.second);
+ }
+ tpl->connect(port_conn);
+
+ tpl->check();
+ goto restart_eval_cmd_string;
+ }
+
+ if (cmd_string.rfind("RECURSION; ", 0) == 0)
+ {
+ cmd_string = cmd_string.substr(strlen("RECURSION; "));
+ while (techmap_module(map, tpl, map, handled_cells, celltypeMap, true)) { }
+ goto restart_eval_cmd_string;
+ }
+
+ Pass::call_on_module(map, tpl, cmd_string);
+
+ log_assert(!strncmp(q, "_TECHMAP_DO_", 12));
+ std::string new_name = data.wire->name.substr(0, q-p) + "_TECHMAP_DONE_" + data.wire->name.substr(q-p+12);
+ while (tpl->wires_.count(new_name))
+ new_name += "_";
+ tpl->rename(data.wire->name, new_name);
+
+ keep_running = true;
+ break;
+ }
+ }
+
+ TechmapWires twd = techmap_find_special_wires(tpl);
+ for (auto &it : twd) {
+ if (it.first != "_TECHMAP_FAIL_" && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_")
+ log_error("Techmap yielded unknown config wire %s.\n", it.first.c_str());
+ if (techmap_do_cache[tpl])
+ for (auto &it2 : it.second)
+ if (!it2.value.is_fully_const())
+ log_error("Techmap yielded config wire %s with non-const value %s.\n", RTLIL::id2cstr(it2.wire->name), log_signal(it2.value));
+ techmap_wire_names.erase(it.first);
+ }
+
+ for (auto &it : techmap_wire_names)
+ log_error("Techmap special wire %s disappeared. This is considered a fatal error.\n", RTLIL::id2cstr(it));
+
+ if (recursive_mode) {
+ if (log_continue) {
+ log_header(design, "Continuing TECHMAP pass.\n");
+ log_continue = false;
+ }
+ while (techmap_module(map, tpl, map, handled_cells, celltypeMap, true)) { }
+ }
+ }
+
+ if (techmap_do_cache.at(tpl) == false)
+ continue;
+
+ if (log_continue) {
+ log_header(design, "Continuing TECHMAP pass.\n");
+ log_continue = false;
+ }
+
+ if (extern_mode && !in_recursion)
+ {
+ std::string m_name = stringf("$extern:%s", log_id(tpl));
+
+ if (!design->module(m_name))
+ {
+ RTLIL::Module *m = design->addModule(m_name);
+ tpl->cloneInto(m);
+
+ for (auto cell : m->cells()) {
+ if (cell->type.substr(0, 2) == "\\$")
+ cell->type = cell->type.substr(1);
+ }
+
+ module_queue.insert(m);
+ }
+
+ log("%s %s.%s to imported %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(m_name));
+ cell->type = m_name;
+ cell->parameters.clear();
+ }
+ else
+ {
+ log("%s %s.%s using %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(tpl));
+ techmap_module_worker(design, module, cell, tpl);
+ cell = NULL;
+ }
+ did_something = true;
+ mapped_cell = true;
+ break;
+ }
+
+ if (assert_mode && !mapped_cell)
+ log_error("(ASSERT MODE) Failed to map cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type));
+
+ handled_cells.insert(cell);
+ }
+
+ if (log_continue) {
+ log_header(design, "Continuing TECHMAP pass.\n");
+ log_continue = false;
+ }
+
+ return did_something;
+ }
+};
+
+struct TechmapPass : public Pass {
+ TechmapPass() : Pass("techmap", "generic technology mapper") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" techmap [-map filename] [selection]\n");
+ log("\n");
+ log("This pass implements a very simple technology mapper that replaces cells in\n");
+ log("the design with implementations given in form of a Verilog or ilang source\n");
+ log("file.\n");
+ log("\n");
+ log(" -map filename\n");
+ log(" the library of cell implementations to be used.\n");
+ log(" without this parameter a builtin library is used that\n");
+ log(" transforms the internal RTL cells to the internal gate\n");
+ log(" library.\n");
+ log("\n");
+ log(" -map %%<design-name>\n");
+ log(" like -map above, but with an in-memory design instead of a file.\n");
+ log("\n");
+ log(" -extern\n");
+ log(" load the cell implementations as separate modules into the design\n");
+ log(" instead of inlining them.\n");
+ log("\n");
+ log(" -max_iter <number>\n");
+ log(" only run the specified number of iterations.\n");
+ log("\n");
+ log(" -recursive\n");
+ log(" instead of the iterative breadth-first algorithm use a recursive\n");
+ log(" depth-first algorithm. both methods should yield equivalent results,\n");
+ log(" but may differ in performance.\n");
+ log("\n");
+ log(" -autoproc\n");
+ log(" Automatically call \"proc\" on implementations that contain processes.\n");
+ log("\n");
+ log(" -assert\n");
+ log(" this option will cause techmap to exit with an error if it can't map\n");
+ log(" a selected cell. only cell types that end on an underscore are accepted\n");
+ log(" as final cell types by this mode.\n");
+ log("\n");
+ log(" -D <define>, -I <incdir>\n");
+ log(" this options are passed as-is to the Verilog frontend for loading the\n");
+ log(" map file. Note that the Verilog frontend is also called with the\n");
+ log(" '-ignore_redef' option set.\n");
+ log("\n");
+ log("When a module in the map file has the 'techmap_celltype' attribute set, it will\n");
+ log("match cells with a type that match the text value of this attribute. Otherwise\n");
+ log("the module name will be used to match the cell.\n");
+ log("\n");
+ log("When a module in the map file has the 'techmap_simplemap' attribute set, techmap\n");
+ log("will use 'simplemap' (see 'help simplemap') to map cells matching the module.\n");
+ log("\n");
+ log("When a module in the map file has the 'techmap_maccmap' attribute set, techmap\n");
+ log("will use 'maccmap' (see 'help maccmap') to map cells matching the module.\n");
+ log("\n");
+ log("When a module in the map file has the 'techmap_wrap' attribute set, techmap\n");
+ log("will create a wrapper for the cell and then run the command string that the\n");
+ log("attribute is set to on the wrapper module.\n");
+ log("\n");
+ log("All wires in the modules from the map file matching the pattern _TECHMAP_*\n");
+ log("or *._TECHMAP_* are special wires that are used to pass instructions from\n");
+ log("the mapping module to the techmap command. At the moment the following special\n");
+ log("wires are supported:\n");
+ log("\n");
+ log(" _TECHMAP_FAIL_\n");
+ log(" When this wire is set to a non-zero constant value, techmap will not\n");
+ log(" use this module and instead try the next module with a matching\n");
+ log(" 'techmap_celltype' attribute.\n");
+ log("\n");
+ log(" When such a wire exists but does not have a constant value after all\n");
+ log(" _TECHMAP_DO_* commands have been executed, an error is generated.\n");
+ log("\n");
+ log(" _TECHMAP_DO_*\n");
+ log(" This wires are evaluated in alphabetical order. The constant text value\n");
+ log(" of this wire is a yosys command (or sequence of commands) that is run\n");
+ log(" by techmap on the module. A common use case is to run 'proc' on modules\n");
+ log(" that are written using always-statements.\n");
+ log("\n");
+ log(" When such a wire has a non-constant value at the time it is to be\n");
+ log(" evaluated, an error is produced. That means it is possible for such a\n");
+ log(" wire to start out as non-constant and evaluate to a constant value\n");
+ log(" during processing of other _TECHMAP_DO_* commands.\n");
+ log("\n");
+ log(" A _TECHMAP_DO_* command may start with the special token 'CONSTMAP; '.\n");
+ log(" in this case techmap will create a copy for each distinct configuration\n");
+ log(" of constant inputs and shorted inputs at this point and import the\n");
+ log(" constant and connected bits into the map module. All further commands\n");
+ log(" are executed in this copy. This is a very convenient way of creating\n");
+ log(" optimized specializations of techmap modules without using the special\n");
+ log(" parameters described below.\n");
+ log("\n");
+ log(" A _TECHMAP_DO_* command may start with the special token 'RECURSION; '.\n");
+ log(" then techmap will recursively replace the cells in the module with their\n");
+ log(" implementation. This is not affected by the -max_iter option.\n");
+ log("\n");
+ log(" It is possible to combine both prefixes to 'RECURSION; CONSTMAP; '.\n");
+ log("\n");
+ log("In addition to this special wires, techmap also supports special parameters in\n");
+ log("modules in the map file:\n");
+ log("\n");
+ log(" _TECHMAP_CELLTYPE_\n");
+ log(" When a parameter with this name exists, it will be set to the type name\n");
+ log(" of the cell that matches the module.\n");
+ log("\n");
+ log(" _TECHMAP_CONSTMSK_<port-name>_\n");
+ log(" _TECHMAP_CONSTVAL_<port-name>_\n");
+ log(" When this pair of parameters is available in a module for a port, then\n");
+ log(" former has a 1-bit for each constant input bit and the latter has the\n");
+ log(" value for this bit. The unused bits of the latter are set to undef (x).\n");
+ log("\n");
+ log(" _TECHMAP_BITS_CONNMAP_\n");
+ log(" _TECHMAP_CONNMAP_<port-name>_\n");
+ log(" For an N-bit port, the _TECHMAP_CONNMAP_<port-name>_ parameter, if it\n");
+ log(" exists, will be set to an N*_TECHMAP_BITS_CONNMAP_ bit vector containing\n");
+ log(" N words (of _TECHMAP_BITS_CONNMAP_ bits each) that assign each single\n");
+ log(" bit driver a unique id. The values 0-3 are reserved for 0, 1, x, and z.\n");
+ log(" This can be used to detect shorted inputs.\n");
+ log("\n");
+ log("When a module in the map file has a parameter where the according cell in the\n");
+ log("design has a port, the module from the map file is only used if the port in\n");
+ log("the design is connected to a constant value. The parameter is then set to the\n");
+ log("constant value.\n");
+ log("\n");
+ log("A cell with the name _TECHMAP_REPLACE_ in the map file will inherit the name\n");
+ log("of the cell that is being replaced.\n");
+ log("\n");
+ log("See 'help extract' for a pass that does the opposite thing.\n");
+ log("\n");
+ log("See 'help flatten' for a pass that does flatten the design (which is\n");
+ log("essentially techmap but using the design itself as map library).\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing TECHMAP pass (map to technology primitives).\n");
+ log_push();
+
+ TechmapWorker worker;
+ simplemap_get_mappers(worker.simplemap_mappers);
+
+ std::vector<std::string> map_files;
+ std::string verilog_frontend = "verilog -ignore_redef";
+ int max_iter = -1;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-map" && argidx+1 < args.size()) {
+ map_files.push_back(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-max_iter" && argidx+1 < args.size()) {
+ max_iter = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-D" && argidx+1 < args.size()) {
+ verilog_frontend += " -D " + args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-I" && argidx+1 < args.size()) {
+ verilog_frontend += " -I " + args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-assert") {
+ worker.assert_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-extern") {
+ worker.extern_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-recursive") {
+ worker.recursive_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-autoproc") {
+ worker.autoproc_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ RTLIL::Design *map = new RTLIL::Design;
+ if (map_files.empty()) {
+ std::istringstream f(stdcells_code);
+ Frontend::frontend_call(map, &f, "<techmap.v>", verilog_frontend);
+ } else
+ for (auto &fn : map_files)
+ if (fn.substr(0, 1) == "%") {
+ if (!saved_designs.count(fn.substr(1))) {
+ delete map;
+ log_cmd_error("Can't saved design `%s'.\n", fn.c_str()+1);
+ }
+ for (auto mod : saved_designs.at(fn.substr(1))->modules())
+ if (!map->has(mod->name))
+ map->add(mod->clone());
+ } else {
+ std::ifstream f;
+ rewrite_filename(fn);
+ f.open(fn.c_str());
+ if (f.fail())
+ log_cmd_error("Can't open map file `%s'\n", fn.c_str());
+ Frontend::frontend_call(map, &f, fn, (fn.size() > 3 && fn.substr(fn.size()-3) == ".il") ? "ilang" : verilog_frontend);
+ }
+
+ std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap;
+ for (auto &it : map->modules_) {
+ if (it.second->attributes.count("\\techmap_celltype") && !it.second->attributes.at("\\techmap_celltype").bits.empty()) {
+ char *p = strdup(it.second->attributes.at("\\techmap_celltype").decode_string().c_str());
+ for (char *q = strtok(p, " \t\r\n"); q; q = strtok(NULL, " \t\r\n"))
+ celltypeMap[RTLIL::escape_id(q)].insert(it.first);
+ free(p);
+ } else {
+ string module_name = it.first.str();
+ if (module_name.substr(0, 2) == "\\$")
+ module_name = module_name.substr(1);
+ celltypeMap[module_name].insert(it.first);
+ }
+ }
+
+ for (auto module : design->modules())
+ worker.module_queue.insert(module);
+
+ while (!worker.module_queue.empty())
+ {
+ RTLIL::Module *module = *worker.module_queue.begin();
+ worker.module_queue.erase(module);
+
+ bool did_something = true;
+ std::set<RTLIL::Cell*> handled_cells;
+ while (did_something) {
+ did_something = false;
+ if (worker.techmap_module(design, module, map, handled_cells, celltypeMap, false))
+ did_something = true;
+ if (did_something)
+ module->check();
+ if (max_iter > 0 && --max_iter == 0)
+ break;
+ }
+ }
+
+ log("No more expansions possible.\n");
+ delete map;
+
+ log_pop();
+ }
+} TechmapPass;
+
+struct FlattenPass : public Pass {
+ FlattenPass() : Pass("flatten", "flatten design") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" flatten [selection]\n");
+ log("\n");
+ log("This pass flattens the design by replacing cells by their implementation. This\n");
+ log("pass is very similar to the 'techmap' pass. The only difference is that this\n");
+ log("pass is using the current design as mapping library.\n");
+ log("\n");
+ log("Cells and/or modules with the 'keep_hierarchy' attribute set will not be\n");
+ log("flattened by this command.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing FLATTEN pass (flatten design).\n");
+ log_push();
+
+ extra_args(args, 1, design);
+
+ TechmapWorker worker;
+ worker.flatten_mode = true;
+
+ std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap;
+ for (auto module : design->modules())
+ celltypeMap[module->name].insert(module->name);
+
+ RTLIL::Module *top_mod = NULL;
+ if (design->full_selection())
+ for (auto mod : design->modules())
+ if (mod->get_bool_attribute("\\top"))
+ top_mod = mod;
+
+ std::set<RTLIL::Cell*> handled_cells;
+ if (top_mod != NULL) {
+ worker.flatten_do_list.insert(top_mod->name);
+ while (!worker.flatten_do_list.empty()) {
+ auto mod = design->module(*worker.flatten_do_list.begin());
+ while (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false)) { }
+ worker.flatten_done_list.insert(mod->name);
+ worker.flatten_do_list.erase(mod->name);
+ }
+ } else {
+ for (auto mod : vector<Module*>(design->modules())) {
+ while (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false)) { }
+ }
+ }
+
+ log("No more expansions possible.\n");
+
+ if (top_mod != NULL)
+ {
+ pool<RTLIL::IdString> used_modules, new_used_modules;
+ new_used_modules.insert(top_mod->name);
+ while (!new_used_modules.empty()) {
+ pool<RTLIL::IdString> queue;
+ queue.swap(new_used_modules);
+ for (auto modname : queue)
+ used_modules.insert(modname);
+ for (auto modname : queue)
+ for (auto cell : design->module(modname)->cells())
+ if (design->module(cell->type) && !used_modules[cell->type])
+ new_used_modules.insert(cell->type);
+ }
+
+ dict<RTLIL::IdString, RTLIL::Module*> new_modules;
+ for (auto mod : vector<Module*>(design->modules()))
+ if (used_modules[mod->name] || mod->get_bool_attribute("\\blackbox")) {
+ new_modules[mod->name] = mod;
+ } else {
+ log("Deleting now unused module %s.\n", log_id(mod));
+ delete mod;
+ }
+ design->modules_.swap(new_modules);
+ }
+
+ log_pop();
+ }
+} FlattenPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/tribuf.cc b/passes/techmap/tribuf.cc
new file mode 100644
index 00000000..03629082
--- /dev/null
+++ b/passes/techmap/tribuf.cc
@@ -0,0 +1,186 @@
+/*
+ * 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 TribufConfig {
+ bool merge_mode;
+ bool logic_mode;
+
+ TribufConfig() {
+ merge_mode = false;
+ logic_mode = false;
+ }
+};
+
+struct TribufWorker {
+ Module *module;
+ SigMap sigmap;
+ const TribufConfig &config;
+
+ TribufWorker(Module *module, const TribufConfig &config) : module(module), sigmap(module), config(config)
+ {
+ }
+
+ static bool is_all_z(SigSpec sig)
+ {
+ for (auto bit : sig)
+ if (bit != State::Sz)
+ return false;
+ return true;
+ }
+
+ void run()
+ {
+ dict<SigSpec, vector<Cell*>> tribuf_cells;
+ pool<SigBit> output_bits;
+
+ if (config.logic_mode)
+ for (auto wire : module->wires())
+ if (wire->port_output)
+ for (auto bit : sigmap(wire))
+ output_bits.insert(bit);
+
+ for (auto cell : module->selected_cells())
+ {
+ if (cell->type == "$tribuf")
+ tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell);
+
+ if (cell->type == "$_TBUF_")
+ tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell);
+
+ if (cell->type.in("$mux", "$_MUX_"))
+ {
+ IdString en_port = cell->type == "$mux" ? "\\EN" : "\\E";
+ IdString tri_type = cell->type == "$mux" ? "$tribuf" : "$_TBUF_";
+
+ if (is_all_z(cell->getPort("\\A")) && is_all_z(cell->getPort("\\B"))) {
+ module->remove(cell);
+ continue;
+ }
+
+ if (is_all_z(cell->getPort("\\A"))) {
+ cell->setPort("\\A", cell->getPort("\\B"));
+ cell->setPort(en_port, cell->getPort("\\S"));
+ cell->unsetPort("\\B");
+ cell->unsetPort("\\S");
+ cell->type = tri_type;
+ tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell);
+ continue;
+ }
+
+ if (is_all_z(cell->getPort("\\B"))) {
+ cell->setPort(en_port, module->Not(NEW_ID, cell->getPort("\\S")));
+ cell->unsetPort("\\B");
+ cell->unsetPort("\\S");
+ cell->type = tri_type;
+ tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell);
+ continue;
+ }
+ }
+ }
+
+ if (config.merge_mode || config.logic_mode)
+ {
+ for (auto &it : tribuf_cells)
+ {
+ bool no_tribuf = false;
+
+ if (config.logic_mode) {
+ no_tribuf = true;
+ for (auto bit : it.first)
+ if (output_bits.count(bit))
+ no_tribuf = false;
+ }
+
+ if (GetSize(it.second) <= 1 && !no_tribuf)
+ continue;
+
+ SigSpec pmux_b, pmux_s;
+ for (auto cell : it.second) {
+ if (cell->type == "$tribuf")
+ pmux_s.append(cell->getPort("\\EN"));
+ else
+ pmux_s.append(cell->getPort("\\E"));
+ pmux_b.append(cell->getPort("\\A"));
+ module->remove(cell);
+ }
+
+ SigSpec muxout = GetSize(pmux_s) > 1 ? module->Pmux(NEW_ID, SigSpec(State::Sx, GetSize(it.first)), pmux_b, pmux_s) : pmux_b;
+
+ if (no_tribuf)
+ module->connect(it.first, muxout);
+ else
+ module->addTribuf(NEW_ID, muxout, module->ReduceOr(NEW_ID, pmux_s), it.first);
+ }
+ }
+ }
+};
+
+struct TribufPass : public Pass {
+ TribufPass() : Pass("tribuf", "infer tri-state buffers") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" tribuf [options] [selection]\n");
+ log("\n");
+ log("This pass transforms $mux cells with 'z' inputs to tristate buffers.\n");
+ log("\n");
+ log(" -merge\n");
+ log(" merge multiple tri-state buffers driving the same net\n");
+ log(" into a single buffer.\n");
+ log("\n");
+ log(" -logic\n");
+ log(" convert tri-state buffers that do not drive output ports\n");
+ log(" to non-tristate logic. this option implies -merge.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ TribufConfig config;
+
+ log_header(design, "Executing TRIBUF pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-merge") {
+ config.merge_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-logic") {
+ config.logic_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules()) {
+ TribufWorker worker(module, config);
+ worker.run();
+ }
+ }
+} TribufPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/zinit.cc b/passes/techmap/zinit.cc
new file mode 100644
index 00000000..a577e123
--- /dev/null
+++ b/passes/techmap/zinit.cc
@@ -0,0 +1,151 @@
+/*
+ * 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 ZinitPass : public Pass {
+ ZinitPass() : Pass("zinit", "add inverters so all FF are zero-initialized") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" zinit [options] [selection]\n");
+ log("\n");
+ log("Add inverters as needed to make all FFs zero-initialized.\n");
+ log("\n");
+ log(" -all\n");
+ log(" also add zero initialization to uninitialized FFs\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool all_mode = false;
+
+ log_header(design, "Executing ZINIT pass (make all FFs zero-initialized).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-singleton") {
+ all_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ SigMap sigmap(module);
+ dict<SigBit, State> initbits;
+ pool<SigBit> donebits;
+
+ for (auto wire : module->selected_wires())
+ {
+ if (wire->attributes.count("\\init") == 0)
+ continue;
+
+ SigSpec wirebits = sigmap(wire);
+ Const initval = wire->attributes.at("\\init");
+ wire->attributes.erase("\\init");
+
+ for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++)
+ {
+ SigBit bit = wirebits[i];
+ State val = initval[i];
+
+ if (val != State::S0 && val != State::S1 && bit.wire != nullptr)
+ continue;
+
+ if (initbits.count(bit)) {
+ if (initbits.at(bit) != val)
+ log_error("Conflicting init values for signal %s (%s = %s != %s).\n",
+ log_signal(bit), log_signal(SigBit(wire, i)),
+ log_signal(val), log_signal(initbits.at(bit)));
+ continue;
+ }
+
+ initbits[bit] = val;
+ }
+ }
+
+ pool<IdString> dff_types = {
+ "$ff", "$dff", "$dffe", "$dffsr", "$adff",
+ "$_FF_", "$_DFFE_NN_", "$_DFFE_NP_", "$_DFFE_PN_", "$_DFFE_PP_",
+ "$_DFFSR_NNN_", "$_DFFSR_NNP_", "$_DFFSR_NPN_", "$_DFFSR_NPP_",
+ "$_DFFSR_PNN_", "$_DFFSR_PNP_", "$_DFFSR_PPN_", "$_DFFSR_PPP_",
+ "$_DFF_N_", "$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_",
+ "$_DFF_P_", "$_DFF_PN0_", "$_DFF_PN1_", "$_DFF_PP0_", "$_DFF_PP1_"
+ };
+
+ for (auto cell : module->selected_cells())
+ {
+ if (!dff_types.count(cell->type))
+ continue;
+
+ SigSpec sig_d = sigmap(cell->getPort("\\D"));
+ SigSpec sig_q = sigmap(cell->getPort("\\Q"));
+
+ if (GetSize(sig_d) < 1 || GetSize(sig_q) < 1)
+ continue;
+
+ 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]));
+ donebits.insert(sig_q[i]);
+ } else
+ initval.bits.push_back(all_mode ? State::S0 : State::Sx);
+ }
+
+ Wire *initwire = module->addWire(NEW_ID, GetSize(initval));
+ initwire->attributes["\\init"] = initval;
+
+ for (int i = 0; i < GetSize(initwire); i++)
+ if (initval.bits.at(i) == State::S1)
+ {
+ sig_d[i] = module->NotGate(NEW_ID, sig_d[i]);
+ module->addNotGate(NEW_ID, SigSpec(initwire, i), sig_q[i]);
+ initwire->attributes["\\init"].bits.at(i) = State::S0;
+ }
+ else
+ {
+ module->connect(sig_q[i], SigSpec(initwire, i));
+ }
+
+ log("FF init value for cell %s (%s): %s = %s\n", log_id(cell), log_id(cell->type),
+ log_signal(sig_q), log_signal(initval));
+
+ cell->setPort("\\D", sig_d);
+ cell->setPort("\\Q", initwire);
+ }
+
+ for (auto &it : initbits)
+ if (donebits.count(it.first) == 0)
+ log_error("Failed to handle init bit %s = %s.\n", log_signal(it.first), log_signal(it.second));
+ }
+ }
+} ZinitPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/tests/Makefile.inc b/passes/tests/Makefile.inc
new file mode 100644
index 00000000..531943d7
--- /dev/null
+++ b/passes/tests/Makefile.inc
@@ -0,0 +1,5 @@
+
+OBJS += passes/tests/test_autotb.o
+OBJS += passes/tests/test_cell.o
+OBJS += passes/tests/test_abcloop.o
+
diff --git a/passes/tests/test_abcloop.cc b/passes/tests/test_abcloop.cc
new file mode 100644
index 00000000..09cb4195
--- /dev/null
+++ b/passes/tests/test_abcloop.cc
@@ -0,0 +1,289 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2014 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.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/satgen.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+static uint32_t xorshift32_state = 123456789;
+
+static uint32_t xorshift32(uint32_t limit) {
+ xorshift32_state ^= xorshift32_state << 13;
+ xorshift32_state ^= xorshift32_state >> 17;
+ xorshift32_state ^= xorshift32_state << 5;
+ return xorshift32_state % limit;
+}
+
+static RTLIL::Wire *getw(std::vector<RTLIL::Wire*> &wires, RTLIL::Wire *w)
+{
+ while (1) {
+ int idx = xorshift32(GetSize(wires));
+ if (wires[idx] != w && !wires[idx]->port_output)
+ return wires[idx];
+ }
+}
+
+static void test_abcloop()
+{
+ log("Rng seed value: %u\n", int(xorshift32_state));
+
+ RTLIL::Design *design = new RTLIL::Design;
+ RTLIL::Module *module = nullptr;
+ RTLIL::SigSpec in_sig, out_sig;
+
+ bool truthtab[16][4];
+ int create_cycles = 0;
+
+ while (1)
+ {
+ module = design->addModule("\\uut");
+ create_cycles++;
+
+ in_sig = {};
+ out_sig = {};
+
+ std::vector<RTLIL::Wire*> wires;
+
+ for (int i = 0; i < 4; i++) {
+ RTLIL::Wire *w = module->addWire(stringf("\\i%d", i));
+ w->port_input = true;
+ wires.push_back(w);
+ in_sig.append(w);
+ }
+
+ for (int i = 0; i < 4; i++) {
+ RTLIL::Wire *w = module->addWire(stringf("\\o%d", i));
+ w->port_output = true;
+ wires.push_back(w);
+ out_sig.append(w);
+ }
+
+ for (int i = 0; i < 16; i++) {
+ RTLIL::Wire *w = module->addWire(stringf("\\t%d", i));
+ wires.push_back(w);
+ }
+
+ for (auto w : wires)
+ if (!w->port_input)
+ switch (xorshift32(12))
+ {
+ case 0:
+ module->addNotGate(w->name.str() + "g", getw(wires, w), w);
+ break;
+ case 1:
+ module->addAndGate(w->name.str() + "g", getw(wires, w), getw(wires, w), w);
+ break;
+ case 2:
+ module->addNandGate(w->name.str() + "g", getw(wires, w), getw(wires, w), w);
+ break;
+ case 3:
+ module->addOrGate(w->name.str() + "g", getw(wires, w), getw(wires, w), w);
+ break;
+ case 4:
+ module->addNorGate(w->name.str() + "g", getw(wires, w), getw(wires, w), w);
+ break;
+ case 5:
+ module->addXorGate(w->name.str() + "g", getw(wires, w), getw(wires, w), w);
+ break;
+ case 6:
+ module->addXnorGate(w->name.str() + "g", getw(wires, w), getw(wires, w), w);
+ break;
+ case 7:
+ module->addMuxGate(w->name.str() + "g", getw(wires, w), getw(wires, w), getw(wires, w), w);
+ break;
+ case 8:
+ module->addAoi3Gate(w->name.str() + "g", getw(wires, w), getw(wires, w), getw(wires, w), w);
+ break;
+ case 9:
+ module->addOai3Gate(w->name.str() + "g", getw(wires, w), getw(wires, w), getw(wires, w), w);
+ break;
+ case 10:
+ module->addAoi4Gate(w->name.str() + "g", getw(wires, w), getw(wires, w), getw(wires, w), getw(wires, w), w);
+ break;
+ case 11:
+ module->addOai4Gate(w->name.str() + "g", getw(wires, w), getw(wires, w), getw(wires, w), getw(wires, w), w);
+ break;
+ }
+
+ module->fixup_ports();
+ Pass::call(design, "clean");
+
+ ezSatPtr ez;
+ SigMap sigmap(module);
+ SatGen satgen(ez.get(), &sigmap);
+
+ for (auto c : module->cells()) {
+ bool ok YS_ATTRIBUTE(unused) = satgen.importCell(c);
+ log_assert(ok);
+ }
+
+ std::vector<int> in_vec = satgen.importSigSpec(in_sig);
+ std::vector<int> inverse_in_vec = ez->vec_not(in_vec);
+
+ std::vector<int> out_vec = satgen.importSigSpec(out_sig);
+
+ for (int i = 0; i < 16; i++)
+ {
+ std::vector<int> assumptions;
+ for (int j = 0; j < GetSize(in_vec); j++)
+ assumptions.push_back((i & (1 << j)) ? in_vec.at(j) : inverse_in_vec.at(j));
+
+ std::vector<bool> results;
+ if (!ez->solve(out_vec, results, assumptions)) {
+ log("No stable solution for input %d found -> recreate module.\n", i);
+ goto recreate_module;
+ }
+
+ for (int j = 0; j < 4; j++)
+ truthtab[i][j] = results[j];
+
+ assumptions.push_back(ez->vec_ne(out_vec, ez->vec_const(results)));
+
+ std::vector<bool> results2;
+ if (ez->solve(out_vec, results2, assumptions)) {
+ log("Two stable solutions for input %d found -> recreate module.\n", i);
+ goto recreate_module;
+ }
+ }
+ break;
+
+ recreate_module:
+ design->remove(module);
+ }
+
+ log("Found viable UUT after %d cycles:\n", create_cycles);
+ Pass::call(design, "write_ilang");
+ Pass::call(design, "abc");
+
+ log("\n");
+ log("Pre- and post-abc truth table:\n");
+
+ ezSatPtr ez;
+ SigMap sigmap(module);
+ SatGen satgen(ez.get(), &sigmap);
+
+ for (auto c : module->cells()) {
+ bool ok YS_ATTRIBUTE(unused) = satgen.importCell(c);
+ log_assert(ok);
+ }
+
+ std::vector<int> in_vec = satgen.importSigSpec(in_sig);
+ std::vector<int> inverse_in_vec = ez->vec_not(in_vec);
+
+ std::vector<int> out_vec = satgen.importSigSpec(out_sig);
+
+ bool found_error = false;
+ bool truthtab2[16][4];
+
+ for (int i = 0; i < 16; i++)
+ {
+ std::vector<int> assumptions;
+ for (int j = 0; j < GetSize(in_vec); j++)
+ assumptions.push_back((i & (1 << j)) ? in_vec.at(j) : inverse_in_vec.at(j));
+
+ for (int j = 0; j < 4; j++)
+ truthtab2[i][j] = truthtab[i][j];
+
+ std::vector<bool> results;
+ if (!ez->solve(out_vec, results, assumptions)) {
+ log("No stable solution for input %d found.\n", i);
+ found_error = true;
+ continue;
+ }
+
+ for (int j = 0; j < 4; j++)
+ truthtab2[i][j] = results[j];
+
+ assumptions.push_back(ez->vec_ne(out_vec, ez->vec_const(results)));
+
+ std::vector<bool> results2;
+ if (ez->solve(out_vec, results2, assumptions)) {
+ log("Two stable solutions for input %d found -> recreate module.\n", i);
+ found_error = true;
+ }
+ }
+
+ for (int i = 0; i < 16; i++) {
+ log("%3d ", i);
+ for (int j = 0; j < 4; j++)
+ log("%c", truthtab[i][j] ? '1' : '0');
+ log(" ");
+ for (int j = 0; j < 4; j++)
+ log("%c", truthtab2[i][j] ? '1' : '0');
+ for (int j = 0; j < 4; j++)
+ if (truthtab[i][j] != truthtab2[i][j]) {
+ found_error = true;
+ log(" !");
+ break;
+ }
+ log("\n");
+ }
+
+ log_assert(found_error == false);
+ log("\n");
+}
+
+struct TestAbcloopPass : public Pass {
+ TestAbcloopPass() : Pass("test_abcloop", "automatically test handling of loops in abc command") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" test_abcloop [options]\n");
+ log("\n");
+ log("Test handling of logic loops in ABC.\n");
+ log("\n");
+ log(" -n {integer}\n");
+ log(" create this number of circuits and test them (default = 100).\n");
+ log("\n");
+ log(" -s {positive_integer}\n");
+ log(" use this value as rng seed value (default = unix time).\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design*)
+ {
+ int num_iter = 100;
+ xorshift32_state = 0;
+
+ int argidx;
+ for (argidx = 1; argidx < GetSize(args); argidx++)
+ {
+ if (args[argidx] == "-n" && argidx+1 < GetSize(args)) {
+ num_iter = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-s" && argidx+1 < GetSize(args)) {
+ xorshift32_state = atoi(args[++argidx].c_str());
+ continue;
+ }
+ break;
+ }
+
+ if (xorshift32_state == 0)
+ xorshift32_state = time(NULL) & 0x7fffffff;
+
+ for (int i = 0; i < num_iter; i++)
+ test_abcloop();
+ }
+} TestAbcloopPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/tests/test_autotb.cc b/passes/tests/test_autotb.cc
new file mode 100644
index 00000000..cb31056f
--- /dev/null
+++ b/passes/tests/test_autotb.cc
@@ -0,0 +1,379 @@
+/*
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+static std::string id(std::string internal_id)
+{
+ const char *str = internal_id.c_str();
+ bool do_escape = false;
+
+ if (*str == '\\')
+ str++;
+
+ if ('0' <= *str && *str <= '9')
+ do_escape = true;
+
+ for (int i = 0; str[i]; i++) {
+ if ('0' <= str[i] && str[i] <= '9')
+ continue;
+ if ('a' <= str[i] && str[i] <= 'z')
+ continue;
+ if ('A' <= str[i] && str[i] <= 'Z')
+ continue;
+ if (str[i] == '_')
+ continue;
+ do_escape = true;
+ break;
+ }
+
+ if (do_escape)
+ return "\\" + std::string(str) + " ";
+ return std::string(str);
+}
+
+static std::string idx(std::string str)
+{
+ if (str[0] == '\\')
+ return str.substr(1);
+ return str;
+}
+
+static std::string idy(std::string str1, std::string str2 = std::string(), std::string str3 = std::string())
+{
+ str1 = idx(str1);
+ if (!str2.empty())
+ str1 += "_" + idx(str2);
+ if (!str3.empty())
+ str1 += "_" + idx(str3);
+ return id(str1);
+}
+
+static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter, int seed)
+{
+ f << stringf("`ifndef outfile\n");
+ f << stringf("\t`define outfile \"/dev/stdout\"\n");
+ f << stringf("`endif\n");
+
+ f << stringf("module testbench;\n\n");
+
+ f << stringf("integer i;\n");
+ f << stringf("integer file;\n\n");
+
+ f << stringf("reg [31:0] xorshift128_x = 123456789;\n");
+ f << stringf("reg [31:0] xorshift128_y = 362436069;\n");
+ f << stringf("reg [31:0] xorshift128_z = 521288629;\n");
+ f << stringf("reg [31:0] xorshift128_w = %u; // <-- seed value\n", seed ? seed : int(time(NULL)));
+ f << stringf("reg [31:0] xorshift128_t;\n\n");
+ f << stringf("task xorshift128;\n");
+ f << stringf("begin\n");
+ f << stringf("\txorshift128_t = xorshift128_x ^ (xorshift128_x << 11);\n");
+ f << stringf("\txorshift128_x = xorshift128_y;\n");
+ f << stringf("\txorshift128_y = xorshift128_z;\n");
+ f << stringf("\txorshift128_z = xorshift128_w;\n");
+ f << stringf("\txorshift128_w = xorshift128_w ^ (xorshift128_w >> 19) ^ xorshift128_t ^ (xorshift128_t >> 8);\n");
+ f << stringf("end\n");
+ f << stringf("endtask\n\n");
+
+ for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it)
+ {
+ std::map<std::string, int> signal_in;
+ std::map<std::string, std::string> signal_const;
+ std::map<std::string, int> signal_clk;
+ std::map<std::string, int> signal_out;
+
+ RTLIL::Module *mod = it->second;
+
+ if (mod->get_bool_attribute("\\gentb_skip"))
+ continue;
+
+ int count_ports = 0;
+ log("Generating test bench for module `%s'.\n", it->first.c_str());
+ for (auto it2 = mod->wires_.begin(); it2 != mod->wires_.end(); ++it2) {
+ RTLIL::Wire *wire = it2->second;
+ if (wire->port_output) {
+ count_ports++;
+ signal_out[idy("sig", mod->name.str(), wire->name.str())] = wire->width;
+ f << stringf("wire [%d:0] %s;\n", wire->width-1, idy("sig", mod->name.str(), wire->name.str()).c_str());
+ } else if (wire->port_input) {
+ count_ports++;
+ bool is_clksignal = wire->get_bool_attribute("\\gentb_clock");
+ for (auto it3 = mod->processes.begin(); it3 != mod->processes.end(); ++it3)
+ for (auto it4 = it3->second->syncs.begin(); it4 != it3->second->syncs.end(); ++it4) {
+ if ((*it4)->type == RTLIL::ST0 || (*it4)->type == RTLIL::ST1)
+ continue;
+ RTLIL::SigSpec &signal = (*it4)->signal;
+ for (auto &c : signal.chunks())
+ if (c.wire == wire)
+ is_clksignal = true;
+ }
+ if (is_clksignal && wire->attributes.count("\\gentb_constant") == 0) {
+ signal_clk[idy("sig", mod->name.str(), wire->name.str())] = wire->width;
+ } else {
+ signal_in[idy("sig", mod->name.str(), wire->name.str())] = wire->width;
+ if (wire->attributes.count("\\gentb_constant") != 0)
+ signal_const[idy("sig", mod->name.str(), wire->name.str())] = wire->attributes["\\gentb_constant"].as_string();
+ }
+ f << stringf("reg [%d:0] %s;\n", wire->width-1, idy("sig", mod->name.str(), wire->name.str()).c_str());
+ }
+ }
+ f << stringf("%s %s(\n", id(mod->name.str()).c_str(), idy("uut", mod->name.str()).c_str());
+ for (auto it2 = mod->wires_.begin(); it2 != mod->wires_.end(); ++it2) {
+ RTLIL::Wire *wire = it2->second;
+ if (wire->port_output || wire->port_input)
+ f << stringf("\t.%s(%s)%s\n", id(wire->name.str()).c_str(),
+ idy("sig", mod->name.str(), wire->name.str()).c_str(), --count_ports ? "," : "");
+ }
+ f << stringf(");\n\n");
+
+ f << stringf("task %s;\n", idy(mod->name.str(), "reset").c_str());
+ f << stringf("begin\n");
+ int delay_counter = 0;
+ for (auto it = signal_in.begin(); it != signal_in.end(); ++it)
+ f << stringf("\t%s <= #%d 0;\n", it->first.c_str(), ++delay_counter*2);
+ for (auto it = signal_clk.begin(); it != signal_clk.end(); ++it)
+ f << stringf("\t%s <= #%d 0;\n", it->first.c_str(), ++delay_counter*2);
+ f << stringf("\t#%d;\n", ((2*delay_counter+99)/100)*100);
+ for (auto it = signal_clk.begin(); it != signal_clk.end(); ++it) {
+ f << stringf("\t#100; %s <= 1;\n", it->first.c_str());
+ f << stringf("\t#100; %s <= 0;\n", it->first.c_str());
+ }
+ delay_counter = 0;
+ for (auto it = signal_in.begin(); it != signal_in.end(); ++it)
+ f << stringf("\t%s <= #%d ~0;\n", it->first.c_str(), ++delay_counter*2);
+ f << stringf("\t#%d;\n", ((2*delay_counter+99)/100)*100);
+ for (auto it = signal_clk.begin(); it != signal_clk.end(); ++it) {
+ f << stringf("\t#100; %s <= 1;\n", it->first.c_str());
+ f << stringf("\t#100; %s <= 0;\n", it->first.c_str());
+ }
+ delay_counter = 0;
+ for (auto it = signal_in.begin(); it != signal_in.end(); ++it) {
+ if (signal_const.count(it->first) == 0)
+ continue;
+ f << stringf("\t%s <= #%d 'b%s;\n", it->first.c_str(), ++delay_counter*2, signal_const[it->first].c_str());
+ }
+ f << stringf("\t#%d;\n", ((2*delay_counter+99)/100)*100);
+ f << stringf("end\n");
+ f << stringf("endtask\n\n");
+
+ f << stringf("task %s;\n", idy(mod->name.str(), "update_data").c_str());
+ f << stringf("begin\n");
+ delay_counter = 0;
+ for (auto it = signal_in.begin(); it != signal_in.end(); it++) {
+ if (signal_const.count(it->first) > 0)
+ continue;
+ f << stringf("\txorshift128;\n");
+ f << stringf("\t%s <= #%d { xorshift128_x, xorshift128_y, xorshift128_z, xorshift128_w };\n", it->first.c_str(), ++delay_counter*2);
+ }
+ f << stringf("\t#%d;\n", ((2*delay_counter+99)/100)*100);
+ f << stringf("end\n");
+ f << stringf("endtask\n\n");
+
+ f << stringf("task %s;\n", idy(mod->name.str(), "update_clock").c_str());
+ f << stringf("begin\n");
+ if (signal_clk.size()) {
+ f << stringf("\txorshift128;\n");
+ f << stringf("\t{");
+ int total_clock_bits = 0;
+ for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) {
+ f << stringf("%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str());
+ total_clock_bits += it->second;
+ }
+ f << stringf(" } = {");
+ for (auto it = signal_clk.begin(); it != signal_clk.end(); it++)
+ f << stringf("%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str());
+ f << stringf(" } ^ (%d'b1 << (xorshift128_w %% %d));\n", total_clock_bits, total_clock_bits + 1);
+ }
+ f << stringf("end\n");
+ f << stringf("endtask\n\n");
+
+ char shorthand = 'A';
+ std::vector<std::string> header1;
+ std::string header2 = "";
+
+ f << stringf("task %s;\n", idy(mod->name.str(), "print_status").c_str());
+ f << stringf("begin\n");
+ f << stringf("\t$fdisplay(file, \"#OUT# %%b %%b %%b %%t %%d\", {");
+ if (signal_in.size())
+ for (auto it = signal_in.begin(); it != signal_in.end(); it++) {
+ f << stringf("%s %s", it == signal_in.begin() ? "" : ",", it->first.c_str());
+ int len = it->second;
+ header2 += ", \"";
+ if (len > 1)
+ header2 += "/", len--;
+ while (len > 1)
+ header2 += "-", len--;
+ if (len > 0)
+ header2 += shorthand, len--;
+ header2 += "\"";
+ header1.push_back(" " + it->first);
+ header1.back()[0] = shorthand;
+ shorthand = shorthand == 'Z' ? 'A' : shorthand+1;
+ }
+ else {
+ f << stringf(" 1'bx");
+ header2 += ", \"#\"";
+ }
+ f << stringf(" }, {");
+ header2 += ", \" \"";
+ if (signal_clk.size()) {
+ for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) {
+ f << stringf("%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str());
+ int len = it->second;
+ header2 += ", \"";
+ if (len > 1)
+ header2 += "/", len--;
+ while (len > 1)
+ header2 += "-", len--;
+ if (len > 0)
+ header2 += shorthand, len--;
+ header2 += "\"";
+ header1.push_back(" " + it->first);
+ header1.back()[0] = shorthand;
+ shorthand = shorthand == 'Z' ? 'A' : shorthand+1;
+ }
+ } else {
+ f << stringf(" 1'bx");
+ header2 += ", \"#\"";
+ }
+ f << stringf(" }, {");
+ header2 += ", \" \"";
+ if (signal_out.size()) {
+ for (auto it = signal_out.begin(); it != signal_out.end(); it++) {
+ f << stringf("%s %s", it == signal_out.begin() ? "" : ",", it->first.c_str());
+ int len = it->second;
+ header2 += ", \"";
+ if (len > 1)
+ header2 += "/", len--;
+ while (len > 1)
+ header2 += "-", len--;
+ if (len > 0)
+ header2 += shorthand, len--;
+ header2 += "\"";
+ header1.push_back(" " + it->first);
+ header1.back()[0] = shorthand;
+ shorthand = shorthand == 'Z' ? 'A' : shorthand+1;
+ }
+ } else {
+ f << stringf(" 1'bx");
+ header2 += ", \"#\"";
+ }
+ f << stringf(" }, $time, i);\n");
+ f << stringf("end\n");
+ f << stringf("endtask\n\n");
+
+ f << stringf("task %s;\n", idy(mod->name.str(), "print_header").c_str());
+ f << stringf("begin\n");
+ f << stringf("\t$fdisplay(file, \"#OUT#\");\n");
+ for (auto &hdr : header1)
+ f << stringf("\t$fdisplay(file, \"#OUT# %s\");\n", hdr.c_str());
+ f << stringf("\t$fdisplay(file, \"#OUT#\");\n");
+ f << stringf("\t$fdisplay(file, {\"#OUT# \"%s});\n", header2.c_str());
+ f << stringf("end\n");
+ f << stringf("endtask\n\n");
+
+ f << stringf("task %s;\n", idy(mod->name.str(), "test").c_str());
+ f << stringf("begin\n");
+ f << stringf("\t$fdisplay(file, \"#OUT#\\n#OUT# ==== %s ====\");\n", idy(mod->name.str()).c_str());
+ f << stringf("\t%s;\n", idy(mod->name.str(), "reset").c_str());
+ f << stringf("\tfor (i=0; i<%d; i=i+1) begin\n", num_iter);
+ f << stringf("\t\tif (i %% 20 == 0) %s;\n", idy(mod->name.str(), "print_header").c_str());
+ f << stringf("\t\t#100; %s;\n", idy(mod->name.str(), "update_data").c_str());
+ f << stringf("\t\t#100; %s;\n", idy(mod->name.str(), "update_clock").c_str());
+ f << stringf("\t\t#100; %s;\n", idy(mod->name.str(), "print_status").c_str());
+ f << stringf("\tend\n");
+ f << stringf("end\n");
+ f << stringf("endtask\n\n");
+ }
+
+ f << stringf("initial begin\n");
+ f << stringf("\t// $dumpfile(\"testbench.vcd\");\n");
+ f << stringf("\t// $dumpvars(0, testbench);\n");
+ f << stringf("\tfile = $fopen(`outfile);\n");
+ for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it)
+ if (!it->second->get_bool_attribute("\\gentb_skip"))
+ f << stringf("\t%s;\n", idy(it->first.str(), "test").c_str());
+ f << stringf("\t$fclose(file);\n");
+ f << stringf("\t$finish;\n");
+ f << stringf("end\n\n");
+
+ f << stringf("endmodule\n");
+}
+
+struct TestAutotbBackend : public Backend {
+ TestAutotbBackend() : Backend("=test_autotb", "generate simple test benches") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" test_autotb [options] [filename]\n");
+ log("\n");
+ log("Automatically create primitive Verilog test benches for all modules in the\n");
+ log("design. The generated testbenches toggle the input pins of the module in\n");
+ log("a semi-random manner and dumps the resulting output signals.\n");
+ log("\n");
+ log("This can be used to check the synthesis results for simple circuits by\n");
+ log("comparing the testbench output for the input files and the synthesis results.\n");
+ log("\n");
+ log("The backend automatically detects clock signals. Additionally a signal can\n");
+ log("be forced to be interpreted as clock signal by setting the attribute\n");
+ log("'gentb_clock' on the signal.\n");
+ log("\n");
+ log("The attribute 'gentb_constant' can be used to force a signal to a constant\n");
+ log("value after initialization. This can e.g. be used to force a reset signal\n");
+ log("low in order to explore more inner states in a state machine.\n");
+ log("\n");
+ log(" -n <int>\n");
+ log(" number of iterations the test bench should run (default = 1000)\n");
+ log("\n");
+ }
+ virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ int num_iter = 1000;
+ int seed = 0;
+
+ log_header(design, "Executing TEST_AUTOTB backend (auto-generate pseudo-random test benches).\n");
+
+ int argidx;
+ for (argidx = 1; argidx < GetSize(args); argidx++)
+ {
+ if (args[argidx] == "-n" && argidx+1 < GetSize(args)) {
+ num_iter = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-seed" && argidx+1 < GetSize(args)) {
+ seed = atoi(args[++argidx].c_str());
+ continue;
+ }
+ break;
+ }
+
+ extra_args(f, filename, args, argidx);
+ autotest(*f, design, num_iter, seed);
+ }
+} TestAutotbBackend;
+
+PRIVATE_NAMESPACE_END
+
diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc
new file mode 100644
index 00000000..049c2053
--- /dev/null
+++ b/passes/tests/test_cell.cc
@@ -0,0 +1,965 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2014 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.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/satgen.h"
+#include "kernel/consteval.h"
+#include "kernel/celledges.h"
+#include "kernel/macc.h"
+#include <algorithm>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+static uint32_t xorshift32_state = 123456789;
+
+static uint32_t xorshift32(uint32_t limit) {
+ xorshift32_state ^= xorshift32_state << 13;
+ xorshift32_state ^= xorshift32_state >> 17;
+ xorshift32_state ^= xorshift32_state << 5;
+ return xorshift32_state % limit;
+}
+
+static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, std::string cell_type_flags, bool constmode, bool muxdiv)
+{
+ RTLIL::Module *module = design->addModule("\\gold");
+ RTLIL::Cell *cell = module->addCell("\\UUT", cell_type);
+ RTLIL::Wire *wire;
+
+ if (cell_type == "$mux" || cell_type == "$pmux")
+ {
+ int width = 1 + xorshift32(8);
+ int swidth = cell_type == "$mux" ? 1 : 1 + xorshift32(8);
+
+ wire = module->addWire("\\A");
+ wire->width = width;
+ wire->port_input = true;
+ cell->setPort("\\A", wire);
+
+ wire = module->addWire("\\B");
+ wire->width = width * swidth;
+ wire->port_input = true;
+ cell->setPort("\\B", wire);
+
+ wire = module->addWire("\\S");
+ wire->width = swidth;
+ wire->port_input = true;
+ cell->setPort("\\S", wire);
+
+ wire = module->addWire("\\Y");
+ wire->width = width;
+ wire->port_output = true;
+ cell->setPort("\\Y", wire);
+ }
+
+ if (cell_type == "$fa")
+ {
+ int width = 1 + xorshift32(8);
+
+ wire = module->addWire("\\A");
+ wire->width = width;
+ wire->port_input = true;
+ cell->setPort("\\A", wire);
+
+ wire = module->addWire("\\B");
+ wire->width = width;
+ wire->port_input = true;
+ cell->setPort("\\B", wire);
+
+ wire = module->addWire("\\C");
+ wire->width = width;
+ wire->port_input = true;
+ cell->setPort("\\C", wire);
+
+ wire = module->addWire("\\X");
+ wire->width = width;
+ wire->port_output = true;
+ cell->setPort("\\X", wire);
+
+ wire = module->addWire("\\Y");
+ wire->width = width;
+ wire->port_output = true;
+ cell->setPort("\\Y", wire);
+ }
+
+ if (cell_type == "$lcu")
+ {
+ int width = 1 + xorshift32(8);
+
+ wire = module->addWire("\\P");
+ wire->width = width;
+ wire->port_input = true;
+ cell->setPort("\\P", wire);
+
+ wire = module->addWire("\\G");
+ wire->width = width;
+ wire->port_input = true;
+ cell->setPort("\\G", wire);
+
+ wire = module->addWire("\\CI");
+ wire->port_input = true;
+ cell->setPort("\\CI", wire);
+
+ wire = module->addWire("\\CO");
+ wire->width = width;
+ wire->port_output = true;
+ cell->setPort("\\CO", wire);
+ }
+
+ if (cell_type == "$macc")
+ {
+ Macc macc;
+ int width = 1 + xorshift32(8);
+ int depth = 1 + xorshift32(6);
+ int mulbits_a = 0, mulbits_b = 0;
+
+ RTLIL::Wire *wire_a = module->addWire("\\A");
+ wire_a->width = 0;
+ wire_a->port_input = true;
+
+ for (int i = 0; i < depth; i++)
+ {
+ int size_a = xorshift32(width) + 1;
+ int size_b = depth > 4 ? 0 : xorshift32(width) + 1;
+
+ if (mulbits_a + size_a*size_b <= 96 && mulbits_b + size_a + size_b <= 16 && xorshift32(2) == 1) {
+ mulbits_a += size_a * size_b;
+ mulbits_b += size_a + size_b;
+ } else
+ size_b = 0;
+
+ Macc::port_t this_port;
+
+ wire_a->width += size_a;
+ this_port.in_a = RTLIL::SigSpec(wire_a, wire_a->width - size_a, size_a);
+
+ wire_a->width += size_b;
+ this_port.in_b = RTLIL::SigSpec(wire_a, wire_a->width - size_b, size_b);
+
+ this_port.is_signed = xorshift32(2) == 1;
+ this_port.do_subtract = xorshift32(2) == 1;
+ macc.ports.push_back(this_port);
+ }
+
+ wire = module->addWire("\\B");
+ wire->width = xorshift32(mulbits_a ? xorshift32(4)+1 : xorshift32(16)+1);
+ wire->port_input = true;
+ macc.bit_ports = wire;
+
+ wire = module->addWire("\\Y");
+ wire->width = width;
+ wire->port_output = true;
+ cell->setPort("\\Y", wire);
+
+ macc.to_cell(cell);
+ }
+
+ if (cell_type == "$lut")
+ {
+ int width = 1 + xorshift32(6);
+
+ wire = module->addWire("\\A");
+ wire->width = width;
+ wire->port_input = true;
+ cell->setPort("\\A", wire);
+
+ wire = module->addWire("\\Y");
+ wire->port_output = true;
+ cell->setPort("\\Y", wire);
+
+ RTLIL::SigSpec config;
+ for (int i = 0; i < (1 << width); i++)
+ config.append(xorshift32(2) ? RTLIL::S1 : RTLIL::S0);
+
+ cell->setParam("\\LUT", config.as_const());
+ }
+
+ if (cell_type == "$sop")
+ {
+ int width = 1 + xorshift32(8);
+ int depth = 1 + xorshift32(8);
+
+ wire = module->addWire("\\A");
+ wire->width = width;
+ wire->port_input = true;
+ cell->setPort("\\A", wire);
+
+ wire = module->addWire("\\Y");
+ wire->port_output = true;
+ cell->setPort("\\Y", wire);
+
+ RTLIL::SigSpec config;
+ for (int i = 0; i < width*depth; i++)
+ switch (xorshift32(3)) {
+ case 0:
+ config.append(RTLIL::S1);
+ config.append(RTLIL::S0);
+ break;
+ case 1:
+ config.append(RTLIL::S0);
+ config.append(RTLIL::S1);
+ break;
+ case 2:
+ config.append(RTLIL::S0);
+ config.append(RTLIL::S0);
+ break;
+ }
+
+ cell->setParam("\\DEPTH", depth);
+ cell->setParam("\\TABLE", config.as_const());
+ }
+
+ if (cell_type_flags.find('A') != std::string::npos) {
+ wire = module->addWire("\\A");
+ wire->width = 1 + xorshift32(8);
+ wire->port_input = true;
+ cell->setPort("\\A", wire);
+ }
+
+ if (cell_type_flags.find('B') != std::string::npos) {
+ wire = module->addWire("\\B");
+ if (cell_type_flags.find('h') != std::string::npos)
+ wire->width = 1 + xorshift32(6);
+ else
+ wire->width = 1 + xorshift32(8);
+ wire->port_input = true;
+ cell->setPort("\\B", wire);
+ }
+
+ if (cell_type_flags.find('S') != std::string::npos && xorshift32(2)) {
+ if (cell_type_flags.find('A') != std::string::npos)
+ cell->parameters["\\A_SIGNED"] = true;
+ if (cell_type_flags.find('B') != std::string::npos)
+ cell->parameters["\\B_SIGNED"] = true;
+ }
+
+ if (cell_type_flags.find('s') != std::string::npos) {
+ if (cell_type_flags.find('A') != std::string::npos && xorshift32(2))
+ cell->parameters["\\A_SIGNED"] = true;
+ if (cell_type_flags.find('B') != std::string::npos && xorshift32(2))
+ cell->parameters["\\B_SIGNED"] = true;
+ }
+
+ if (cell_type_flags.find('Y') != std::string::npos) {
+ wire = module->addWire("\\Y");
+ wire->width = 1 + xorshift32(8);
+ wire->port_output = true;
+ cell->setPort("\\Y", wire);
+ }
+
+ if (muxdiv && (cell_type == "$div" || cell_type == "$mod")) {
+ auto b_not_zero = module->ReduceBool(NEW_ID, cell->getPort("\\B"));
+ auto div_out = module->addWire(NEW_ID, GetSize(cell->getPort("\\Y")));
+ module->addMux(NEW_ID, RTLIL::SigSpec(0, GetSize(div_out)), div_out, b_not_zero, cell->getPort("\\Y"));
+ cell->setPort("\\Y", div_out);
+ }
+
+ if (cell_type == "$alu")
+ {
+ wire = module->addWire("\\CI");
+ wire->port_input = true;
+ cell->setPort("\\CI", wire);
+
+ wire = module->addWire("\\BI");
+ wire->port_input = true;
+ cell->setPort("\\BI", wire);
+
+ wire = module->addWire("\\X");
+ wire->width = GetSize(cell->getPort("\\Y"));
+ wire->port_output = true;
+ cell->setPort("\\X", wire);
+
+ wire = module->addWire("\\CO");
+ wire->width = GetSize(cell->getPort("\\Y"));
+ wire->port_output = true;
+ cell->setPort("\\CO", wire);
+ }
+
+ if (constmode)
+ {
+ auto conn_list = cell->connections();
+ for (auto &conn : conn_list)
+ {
+ RTLIL::SigSpec sig = conn.second;
+
+ if (GetSize(sig) == 0 || sig[0].wire == nullptr || sig[0].wire->port_output)
+ continue;
+
+ int n, m;
+ switch (xorshift32(5))
+ {
+ case 0:
+ n = xorshift32(GetSize(sig) + 1);
+ for (int i = 0; i < n; i++)
+ sig[i] = xorshift32(2) == 1 ? RTLIL::S1 : RTLIL::S0;
+ break;
+ case 1:
+ n = xorshift32(GetSize(sig) + 1);
+ for (int i = n; i < GetSize(sig); i++)
+ sig[i] = xorshift32(2) == 1 ? RTLIL::S1 : RTLIL::S0;
+ break;
+ case 2:
+ n = xorshift32(GetSize(sig));
+ m = xorshift32(GetSize(sig));
+ for (int i = min(n, m); i < max(n, m); i++)
+ sig[i] = xorshift32(2) == 1 ? RTLIL::S1 : RTLIL::S0;
+ break;
+ }
+
+ cell->setPort(conn.first, sig);
+ }
+ }
+
+ module->fixup_ports();
+ cell->fixup_parameters();
+ cell->check();
+}
+
+static void run_edges_test(RTLIL::Design *design, bool verbose)
+{
+ Module *module = *design->modules().begin();
+ Cell *cell = *module->cells().begin();
+
+ ezSatPtr ezptr;
+ ezSAT &ez = *ezptr.get();
+
+ SigMap sigmap(module);
+ SatGen satgen(&ez, &sigmap);
+
+ FwdCellEdgesDatabase edges_db(sigmap);
+ if (!edges_db.add_edges_from_cell(cell))
+ log_error("Creating edge database failed for this cell!\n");
+
+ dict<SigBit, pool<SigBit>> satgen_db;
+
+ satgen.setContext(&sigmap, "X:");
+ satgen.importCell(cell);
+
+ satgen.setContext(&sigmap, "Y:");
+ satgen.importCell(cell);
+
+ vector<tuple<SigBit, int, int>> input_db, output_db;
+
+ for (auto &conn : cell->connections())
+ {
+ SigSpec bits = sigmap(conn.second);
+
+ satgen.setContext(&sigmap, "X:");
+ std::vector<int> xbits = satgen.importSigSpec(bits);
+
+ satgen.setContext(&sigmap, "Y:");
+ std::vector<int> ybits = satgen.importSigSpec(bits);
+
+ for (int i = 0; i < GetSize(bits); i++)
+ if (cell->input(conn.first))
+ input_db.emplace_back(bits[i], xbits[i], ybits[i]);
+ else
+ output_db.emplace_back(bits[i], xbits[i], ybits[i]);
+ }
+
+ if (verbose)
+ log("\nSAT solving for all edges:\n");
+
+ for (int i = 0; i < GetSize(input_db); i++)
+ {
+ SigBit inbit = std::get<0>(input_db[i]);
+
+ if (verbose)
+ log(" Testing input signal %s:\n", log_signal(inbit));
+
+ vector<int> xinbits, yinbits;
+ for (int k = 0; k < GetSize(input_db); k++)
+ if (k != i) {
+ xinbits.push_back(std::get<1>(input_db[k]));
+ yinbits.push_back(std::get<2>(input_db[k]));
+ }
+
+ int xyinbit_ok = ez.vec_eq(xinbits, yinbits);
+
+ for (int k = 0; k < GetSize(output_db); k++)
+ {
+ SigBit outbit = std::get<0>(output_db[k]);
+ int xoutbit = std::get<1>(output_db[k]);
+ int youtbit = std::get<2>(output_db[k]);
+
+ bool is_edge = ez.solve(xyinbit_ok, ez.XOR(xoutbit, youtbit));
+
+ if (is_edge)
+ satgen_db[inbit].insert(outbit);
+
+ if (verbose) {
+ bool is_ref_edge = edges_db.db.count(inbit) && edges_db.db.at(inbit).count(outbit);
+ log(" %c %s %s\n", is_edge ? 'x' : 'o', log_signal(outbit), is_edge == is_ref_edge ? "OK" : "ERROR");
+ }
+ }
+ }
+
+ if (satgen_db == edges_db.db)
+ log("PASS.\n");
+ else
+ log_error("SAT-based edge table does not match the database!\n");
+}
+
+static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std::string uut_name, std::ofstream &vlog_file)
+{
+ log("Eval testing:%c", verbose ? '\n' : ' ');
+
+ RTLIL::Module *gold_mod = design->module("\\gold");
+ RTLIL::Module *gate_mod = design->module("\\gate");
+ ConstEval gold_ce(gold_mod), gate_ce(gate_mod);
+
+ ezSatPtr ez1, ez2;
+ SigMap sigmap(gold_mod);
+ SatGen satgen1(ez1.get(), &sigmap);
+ SatGen satgen2(ez2.get(), &sigmap);
+ satgen2.model_undef = true;
+
+ if (!nosat)
+ for (auto cell : gold_mod->cells()) {
+ satgen1.importCell(cell);
+ satgen2.importCell(cell);
+ }
+
+ if (vlog_file.is_open())
+ {
+ vlog_file << stringf("\nmodule %s;\n", uut_name.c_str());
+
+ for (auto port : gold_mod->ports) {
+ RTLIL::Wire *wire = gold_mod->wire(port);
+ if (wire->port_input)
+ vlog_file << stringf(" reg [%d:0] %s;\n", GetSize(wire)-1, log_id(wire));
+ else
+ vlog_file << stringf(" wire [%d:0] %s_expr, %s_noexpr;\n", GetSize(wire)-1, log_id(wire), log_id(wire));
+ }
+
+ vlog_file << stringf(" %s_expr uut_expr(", uut_name.c_str());
+ for (int i = 0; i < GetSize(gold_mod->ports); i++)
+ vlog_file << stringf("%s.%s(%s%s)", i ? ", " : "", log_id(gold_mod->ports[i]), log_id(gold_mod->ports[i]),
+ gold_mod->wire(gold_mod->ports[i])->port_input ? "" : "_expr");
+ vlog_file << stringf(");\n");
+
+ vlog_file << stringf(" %s_expr uut_noexpr(", uut_name.c_str());
+ for (int i = 0; i < GetSize(gold_mod->ports); i++)
+ vlog_file << stringf("%s.%s(%s%s)", i ? ", " : "", log_id(gold_mod->ports[i]), log_id(gold_mod->ports[i]),
+ gold_mod->wire(gold_mod->ports[i])->port_input ? "" : "_noexpr");
+ vlog_file << stringf(");\n");
+
+ vlog_file << stringf(" task run;\n");
+ vlog_file << stringf(" begin\n");
+ vlog_file << stringf(" $display(\"%s\");\n", uut_name.c_str());
+ }
+
+ for (int i = 0; i < 64; i++)
+ {
+ log(verbose ? "\n" : ".");
+ gold_ce.clear();
+ gate_ce.clear();
+
+ RTLIL::SigSpec in_sig, in_val;
+ RTLIL::SigSpec out_sig, out_val;
+ std::string vlog_pattern_info;
+
+ for (auto port : gold_mod->ports)
+ {
+ RTLIL::Wire *gold_wire = gold_mod->wire(port);
+ RTLIL::Wire *gate_wire = gate_mod->wire(port);
+
+ log_assert(gold_wire != nullptr);
+ log_assert(gate_wire != nullptr);
+ log_assert(gold_wire->port_input == gate_wire->port_input);
+ log_assert(GetSize(gold_wire) == GetSize(gate_wire));
+
+ if (!gold_wire->port_input)
+ continue;
+
+ RTLIL::Const in_value;
+ for (int i = 0; i < GetSize(gold_wire); i++)
+ in_value.bits.push_back(xorshift32(2) ? RTLIL::S1 : RTLIL::S0);
+
+ if (xorshift32(4) == 0) {
+ int inv_chance = 1 + xorshift32(8);
+ for (int i = 0; i < GetSize(gold_wire); i++)
+ if (xorshift32(inv_chance) == 0)
+ in_value.bits[i] = RTLIL::Sx;
+ }
+
+ if (verbose)
+ log("%s: %s\n", log_id(gold_wire), log_signal(in_value));
+
+ in_sig.append(gold_wire);
+ in_val.append(in_value);
+
+ gold_ce.set(gold_wire, in_value);
+ gate_ce.set(gate_wire, in_value);
+
+ if (vlog_file.is_open() && GetSize(in_value) > 0) {
+ vlog_file << stringf(" %s = 'b%s;\n", log_id(gold_wire), in_value.as_string().c_str());
+ if (!vlog_pattern_info.empty())
+ vlog_pattern_info += " ";
+ vlog_pattern_info += stringf("%s=%s", log_id(gold_wire), log_signal(in_value));
+ }
+ }
+
+ if (vlog_file.is_open())
+ vlog_file << stringf(" #1;\n");
+
+ for (auto port : gold_mod->ports)
+ {
+ RTLIL::Wire *gold_wire = gold_mod->wire(port);
+ RTLIL::Wire *gate_wire = gate_mod->wire(port);
+
+ log_assert(gold_wire != nullptr);
+ log_assert(gate_wire != nullptr);
+ log_assert(gold_wire->port_output == gate_wire->port_output);
+ log_assert(GetSize(gold_wire) == GetSize(gate_wire));
+
+ if (!gold_wire->port_output)
+ continue;
+
+ RTLIL::SigSpec gold_outval(gold_wire);
+ RTLIL::SigSpec gate_outval(gate_wire);
+
+ if (!gold_ce.eval(gold_outval))
+ log_error("Failed to eval %s in gold module.\n", log_id(gold_wire));
+
+ if (!gate_ce.eval(gate_outval))
+ log_error("Failed to eval %s in gate module.\n", log_id(gate_wire));
+
+ bool gold_gate_mismatch = false;
+ for (int i = 0; i < GetSize(gold_wire); i++) {
+ if (gold_outval[i] == RTLIL::Sx)
+ continue;
+ if (gold_outval[i] == gate_outval[i])
+ continue;
+ gold_gate_mismatch = true;
+ break;
+ }
+
+ if (gold_gate_mismatch)
+ log_error("Mismatch in output %s: gold:%s != gate:%s\n", log_id(gate_wire), log_signal(gold_outval), log_signal(gate_outval));
+
+ if (verbose)
+ log("%s: %s\n", log_id(gold_wire), log_signal(gold_outval));
+
+ out_sig.append(gold_wire);
+ out_val.append(gold_outval);
+
+ if (vlog_file.is_open()) {
+ vlog_file << stringf(" $display(\"[%s] %s expected: %%b, expr: %%b, noexpr: %%b\", %d'b%s, %s_expr, %s_noexpr);\n",
+ vlog_pattern_info.c_str(), log_id(gold_wire), GetSize(gold_outval), gold_outval.as_string().c_str(), log_id(gold_wire), log_id(gold_wire));
+ vlog_file << stringf(" if (%s_expr !== %d'b%s) begin $display(\"ERROR\"); $finish; end\n", log_id(gold_wire), GetSize(gold_outval), gold_outval.as_string().c_str());
+ vlog_file << stringf(" if (%s_noexpr !== %d'b%s) begin $display(\"ERROR\"); $finish; end\n", log_id(gold_wire), GetSize(gold_outval), gold_outval.as_string().c_str());
+ }
+ }
+
+ if (verbose)
+ log("EVAL: %s\n", out_val.as_string().c_str());
+
+ if (!nosat)
+ {
+ std::vector<int> sat1_in_sig = satgen1.importSigSpec(in_sig);
+ std::vector<int> sat1_in_val = satgen1.importSigSpec(in_val);
+
+ std::vector<int> sat1_model = satgen1.importSigSpec(out_sig);
+ std::vector<bool> sat1_model_value;
+
+ if (!ez1->solve(sat1_model, sat1_model_value, ez1->vec_eq(sat1_in_sig, sat1_in_val)))
+ log_error("Evaluating sat model 1 (no undef modeling) failed!\n");
+
+ if (verbose) {
+ log("SAT 1: ");
+ for (int i = GetSize(out_sig)-1; i >= 0; i--)
+ log("%c", sat1_model_value.at(i) ? '1' : '0');
+ log("\n");
+ }
+
+ for (int i = 0; i < GetSize(out_sig); i++) {
+ if (out_val[i] != RTLIL::S0 && out_val[i] != RTLIL::S1)
+ continue;
+ if (out_val[i] == RTLIL::S0 && sat1_model_value.at(i) == false)
+ continue;
+ if (out_val[i] == RTLIL::S1 && sat1_model_value.at(i) == true)
+ continue;
+ log_error("Mismatch in sat model 1 (no undef modeling) output!\n");
+ }
+
+ std::vector<int> sat2_in_def_sig = satgen2.importDefSigSpec(in_sig);
+ std::vector<int> sat2_in_def_val = satgen2.importDefSigSpec(in_val);
+
+ std::vector<int> sat2_in_undef_sig = satgen2.importUndefSigSpec(in_sig);
+ std::vector<int> sat2_in_undef_val = satgen2.importUndefSigSpec(in_val);
+
+ std::vector<int> sat2_model_def_sig = satgen2.importDefSigSpec(out_sig);
+ std::vector<int> sat2_model_undef_sig = satgen2.importUndefSigSpec(out_sig);
+
+ std::vector<int> sat2_model;
+ sat2_model.insert(sat2_model.end(), sat2_model_def_sig.begin(), sat2_model_def_sig.end());
+ sat2_model.insert(sat2_model.end(), sat2_model_undef_sig.begin(), sat2_model_undef_sig.end());
+
+ std::vector<bool> sat2_model_value;
+
+ if (!ez2->solve(sat2_model, sat2_model_value, ez2->vec_eq(sat2_in_def_sig, sat2_in_def_val), ez2->vec_eq(sat2_in_undef_sig, sat2_in_undef_val)))
+ log_error("Evaluating sat model 2 (undef modeling) failed!\n");
+
+ if (verbose) {
+ log("SAT 2: ");
+ for (int i = GetSize(out_sig)-1; i >= 0; i--)
+ log("%c", sat2_model_value.at(GetSize(out_sig) + i) ? 'x' : sat2_model_value.at(i) ? '1' : '0');
+ log("\n");
+ }
+
+ for (int i = 0; i < GetSize(out_sig); i++) {
+ if (sat2_model_value.at(GetSize(out_sig) + i)) {
+ if (out_val[i] != RTLIL::S0 && out_val[i] != RTLIL::S1)
+ continue;
+ } else {
+ if (out_val[i] == RTLIL::S0 && sat2_model_value.at(i) == false)
+ continue;
+ if (out_val[i] == RTLIL::S1 && sat2_model_value.at(i) == true)
+ continue;
+ }
+ log_error("Mismatch in sat model 2 (undef modeling) output!\n");
+ }
+ }
+ }
+
+ if (vlog_file.is_open()) {
+ vlog_file << stringf(" end\n");
+ vlog_file << stringf(" endtask\n");
+ vlog_file << stringf("endmodule\n");
+ }
+
+ if (!verbose)
+ log(" ok.\n");
+}
+
+struct TestCellPass : public Pass {
+ TestCellPass() : Pass("test_cell", "automatically test the implementation of a cell type") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" test_cell [options] {cell-types}\n");
+ log("\n");
+ log("Tests the internal implementation of the given cell type (for example '$add')\n");
+ log("by comparing SAT solver, EVAL and TECHMAP implementations of the cell types..\n");
+ log("\n");
+ log("Run with 'all' instead of a cell type to run the test on all supported\n");
+ log("cell types. Use for example 'all /$add' for all cell types except $add.\n");
+ log("\n");
+ log(" -n {integer}\n");
+ log(" create this number of cell instances and test them (default = 100).\n");
+ log("\n");
+ log(" -s {positive_integer}\n");
+ log(" use this value as rng seed value (default = unix time).\n");
+ log("\n");
+ log(" -f {ilang_file}\n");
+ log(" don't generate circuits. instead load the specified ilang file.\n");
+ log("\n");
+ log(" -w {filename_prefix}\n");
+ log(" don't test anything. just generate the circuits and write them\n");
+ log(" to ilang files with the specified prefix\n");
+ log("\n");
+ log(" -map {filename}\n");
+ log(" pass this option to techmap.\n");
+ log("\n");
+ log(" -simlib\n");
+ log(" use \"techmap -D SIMLIB_NOCHECKS -map +/simlib.v -max_iter 2 -autoproc\"\n");
+ log("\n");
+ log(" -aigmap\n");
+ log(" instead of calling \"techmap\", call \"aigmap\"\n");
+ log("\n");
+ log(" -muxdiv\n");
+ log(" when creating test benches with dividers, create an additional mux\n");
+ log(" to mask out the division-by-zero case\n");
+ log("\n");
+ log(" -script {script_file}\n");
+ log(" instead of calling \"techmap\", call \"script {script_file}\".\n");
+ log("\n");
+ log(" -const\n");
+ log(" set some input bits to random constant values\n");
+ log("\n");
+ log(" -nosat\n");
+ log(" do not check SAT model or run SAT equivalence checking\n");
+ log("\n");
+ log(" -noeval\n");
+ log(" do not check const-eval models\n");
+ log("\n");
+ log(" -edges\n");
+ log(" test cell edges db creator against sat-based implementation\n");
+ log("\n");
+ log(" -v\n");
+ log(" print additional debug information to the console\n");
+ log("\n");
+ log(" -vlog {filename}\n");
+ log(" create a Verilog test bench to test simlib and write_verilog\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design*)
+ {
+ int num_iter = 100;
+ std::string techmap_cmd = "techmap -assert";
+ std::string ilang_file, write_prefix;
+ xorshift32_state = 0;
+ std::ofstream vlog_file;
+ bool muxdiv = false;
+ bool verbose = false;
+ bool constmode = false;
+ bool nosat = false;
+ bool noeval = false;
+ bool edges = false;
+
+ int argidx;
+ for (argidx = 1; argidx < GetSize(args); argidx++)
+ {
+ if (args[argidx] == "-n" && argidx+1 < GetSize(args)) {
+ num_iter = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-s" && argidx+1 < GetSize(args)) {
+ xorshift32_state = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-map" && argidx+1 < GetSize(args)) {
+ techmap_cmd += " -map " + args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-f" && argidx+1 < GetSize(args)) {
+ ilang_file = args[++argidx];
+ num_iter = 1;
+ continue;
+ }
+ if (args[argidx] == "-w" && argidx+1 < GetSize(args)) {
+ write_prefix = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-script" && argidx+1 < GetSize(args)) {
+ techmap_cmd = "script " + args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-simlib") {
+ techmap_cmd = "techmap -D SIMLIB_NOCHECKS -map +/simlib.v -max_iter 2 -autoproc";
+ continue;
+ }
+ if (args[argidx] == "-aigmap") {
+ techmap_cmd = "aigmap";
+ continue;
+ }
+ if (args[argidx] == "-muxdiv") {
+ muxdiv = true;
+ continue;
+ }
+ if (args[argidx] == "-const") {
+ constmode = true;
+ continue;
+ }
+ if (args[argidx] == "-nosat") {
+ nosat = true;
+ continue;
+ }
+ if (args[argidx] == "-noeval") {
+ noeval = true;
+ continue;
+ }
+ if (args[argidx] == "-edges") {
+ edges = true;
+ continue;
+ }
+ if (args[argidx] == "-v") {
+ verbose = true;
+ continue;
+ }
+ if (args[argidx] == "-vlog" && argidx+1 < GetSize(args)) {
+ vlog_file.open(args[++argidx], std::ios_base::trunc);
+ if (!vlog_file.is_open())
+ log_cmd_error("Failed to open output file `%s'.\n", args[argidx].c_str());
+ continue;
+ }
+ break;
+ }
+
+ if (xorshift32_state == 0) {
+ xorshift32_state = time(NULL) & 0x7fffffff;
+ log("Rng seed value: %d\n", int(xorshift32_state));
+ }
+
+ std::map<std::string, std::string> cell_types;
+ std::vector<std::string> selected_cell_types;
+
+ cell_types["$not"] = "ASY";
+ cell_types["$pos"] = "ASY";
+ cell_types["$neg"] = "ASY";
+
+ cell_types["$and"] = "ABSY";
+ cell_types["$or"] = "ABSY";
+ cell_types["$xor"] = "ABSY";
+ cell_types["$xnor"] = "ABSY";
+
+ cell_types["$reduce_and"] = "ASY";
+ cell_types["$reduce_or"] = "ASY";
+ cell_types["$reduce_xor"] = "ASY";
+ cell_types["$reduce_xnor"] = "ASY";
+ cell_types["$reduce_bool"] = "ASY";
+
+ cell_types["$shl"] = "ABshY";
+ cell_types["$shr"] = "ABshY";
+ cell_types["$sshl"] = "ABshY";
+ cell_types["$sshr"] = "ABshY";
+ cell_types["$shift"] = "ABshY";
+ cell_types["$shiftx"] = "ABshY";
+
+ cell_types["$lt"] = "ABSY";
+ cell_types["$le"] = "ABSY";
+ cell_types["$eq"] = "ABSY";
+ cell_types["$ne"] = "ABSY";
+ // cell_types["$eqx"] = "ABSY";
+ // cell_types["$nex"] = "ABSY";
+ cell_types["$ge"] = "ABSY";
+ cell_types["$gt"] = "ABSY";
+
+ cell_types["$add"] = "ABSY";
+ cell_types["$sub"] = "ABSY";
+ cell_types["$mul"] = "ABSY";
+ cell_types["$div"] = "ABSY";
+ cell_types["$mod"] = "ABSY";
+ // cell_types["$pow"] = "ABsY";
+
+ cell_types["$logic_not"] = "ASY";
+ cell_types["$logic_and"] = "ABSY";
+ cell_types["$logic_or"] = "ABSY";
+
+ if (edges) {
+ cell_types["$mux"] = "*";
+ cell_types["$pmux"] = "*";
+ }
+
+ // cell_types["$slice"] = "A";
+ // cell_types["$concat"] = "A";
+ // cell_types["$assert"] = "A";
+ // cell_types["$assume"] = "A";
+
+ cell_types["$lut"] = "*";
+ cell_types["$sop"] = "*";
+ cell_types["$alu"] = "ABSY";
+ cell_types["$lcu"] = "*";
+ cell_types["$macc"] = "*";
+ cell_types["$fa"] = "*";
+
+ for (; argidx < GetSize(args); argidx++)
+ {
+ if (args[argidx].rfind("-", 0) == 0)
+ log_cmd_error("Unexpected option: %s\n", args[argidx].c_str());
+
+ if (args[argidx] == "all") {
+ for (auto &it : cell_types)
+ if (std::count(selected_cell_types.begin(), selected_cell_types.end(), it.first) == 0)
+ selected_cell_types.push_back(it.first);
+ continue;
+ }
+
+ if (args[argidx].substr(0, 1) == "/") {
+ std::vector<std::string> new_selected_cell_types;
+ for (auto it : selected_cell_types)
+ if (it != args[argidx].substr(1))
+ new_selected_cell_types.push_back(it);
+ new_selected_cell_types.swap(selected_cell_types);
+ continue;
+ }
+
+ if (cell_types.count(args[argidx]) == 0) {
+ std::string cell_type_list;
+ int charcount = 100;
+ for (auto &it : cell_types) {
+ if (charcount > 60) {
+ cell_type_list += "\n" + it.first;
+ charcount = 0;
+ } else
+ cell_type_list += " " + it.first;
+ charcount += GetSize(it.first);
+ }
+ log_cmd_error("The cell type `%s' is currently not supported. Try one of these:%s\n",
+ args[argidx].c_str(), cell_type_list.c_str());
+ }
+
+ if (std::count(selected_cell_types.begin(), selected_cell_types.end(), args[argidx]) == 0)
+ selected_cell_types.push_back(args[argidx]);
+ }
+
+ if (!ilang_file.empty()) {
+ if (!selected_cell_types.empty())
+ log_cmd_error("Do not specify any cell types when using -f.\n");
+ selected_cell_types.push_back("ilang");
+ }
+
+ if (selected_cell_types.empty())
+ log_cmd_error("No cell type to test specified.\n");
+
+ std::vector<std::string> uut_names;
+
+ for (auto cell_type : selected_cell_types)
+ for (int i = 0; i < num_iter; i++)
+ {
+ RTLIL::Design *design = new RTLIL::Design;
+ if (cell_type == "ilang")
+ Frontend::frontend_call(design, NULL, std::string(), "ilang " + ilang_file);
+ else
+ create_gold_module(design, cell_type, cell_types.at(cell_type), constmode, muxdiv);
+ if (!write_prefix.empty()) {
+ Pass::call(design, stringf("write_ilang %s_%s_%05d.il", write_prefix.c_str(), cell_type.c_str()+1, i));
+ } else if (edges) {
+ Pass::call(design, "dump gold");
+ run_edges_test(design, verbose);
+ } else {
+ Pass::call(design, stringf("copy gold gate; cd gate; %s; cd ..; opt -fast gate", techmap_cmd.c_str()));
+ if (!nosat)
+ Pass::call(design, "miter -equiv -flatten -make_outputs -ignore_gold_x gold gate miter");
+ if (verbose)
+ Pass::call(design, "dump gate");
+ Pass::call(design, "dump gold");
+ if (!nosat)
+ Pass::call(design, "sat -verify -enable_undef -prove trigger 0 -show-inputs -show-outputs miter");
+ std::string uut_name = stringf("uut_%s_%d", cell_type.substr(1).c_str(), i);
+ if (vlog_file.is_open()) {
+ Pass::call(design, stringf("copy gold %s_expr; select %s_expr", uut_name.c_str(), uut_name.c_str()));
+ Backend::backend_call(design, &vlog_file, "<test_cell -vlog>", "verilog -selected");
+ Pass::call(design, stringf("copy gold %s_noexpr; select %s_noexpr", uut_name.c_str(), uut_name.c_str()));
+ Backend::backend_call(design, &vlog_file, "<test_cell -vlog>", "verilog -selected -noexpr");
+ uut_names.push_back(uut_name);
+ }
+ if (!noeval)
+ run_eval_test(design, verbose, nosat, uut_name, vlog_file);
+ }
+ delete design;
+ }
+
+ if (vlog_file.is_open()) {
+ vlog_file << "\nmodule testbench;\n";
+ for (auto &uut : uut_names)
+ vlog_file << stringf(" %s %s ();\n", uut.c_str(), uut.c_str());
+ vlog_file << " initial begin\n";
+ for (auto &uut : uut_names)
+ vlog_file << " " << uut << ".run;\n";
+ vlog_file << " end\n";
+ vlog_file << "endmodule\n";
+ }
+ }
+} TestCellPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/.gitignore b/techlibs/.gitignore
new file mode 100644
index 00000000..e81e4e4b
--- /dev/null
+++ b/techlibs/.gitignore
@@ -0,0 +1 @@
+blackbox.v
diff --git a/techlibs/common/.gitignore b/techlibs/common/.gitignore
new file mode 100644
index 00000000..0a1e7b68
--- /dev/null
+++ b/techlibs/common/.gitignore
@@ -0,0 +1,2 @@
+simlib_help.inc
+simcells_help.inc
diff --git a/techlibs/common/Makefile.inc b/techlibs/common/Makefile.inc
new file mode 100644
index 00000000..236d6c55
--- /dev/null
+++ b/techlibs/common/Makefile.inc
@@ -0,0 +1,28 @@
+
+ifneq ($(SMALL),1)
+OBJS += techlibs/common/synth.o
+OBJS += techlibs/common/prep.o
+endif
+
+GENFILES += techlibs/common/simlib_help.inc
+GENFILES += techlibs/common/simcells_help.inc
+
+techlibs/common/simlib_help.inc: techlibs/common/cellhelp.py techlibs/common/simlib.v
+ $(Q) mkdir -p techlibs/common
+ $(P) python3 $^ > $@.new
+ $(Q) mv $@.new $@
+
+techlibs/common/simcells_help.inc: techlibs/common/cellhelp.py techlibs/common/simcells.v
+ $(Q) mkdir -p techlibs/common
+ $(P) python3 $^ > $@.new
+ $(Q) mv $@.new $@
+
+kernel/register.o: techlibs/common/simlib_help.inc techlibs/common/simcells_help.inc
+
+$(eval $(call add_share_file,share,techlibs/common/simlib.v))
+$(eval $(call add_share_file,share,techlibs/common/simcells.v))
+$(eval $(call add_share_file,share,techlibs/common/techmap.v))
+$(eval $(call add_share_file,share,techlibs/common/pmux2mux.v))
+$(eval $(call add_share_file,share,techlibs/common/adff2dff.v))
+$(eval $(call add_share_file,share,techlibs/common/cells.lib))
+
diff --git a/techlibs/common/adff2dff.v b/techlibs/common/adff2dff.v
new file mode 100644
index 00000000..86744d41
--- /dev/null
+++ b/techlibs/common/adff2dff.v
@@ -0,0 +1,27 @@
+(* techmap_celltype = "$adff" *)
+module adff2dff (CLK, ARST, D, Q);
+ parameter WIDTH = 1;
+ parameter CLK_POLARITY = 1;
+ parameter ARST_POLARITY = 1;
+ parameter ARST_VALUE = 0;
+
+ input CLK, ARST;
+ input [WIDTH-1:0] D;
+ output reg [WIDTH-1:0] Q;
+ wire reg [WIDTH-1:0] NEXT_Q;
+
+ wire [1023:0] _TECHMAP_DO_ = "proc;;";
+
+ always @*
+ if (ARST == ARST_POLARITY)
+ NEXT_Q <= ARST_VALUE;
+ else
+ NEXT_Q <= D;
+
+ if (CLK_POLARITY)
+ always @(posedge CLK)
+ Q <= NEXT_Q;
+ else
+ always @(negedge CLK)
+ Q <= NEXT_Q;
+endmodule
diff --git a/techlibs/common/cellhelp.py b/techlibs/common/cellhelp.py
new file mode 100644
index 00000000..5c44cb80
--- /dev/null
+++ b/techlibs/common/cellhelp.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python3
+
+import fileinput
+import json
+
+current_help_msg = []
+current_module_code = []
+current_module_name = None
+current_module_signature = None
+
+def print_current_cell():
+ print("cell_help[\"%s\"] = %s;" % (current_module_name, "\n".join([json.dumps(line) for line in current_help_msg])))
+ print("cell_code[\"%s+\"] = %s;" % (current_module_name, "\n".join([json.dumps(line) for line in current_module_code])))
+
+for line in fileinput.input():
+ if line.startswith("//-"):
+ current_help_msg.append(line[4:] if len(line) > 4 else "\n")
+ if line.startswith("module "):
+ current_module_name = line.split()[1].strip("\\")
+ current_module_signature = " ".join(line.replace("\\", "").replace(";", "").split()[1:])
+ current_module_code = []
+ elif not line.startswith("endmodule"):
+ line = " " + line
+ current_module_code.append(line.replace("\t", " "))
+ if line.startswith("endmodule"):
+ if len(current_help_msg) == 0:
+ current_help_msg.append("\n")
+ current_help_msg.append(" %s\n" % current_module_signature)
+ current_help_msg.append("\n")
+ current_help_msg.append("No help message for this cell type found.\n")
+ current_help_msg.append("\n")
+ print_current_cell()
+ current_help_msg = []
+
diff --git a/techlibs/common/cells.lib b/techlibs/common/cells.lib
new file mode 100644
index 00000000..eb89036d
--- /dev/null
+++ b/techlibs/common/cells.lib
@@ -0,0 +1,108 @@
+library(yosys_cells) {
+ cell(DFF_N) {
+ ff(IQ, IQN) {
+ clocked_on: "!C";
+ next_state: "D";
+ }
+ pin(D) { direction: input; }
+ pin(C) { direction: input; clock: true; }
+ pin(Q) { direction: output; function: "IQ"; }
+ }
+ cell(DFF_P) {
+ ff(IQ, IQN) {
+ clocked_on: "C";
+ next_state: "D";
+ }
+ pin(D) { direction: input; }
+ pin(C) { direction: input; clock: true; }
+ pin(Q) { direction: output; function: "IQ"; }
+ }
+ cell(DFF_NN0) {
+ ff(IQ, IQN) {
+ clocked_on: "!C";
+ next_state: "D";
+ clear: "!R";
+ }
+ pin(D) { direction: input; }
+ pin(R) { direction: input; }
+ pin(C) { direction: input; clock: true; }
+ pin(Q) { direction: output; function: "IQ"; }
+ }
+ cell(DFF_NN1) {
+ ff(IQ, IQN) {
+ clocked_on: "!C";
+ next_state: "D";
+ preset: "!R";
+ }
+ pin(D) { direction: input; }
+ pin(R) { direction: input; }
+ pin(C) { direction: input; clock: true; }
+ pin(Q) { direction: output; function: "IQ"; }
+ }
+ cell(DFF_NP0) {
+ ff(IQ, IQN) {
+ clocked_on: "!C";
+ next_state: "D";
+ clear: "R";
+ }
+ pin(D) { direction: input; }
+ pin(R) { direction: input; }
+ pin(C) { direction: input; clock: true; }
+ pin(Q) { direction: output; function: "IQ"; }
+ }
+ cell(DFF_NP1) {
+ ff(IQ, IQN) {
+ clocked_on: "!C";
+ next_state: "D";
+ preset: "R";
+ }
+ pin(D) { direction: input; }
+ pin(R) { direction: input; }
+ pin(C) { direction: input; clock: true; }
+ pin(Q) { direction: output; function: "IQ"; }
+ }
+ cell(DFF_PN0) {
+ ff(IQ, IQN) {
+ clocked_on: "C";
+ next_state: "D";
+ clear: "!R";
+ }
+ pin(D) { direction: input; }
+ pin(R) { direction: input; }
+ pin(C) { direction: input; clock: true; }
+ pin(Q) { direction: output; function: "IQ"; }
+ }
+ cell(DFF_PN1) {
+ ff(IQ, IQN) {
+ clocked_on: "C";
+ next_state: "D";
+ preset: "!R";
+ }
+ pin(D) { direction: input; }
+ pin(R) { direction: input; }
+ pin(C) { direction: input; clock: true; }
+ pin(Q) { direction: output; function: "IQ"; }
+ }
+ cell(DFF_PP0) {
+ ff(IQ, IQN) {
+ clocked_on: "C";
+ next_state: "D";
+ clear: "R";
+ }
+ pin(D) { direction: input; }
+ pin(R) { direction: input; }
+ pin(C) { direction: input; clock: true; }
+ pin(Q) { direction: output; function: "IQ"; }
+ }
+ cell(DFF_PP1) {
+ ff(IQ, IQN) {
+ clocked_on: "C";
+ next_state: "D";
+ preset: "R";
+ }
+ pin(D) { direction: input; }
+ pin(R) { direction: input; }
+ pin(C) { direction: input; clock: true; }
+ pin(Q) { direction: output; function: "IQ"; }
+ }
+}
diff --git a/techlibs/common/pmux2mux.v b/techlibs/common/pmux2mux.v
new file mode 100644
index 00000000..9c97245a
--- /dev/null
+++ b/techlibs/common/pmux2mux.v
@@ -0,0 +1,21 @@
+module \$pmux (A, B, S, Y);
+
+wire [1023:0] _TECHMAP_DO_ = "proc; clean";
+
+parameter WIDTH = 1;
+parameter S_WIDTH = 1;
+
+input [WIDTH-1:0] A;
+input [WIDTH*S_WIDTH-1:0] B;
+input [S_WIDTH-1:0] S;
+output reg [WIDTH-1:0] Y;
+
+integer i;
+
+always @* begin
+ Y <= A;
+ for (i = 0; i < S_WIDTH; i=i+1)
+ if (S[i]) Y <= B[WIDTH*i +: WIDTH];
+end
+
+endmodule
diff --git a/techlibs/common/prep.cc b/techlibs/common/prep.cc
new file mode 100644
index 00000000..71534983
--- /dev/null
+++ b/techlibs/common/prep.cc
@@ -0,0 +1,216 @@
+/*
+ * 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/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct PrepPass : public ScriptPass
+{
+ PrepPass() : ScriptPass("prep", "generic synthesis script") { }
+
+ virtual void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" prep [options]\n");
+ log("\n");
+ log("This command runs a conservative RTL synthesis. A typical application for this\n");
+ log("is the preparation stage of a verification flow. This command does not operate\n");
+ log("on partly selected designs.\n");
+ log("\n");
+ log(" -top <module>\n");
+ log(" use the specified module as top module (default='top')\n");
+ log("\n");
+ log(" -auto-top\n");
+ log(" automatically determine the top of the design hierarchy\n");
+ log("\n");
+ log(" -flatten\n");
+ log(" flatten the design before synthesis. this will pass '-auto-top' to\n");
+ log(" 'hierarchy' if no top module is specified.\n");
+ log("\n");
+ log(" -ifx\n");
+ log(" passed to 'proc'. uses verilog simulation behavior for verilog if/case\n");
+ log(" undef handling. this also prevents 'wreduce' from being run.\n");
+ log("\n");
+ log(" -memx\n");
+ log(" simulate verilog simulation behavior for out-of-bounds memory accesses\n");
+ log(" using the 'memory_memx' pass. This option implies -nordff.\n");
+ log("\n");
+ log(" -nomem\n");
+ log(" do not run any of the memory_* passes\n");
+ log("\n");
+ log(" -nordff\n");
+ log(" passed to 'memory_dff'. prohibits merging of FFs into memory read ports\n");
+ log("\n");
+ log(" -nokeepdc\n");
+ log(" do not call opt_* with -keepdc\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 'begin', and empty to label is\n");
+ log(" synonymous to the end of the command list.\n");
+ log("\n");
+ log("\n");
+ log("The following commands are executed by this synthesis command:\n");
+ help_script();
+ log("\n");
+ }
+
+ string top_module, fsm_opts, memory_opts;
+ bool autotop, flatten, ifxmode, memxmode, nomemmode, nokeepdc;
+
+ virtual void clear_flags() YS_OVERRIDE
+ {
+ top_module.clear();
+ memory_opts.clear();
+
+ autotop = false;
+ flatten = false;
+ ifxmode = false;
+ memxmode = false;
+ nomemmode = false;
+ nokeepdc = false;
+ }
+
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ string run_from, run_to;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-top" && argidx+1 < args.size()) {
+ top_module = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-run" && argidx+1 < args.size()) {
+ size_t pos = args[argidx+1].find(':');
+ if (pos == std::string::npos) {
+ run_from = args[++argidx];
+ run_to = args[argidx];
+ } else {
+ run_from = args[++argidx].substr(0, pos);
+ run_to = args[argidx].substr(pos+1);
+ }
+ continue;
+ }
+ if (args[argidx] == "-auto-top") {
+ autotop = true;
+ continue;
+ }
+ if (args[argidx] == "-flatten") {
+ flatten = true;
+ continue;
+ }
+ if (args[argidx] == "-ifx") {
+ ifxmode = true;
+ continue;
+ }
+ if (args[argidx] == "-memx") {
+ memxmode = true;
+ memory_opts += " -nordff";
+ continue;
+ }
+ if (args[argidx] == "-nomem") {
+ nomemmode = true;
+ continue;
+ }
+ if (args[argidx] == "-nordff") {
+ memory_opts += " -nordff";
+ continue;
+ }
+ if (args[argidx] == "-nokeepdc") {
+ nokeepdc = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!design->full_selection())
+ log_cmd_error("This comannd only operates on fully selected designs!\n");
+
+ log_header(design, "Executing PREP pass.\n");
+ log_push();
+
+ run_script(design, run_from, run_to);
+
+ log_pop();
+ }
+
+ virtual void script() YS_OVERRIDE
+ {
+
+ if (check_label("begin"))
+ {
+ if (help_mode) {
+ run("hierarchy -check [-top <top> | -auto-top]");
+ } else {
+ if (top_module.empty()) {
+ if (flatten || autotop)
+ run("hierarchy -check -auto-top");
+ else
+ run("hierarchy -check");
+ } else
+ run(stringf("hierarchy -check -top %s", top_module.c_str()));
+ }
+ }
+
+ if (check_label("coarse"))
+ {
+ if (help_mode)
+ run("proc [-ifx]");
+ else
+ run(ifxmode ? "proc -ifx" : "proc");
+ if (help_mode || flatten)
+ run("flatten", "(if -flatten)");
+ run(nokeepdc ? "opt_expr" : "opt_expr -keepdc");
+ run("opt_clean");
+ run("check");
+ run(nokeepdc ? "opt" : "opt -keepdc");
+ if (!ifxmode) {
+ if (help_mode)
+ run("wreduce [-memx]");
+ else
+ run(memxmode ? "wreduce -memx" : "wreduce");
+ }
+ if (!nomemmode) {
+ run("memory_dff" + (help_mode ? " [-nordff]" : memory_opts));
+ if (help_mode || memxmode)
+ run("memory_memx", "(if -memx)");
+ run("opt_clean");
+ run("memory_collect");
+ }
+ run(nokeepdc ? "opt -fast" : "opt -keepdc -fast");
+ }
+
+ if (check_label("check"))
+ {
+ run("stat");
+ run("check");
+ }
+ }
+} PrepPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/common/simcells.v b/techlibs/common/simcells.v
new file mode 100644
index 00000000..e770c545
--- /dev/null
+++ b/techlibs/common/simcells.v
@@ -0,0 +1,1276 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * The internal logic cell simulation library.
+ *
+ * This Verilog library contains simple simulation models for the internal
+ * logic cells ($_NOT_ , $_AND_ , ...) that are generated by the default technology
+ * mapper (see "techmap.v" in this directory) and expected by the "abc" pass.
+ *
+ */
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_BUF_ (A, Y)
+//-
+//- A buffer. This cell type is always optimized away by the opt_clean pass.
+//-
+//- Truth table: A | Y
+//- ---+---
+//- 0 | 0
+//- 1 | 1
+//-
+module \$_BUF_ (A, Y);
+input A;
+output Y;
+assign Y = A;
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_NOT_ (A, Y)
+//-
+//- An inverter gate.
+//-
+//- Truth table: A | Y
+//- ---+---
+//- 0 | 1
+//- 1 | 0
+//-
+module \$_NOT_ (A, Y);
+input A;
+output Y;
+assign Y = ~A;
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_AND_ (A, B, Y)
+//-
+//- A 2-input AND gate.
+//-
+//- Truth table: A B | Y
+//- -----+---
+//- 0 0 | 0
+//- 0 1 | 0
+//- 1 0 | 0
+//- 1 1 | 1
+//-
+module \$_AND_ (A, B, Y);
+input A, B;
+output Y;
+assign Y = A & B;
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_NAND_ (A, B, Y)
+//-
+//- A 2-input NAND gate.
+//-
+//- Truth table: A B | Y
+//- -----+---
+//- 0 0 | 1
+//- 0 1 | 1
+//- 1 0 | 1
+//- 1 1 | 0
+//-
+module \$_NAND_ (A, B, Y);
+input A, B;
+output Y;
+assign Y = ~(A & B);
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_OR_ (A, B, Y)
+//-
+//- A 2-input OR gate.
+//-
+//- Truth table: A B | Y
+//- -----+---
+//- 0 0 | 0
+//- 0 1 | 1
+//- 1 0 | 1
+//- 1 1 | 1
+//-
+module \$_OR_ (A, B, Y);
+input A, B;
+output Y;
+assign Y = A | B;
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_NOR_ (A, B, Y)
+//-
+//- A 2-input NOR gate.
+//-
+//- Truth table: A B | Y
+//- -----+---
+//- 0 0 | 1
+//- 0 1 | 0
+//- 1 0 | 0
+//- 1 1 | 0
+//-
+module \$_NOR_ (A, B, Y);
+input A, B;
+output Y;
+assign Y = ~(A | B);
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_XOR_ (A, B, Y)
+//-
+//- A 2-input XOR gate.
+//-
+//- Truth table: A B | Y
+//- -----+---
+//- 0 0 | 0
+//- 0 1 | 1
+//- 1 0 | 1
+//- 1 1 | 0
+//-
+module \$_XOR_ (A, B, Y);
+input A, B;
+output Y;
+assign Y = A ^ B;
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_XNOR_ (A, B, Y)
+//-
+//- A 2-input XNOR gate.
+//-
+//- Truth table: A B | Y
+//- -----+---
+//- 0 0 | 1
+//- 0 1 | 0
+//- 1 0 | 0
+//- 1 1 | 1
+//-
+module \$_XNOR_ (A, B, Y);
+input A, B;
+output Y;
+assign Y = ~(A ^ B);
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_MUX_ (A, B, S, Y)
+//-
+//- A 2-input MUX gate.
+//-
+//- Truth table: A B S | Y
+//- -------+---
+//- a - 0 | a
+//- - b 1 | b
+//-
+module \$_MUX_ (A, B, S, Y);
+input A, B, S;
+output Y;
+assign Y = S ? B : A;
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_MUX4_ (A, B, C, D, S, T, Y)
+//-
+//- A 4-input MUX gate.
+//-
+//- Truth table: A B C D S T | Y
+//- -------------+---
+//- a - - - 0 0 | a
+//- - b - - 1 0 | b
+//- - - c - 0 1 | c
+//- - - - d 1 1 | d
+//-
+module \$_MUX4_ (A, B, C, D, S, T, Y);
+input A, B, C, D, S, T;
+output Y;
+assign Y = T ? (S ? D : C) :
+ (S ? B : A);
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_MUX8_ (A, B, C, D, E, F, G, H, S, T, U, Y)
+//-
+//- An 8-input MUX gate.
+//-
+//- Truth table: A B C D E F G H S T U | Y
+//- -----------------------+---
+//- a - - - - - - - 0 0 0 | a
+//- - b - - - - - - 1 0 0 | b
+//- - - c - - - - - 0 1 0 | c
+//- - - - d - - - - 1 1 0 | d
+//- - - - - e - - - 0 0 1 | e
+//- - - - - - f - - 1 0 1 | f
+//- - - - - - - g - 0 1 1 | g
+//- - - - - - - - h 1 1 1 | h
+//-
+module \$_MUX8_ (A, B, C, D, E, F, G, H, S, T, U, Y);
+input A, B, C, D, E, F, G, H, S, T, U;
+output Y;
+assign Y = U ? T ? (S ? H : G) :
+ (S ? F : E) :
+ T ? (S ? D : C) :
+ (S ? B : A);
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_MUX16_ (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V, Y)
+//-
+//- A 16-input MUX gate.
+//-
+//- Truth table: A B C D E F G H I J K L M N O P S T U V | Y
+//- -----------------------------------------+---
+//- a - - - - - - - - - - - - - - - 0 0 0 0 | a
+//- - b - - - - - - - - - - - - - - 1 0 0 0 | b
+//- - - c - - - - - - - - - - - - - 0 1 0 0 | c
+//- - - - d - - - - - - - - - - - - 1 1 0 0 | d
+//- - - - - e - - - - - - - - - - - 0 0 1 0 | e
+//- - - - - - f - - - - - - - - - - 1 0 1 0 | f
+//- - - - - - - g - - - - - - - - - 0 1 1 0 | g
+//- - - - - - - - h - - - - - - - - 1 1 1 0 | h
+//- - - - - - - - - i - - - - - - - 0 0 0 1 | i
+//- - - - - - - - - - j - - - - - - 1 0 0 1 | j
+//- - - - - - - - - - - k - - - - - 0 1 0 1 | k
+//- - - - - - - - - - - - l - - - - 1 1 0 1 | l
+//- - - - - - - - - - - - - m - - - 0 0 1 1 | m
+//- - - - - - - - - - - - - - n - - 1 0 1 1 | n
+//- - - - - - - - - - - - - - - o - 0 1 1 1 | o
+//- - - - - - - - - - - - - - - - p 1 1 1 1 | p
+//-
+module \$_MUX16_ (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V, Y);
+input A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V;
+output Y;
+assign Y = V ? U ? T ? (S ? P : O) :
+ (S ? N : M) :
+ T ? (S ? L : K) :
+ (S ? J : I) :
+ U ? T ? (S ? H : G) :
+ (S ? F : E) :
+ T ? (S ? D : C) :
+ (S ? B : A);
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_AOI3_ (A, B, C, Y)
+//-
+//- A 3-input And-Or-Invert gate.
+//-
+//- Truth table: A B C | Y
+//- -------+---
+//- 0 0 0 | 1
+//- 0 0 1 | 0
+//- 0 1 0 | 1
+//- 0 1 1 | 0
+//- 1 0 0 | 1
+//- 1 0 1 | 0
+//- 1 1 0 | 0
+//- 1 1 1 | 0
+//-
+module \$_AOI3_ (A, B, C, Y);
+input A, B, C;
+output Y;
+assign Y = ~((A & B) | C);
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_OAI3_ (A, B, C, Y)
+//-
+//- A 3-input Or-And-Invert gate.
+//-
+//- Truth table: A B C | Y
+//- -------+---
+//- 0 0 0 | 1
+//- 0 0 1 | 1
+//- 0 1 0 | 1
+//- 0 1 1 | 0
+//- 1 0 0 | 1
+//- 1 0 1 | 0
+//- 1 1 0 | 1
+//- 1 1 1 | 0
+//-
+module \$_OAI3_ (A, B, C, Y);
+input A, B, C;
+output Y;
+assign Y = ~((A | B) & C);
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_AOI4_ (A, B, C, Y)
+//-
+//- A 4-input And-Or-Invert gate.
+//-
+//- Truth table: A B C D | Y
+//- ---------+---
+//- 0 0 0 0 | 1
+//- 0 0 0 1 | 1
+//- 0 0 1 0 | 1
+//- 0 0 1 1 | 0
+//- 0 1 0 0 | 1
+//- 0 1 0 1 | 1
+//- 0 1 1 0 | 1
+//- 0 1 1 1 | 0
+//- 1 0 0 0 | 1
+//- 1 0 0 1 | 1
+//- 1 0 1 0 | 1
+//- 1 0 1 1 | 0
+//- 1 1 0 0 | 0
+//- 1 1 0 1 | 0
+//- 1 1 1 0 | 0
+//- 1 1 1 1 | 0
+//-
+module \$_AOI4_ (A, B, C, D, Y);
+input A, B, C, D;
+output Y;
+assign Y = ~((A & B) | (C & D));
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_OAI4_ (A, B, C, Y)
+//-
+//- A 4-input Or-And-Invert gate.
+//-
+//- Truth table: A B C D | Y
+//- ---------+---
+//- 0 0 0 0 | 1
+//- 0 0 0 1 | 1
+//- 0 0 1 0 | 1
+//- 0 0 1 1 | 1
+//- 0 1 0 0 | 1
+//- 0 1 0 1 | 0
+//- 0 1 1 0 | 0
+//- 0 1 1 1 | 0
+//- 1 0 0 0 | 1
+//- 1 0 0 1 | 0
+//- 1 0 1 0 | 0
+//- 1 0 1 1 | 0
+//- 1 1 0 0 | 1
+//- 1 1 0 1 | 0
+//- 1 1 1 0 | 0
+//- 1 1 1 1 | 0
+//-
+module \$_OAI4_ (A, B, C, D, Y);
+input A, B, C, D;
+output Y;
+assign Y = ~((A | B) & (C | D));
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_TBUF_ (A, E, Y)
+//-
+//- A tri-state buffer.
+//-
+//- Truth table: A E | Y
+//- -----+---
+//- a 1 | a
+//- - 0 | z
+//-
+module \$_TBUF_ (A, E, Y);
+input A, E;
+output Y;
+assign Y = E ? A : 1'bz;
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_SR_NN_ (S, R, Q)
+//-
+//- A set-reset latch with negative polarity SET and RESET.
+//-
+//- Truth table: S R | Q
+//- -----+---
+//- 0 0 | x
+//- 0 1 | 1
+//- 1 0 | 0
+//- 1 1 | y
+//-
+module \$_SR_NN_ (S, R, Q);
+input S, R;
+output reg Q;
+always @(negedge S, negedge R) begin
+ if (R == 0)
+ Q <= 0;
+ else if (S == 0)
+ Q <= 1;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_SR_NP_ (S, R, Q)
+//-
+//- A set-reset latch with negative polarity SET and positive polarioty RESET.
+//-
+//- Truth table: S R | Q
+//- -----+---
+//- 0 1 | x
+//- 0 0 | 1
+//- 1 1 | 0
+//- 1 0 | y
+//-
+module \$_SR_NP_ (S, R, Q);
+input S, R;
+output reg Q;
+always @(negedge S, posedge R) begin
+ if (R == 1)
+ Q <= 0;
+ else if (S == 0)
+ Q <= 1;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_SR_PN_ (S, R, Q)
+//-
+//- A set-reset latch with positive polarity SET and negative polarioty RESET.
+//-
+//- Truth table: S R | Q
+//- -----+---
+//- 1 0 | x
+//- 1 1 | 1
+//- 0 0 | 0
+//- 0 1 | y
+//-
+module \$_SR_PN_ (S, R, Q);
+input S, R;
+output reg Q;
+always @(posedge S, negedge R) begin
+ if (R == 0)
+ Q <= 0;
+ else if (S == 1)
+ Q <= 1;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_SR_PP_ (S, R, Q)
+//-
+//- A set-reset latch with positive polarity SET and RESET.
+//-
+//- Truth table: S R | Q
+//- -----+---
+//- 1 1 | x
+//- 1 0 | 1
+//- 0 1 | 0
+//- 0 0 | y
+//-
+module \$_SR_PP_ (S, R, Q);
+input S, R;
+output reg Q;
+always @(posedge S, posedge R) begin
+ if (R == 1)
+ Q <= 0;
+ else if (S == 1)
+ Q <= 1;
+end
+endmodule
+
+`ifdef SIMCELLS_FF
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_FF_ (D, Q)
+//-
+//- A D-type flip-flop that is clocked from the implicit global clock. (This cell
+//- type is usually only used in netlists for formal verification.)
+//-
+module \$_FF_ (D, Q);
+input D;
+output reg Q;
+always @($global_clock) begin
+ Q <= D;
+end
+endmodule
+`endif
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFF_N_ (D, C, Q)
+//-
+//- A negative edge D-type flip-flop.
+//-
+//- Truth table: D C | Q
+//- -----+---
+//- d \ | d
+//- - - | q
+//-
+module \$_DFF_N_ (D, C, Q);
+input D, C;
+output reg Q;
+always @(negedge C) begin
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFF_P_ (D, C, Q)
+//-
+//- A positive edge D-type flip-flop.
+//-
+//- Truth table: D C | Q
+//- -----+---
+//- d / | d
+//- - - | q
+//-
+module \$_DFF_P_ (D, C, Q);
+input D, C;
+output reg Q;
+always @(posedge C) begin
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFFE_NN_ (D, C, E, Q)
+//-
+//- A negative edge D-type flip-flop with negative polarity enable.
+//-
+//- Truth table: D C E | Q
+//- -------+---
+//- d \ 0 | d
+//- - - - | q
+//-
+module \$_DFFE_NN_ (D, C, E, Q);
+input D, C, E;
+output reg Q;
+always @(negedge C) begin
+ if (!E) Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFFE_NP_ (D, C, E, Q)
+//-
+//- A negative edge D-type flip-flop with positive polarity enable.
+//-
+//- Truth table: D C E | Q
+//- -------+---
+//- d \ 1 | d
+//- - - - | q
+//-
+module \$_DFFE_NP_ (D, C, E, Q);
+input D, C, E;
+output reg Q;
+always @(negedge C) begin
+ if (E) Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFFE_PN_ (D, C, E, Q)
+//-
+//- A positive edge D-type flip-flop with negative polarity enable.
+//-
+//- Truth table: D C E | Q
+//- -------+---
+//- d / 0 | d
+//- - - - | q
+//-
+module \$_DFFE_PN_ (D, C, E, Q);
+input D, C, E;
+output reg Q;
+always @(posedge C) begin
+ if (!E) Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFFE_PP_ (D, C, E, Q)
+//-
+//- A positive edge D-type flip-flop with positive polarity enable.
+//-
+//- Truth table: D C E | Q
+//- -------+---
+//- d / 1 | d
+//- - - - | q
+//-
+module \$_DFFE_PP_ (D, C, E, Q);
+input D, C, E;
+output reg Q;
+always @(posedge C) begin
+ if (E) Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFF_NN0_ (D, C, R, Q)
+//-
+//- A negative edge D-type flip-flop with negative polarity reset.
+//-
+//- Truth table: D C R | Q
+//- -------+---
+//- - - 0 | 0
+//- d \ - | d
+//- - - - | q
+//-
+module \$_DFF_NN0_ (D, C, R, Q);
+input D, C, R;
+output reg Q;
+always @(negedge C or negedge R) begin
+ if (R == 0)
+ Q <= 0;
+ else
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFF_NN1_ (D, C, R, Q)
+//-
+//- A negative edge D-type flip-flop with negative polarity set.
+//-
+//- Truth table: D C R | Q
+//- -------+---
+//- - - 0 | 1
+//- d \ - | d
+//- - - - | q
+//-
+module \$_DFF_NN1_ (D, C, R, Q);
+input D, C, R;
+output reg Q;
+always @(negedge C or negedge R) begin
+ if (R == 0)
+ Q <= 1;
+ else
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFF_NP0_ (D, C, R, Q)
+//-
+//- A negative edge D-type flip-flop with positive polarity reset.
+//-
+//- Truth table: D C R | Q
+//- -------+---
+//- - - 1 | 0
+//- d \ - | d
+//- - - - | q
+//-
+module \$_DFF_NP0_ (D, C, R, Q);
+input D, C, R;
+output reg Q;
+always @(negedge C or posedge R) begin
+ if (R == 1)
+ Q <= 0;
+ else
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFF_NP1_ (D, C, R, Q)
+//-
+//- A negative edge D-type flip-flop with positive polarity set.
+//-
+//- Truth table: D C R | Q
+//- -------+---
+//- - - 1 | 1
+//- d \ - | d
+//- - - - | q
+//-
+module \$_DFF_NP1_ (D, C, R, Q);
+input D, C, R;
+output reg Q;
+always @(negedge C or posedge R) begin
+ if (R == 1)
+ Q <= 1;
+ else
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFF_PN0_ (D, C, R, Q)
+//-
+//- A positive edge D-type flip-flop with negative polarity reset.
+//-
+//- Truth table: D C R | Q
+//- -------+---
+//- - - 0 | 0
+//- d / - | d
+//- - - - | q
+//-
+module \$_DFF_PN0_ (D, C, R, Q);
+input D, C, R;
+output reg Q;
+always @(posedge C or negedge R) begin
+ if (R == 0)
+ Q <= 0;
+ else
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFF_PN1_ (D, C, R, Q)
+//-
+//- A positive edge D-type flip-flop with negative polarity set.
+//-
+//- Truth table: D C R | Q
+//- -------+---
+//- - - 0 | 1
+//- d / - | d
+//- - - - | q
+//-
+module \$_DFF_PN1_ (D, C, R, Q);
+input D, C, R;
+output reg Q;
+always @(posedge C or negedge R) begin
+ if (R == 0)
+ Q <= 1;
+ else
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFF_PP0_ (D, C, R, Q)
+//-
+//- A positive edge D-type flip-flop with positive polarity reset.
+//-
+//- Truth table: D C R | Q
+//- -------+---
+//- - - 1 | 0
+//- d / - | d
+//- - - - | q
+//-
+module \$_DFF_PP0_ (D, C, R, Q);
+input D, C, R;
+output reg Q;
+always @(posedge C or posedge R) begin
+ if (R == 1)
+ Q <= 0;
+ else
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFF_PP1_ (D, C, R, Q)
+//-
+//- A positive edge D-type flip-flop with positive polarity set.
+//-
+//- Truth table: D C R | Q
+//- -------+---
+//- - - 1 | 1
+//- d / - | d
+//- - - - | q
+//-
+module \$_DFF_PP1_ (D, C, R, Q);
+input D, C, R;
+output reg Q;
+always @(posedge C or posedge R) begin
+ if (R == 1)
+ Q <= 1;
+ else
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFFSR_NNN_ (C, S, R, D, Q)
+//-
+//- A negative edge D-type flip-flop with negative polarity set and reset.
+//-
+//- Truth table: C S R D | Q
+//- ---------+---
+//- - - 0 - | 0
+//- - 0 - - | 1
+//- \ - - d | d
+//- - - - - | q
+//-
+module \$_DFFSR_NNN_ (C, S, R, D, Q);
+input C, S, R, D;
+output reg Q;
+always @(negedge C, negedge S, negedge R) begin
+ if (R == 0)
+ Q <= 0;
+ else if (S == 0)
+ Q <= 1;
+ else
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFFSR_NNP_ (C, S, R, D, Q)
+//-
+//- A negative edge D-type flip-flop with negative polarity set and positive
+//- polarity reset.
+//-
+//- Truth table: C S R D | Q
+//- ---------+---
+//- - - 1 - | 0
+//- - 0 - - | 1
+//- \ - - d | d
+//- - - - - | q
+//-
+module \$_DFFSR_NNP_ (C, S, R, D, Q);
+input C, S, R, D;
+output reg Q;
+always @(negedge C, negedge S, posedge R) begin
+ if (R == 1)
+ Q <= 0;
+ else if (S == 0)
+ Q <= 1;
+ else
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFFSR_NPN_ (C, S, R, D, Q)
+//-
+//- A negative edge D-type flip-flop with positive polarity set and negative
+//- polarity reset.
+//-
+//- Truth table: C S R D | Q
+//- ---------+---
+//- - - 0 - | 0
+//- - 1 - - | 1
+//- \ - - d | d
+//- - - - - | q
+//-
+module \$_DFFSR_NPN_ (C, S, R, D, Q);
+input C, S, R, D;
+output reg Q;
+always @(negedge C, posedge S, negedge R) begin
+ if (R == 0)
+ Q <= 0;
+ else if (S == 1)
+ Q <= 1;
+ else
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFFSR_NPP_ (C, S, R, D, Q)
+//-
+//- A negative edge D-type flip-flop with positive polarity set and reset.
+//-
+//- Truth table: C S R D | Q
+//- ---------+---
+//- - - 1 - | 0
+//- - 1 - - | 1
+//- \ - - d | d
+//- - - - - | q
+//-
+module \$_DFFSR_NPP_ (C, S, R, D, Q);
+input C, S, R, D;
+output reg Q;
+always @(negedge C, posedge S, posedge R) begin
+ if (R == 1)
+ Q <= 0;
+ else if (S == 1)
+ Q <= 1;
+ else
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFFSR_PNN_ (C, S, R, D, Q)
+//-
+//- A positive edge D-type flip-flop with negative polarity set and reset.
+//-
+//- Truth table: C S R D | Q
+//- ---------+---
+//- - - 0 - | 0
+//- - 0 - - | 1
+//- / - - d | d
+//- - - - - | q
+//-
+module \$_DFFSR_PNN_ (C, S, R, D, Q);
+input C, S, R, D;
+output reg Q;
+always @(posedge C, negedge S, negedge R) begin
+ if (R == 0)
+ Q <= 0;
+ else if (S == 0)
+ Q <= 1;
+ else
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFFSR_PNP_ (C, S, R, D, Q)
+//-
+//- A positive edge D-type flip-flop with negative polarity set and positive
+//- polarity reset.
+//-
+//- Truth table: C S R D | Q
+//- ---------+---
+//- - - 1 - | 0
+//- - 0 - - | 1
+//- / - - d | d
+//- - - - - | q
+//-
+module \$_DFFSR_PNP_ (C, S, R, D, Q);
+input C, S, R, D;
+output reg Q;
+always @(posedge C, negedge S, posedge R) begin
+ if (R == 1)
+ Q <= 0;
+ else if (S == 0)
+ Q <= 1;
+ else
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFFSR_PPN_ (C, S, R, D, Q)
+//-
+//- A positive edge D-type flip-flop with positive polarity set and negative
+//- polarity reset.
+//-
+//- Truth table: C S R D | Q
+//- ---------+---
+//- - - 0 - | 0
+//- - 1 - - | 1
+//- / - - d | d
+//- - - - - | q
+//-
+module \$_DFFSR_PPN_ (C, S, R, D, Q);
+input C, S, R, D;
+output reg Q;
+always @(posedge C, posedge S, negedge R) begin
+ if (R == 0)
+ Q <= 0;
+ else if (S == 1)
+ Q <= 1;
+ else
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DFFSR_PPP_ (C, S, R, D, Q)
+//-
+//- A positive edge D-type flip-flop with positive polarity set and reset.
+//-
+//- Truth table: C S R D | Q
+//- ---------+---
+//- - - 1 - | 0
+//- - 1 - - | 1
+//- / - - d | d
+//- - - - - | q
+//-
+module \$_DFFSR_PPP_ (C, S, R, D, Q);
+input C, S, R, D;
+output reg Q;
+always @(posedge C, posedge S, posedge R) begin
+ if (R == 1)
+ Q <= 0;
+ else if (S == 1)
+ Q <= 1;
+ else
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DLATCH_N_ (E, D, Q)
+//-
+//- A negative enable D-type latch.
+//-
+//- Truth table: E D | Q
+//- -----+---
+//- 0 d | d
+//- - - | q
+//-
+module \$_DLATCH_N_ (E, D, Q);
+input E, D;
+output reg Q;
+always @* begin
+ if (E == 0)
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DLATCH_P_ (E, D, Q)
+//-
+//- A positive enable D-type latch.
+//-
+//- Truth table: E D | Q
+//- -----+---
+//- 1 d | d
+//- - - | q
+//-
+module \$_DLATCH_P_ (E, D, Q);
+input E, D;
+output reg Q;
+always @* begin
+ if (E == 1)
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DLATCHSR_NNN_ (E, S, R, D, Q)
+//-
+//- A negative enable D-type latch with negative polarity set and reset.
+//-
+//- Truth table: E S R D | Q
+//- ---------+---
+//- - - 0 - | 0
+//- - 0 - - | 1
+//- 0 - - d | d
+//- - - - - | q
+//-
+module \$_DLATCHSR_NNN_ (E, S, R, D, Q);
+input E, S, R, D;
+output reg Q;
+always @* begin
+ if (R == 0)
+ Q <= 0;
+ else if (S == 0)
+ Q <= 1;
+ else if (E == 0)
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DLATCHSR_NNP_ (E, S, R, D, Q)
+//-
+//- A negative enable D-type latch with negative polarity set and positive polarity
+//- reset.
+//-
+//- Truth table: E S R D | Q
+//- ---------+---
+//- - - 1 - | 0
+//- - 0 - - | 1
+//- 0 - - d | d
+//- - - - - | q
+//-
+module \$_DLATCHSR_NNP_ (E, S, R, D, Q);
+input E, S, R, D;
+output reg Q;
+always @* begin
+ if (R == 1)
+ Q <= 0;
+ else if (S == 0)
+ Q <= 1;
+ else if (E == 0)
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DLATCHSR_NPN_ (E, S, R, D, Q)
+//-
+//- A negative enable D-type latch with positive polarity set and negative polarity
+//- reset.
+//-
+//- Truth table: E S R D | Q
+//- ---------+---
+//- - - 0 - | 0
+//- - 1 - - | 1
+//- 0 - - d | d
+//- - - - - | q
+//-
+module \$_DLATCHSR_NPN_ (E, S, R, D, Q);
+input E, S, R, D;
+output reg Q;
+always @* begin
+ if (R == 0)
+ Q <= 0;
+ else if (S == 1)
+ Q <= 1;
+ else if (E == 0)
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DLATCHSR_NPP_ (E, S, R, D, Q)
+//-
+//- A negative enable D-type latch with positive polarity set and reset.
+//-
+//- Truth table: E S R D | Q
+//- ---------+---
+//- - - 1 - | 0
+//- - 1 - - | 1
+//- 0 - - d | d
+//- - - - - | q
+//-
+module \$_DLATCHSR_NPP_ (E, S, R, D, Q);
+input E, S, R, D;
+output reg Q;
+always @* begin
+ if (R == 1)
+ Q <= 0;
+ else if (S == 1)
+ Q <= 1;
+ else if (E == 0)
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DLATCHSR_PNN_ (E, S, R, D, Q)
+//-
+//- A positive enable D-type latch with negative polarity set and reset.
+//-
+//- Truth table: E S R D | Q
+//- ---------+---
+//- - - 0 - | 0
+//- - 0 - - | 1
+//- 1 - - d | d
+//- - - - - | q
+//-
+module \$_DLATCHSR_PNN_ (E, S, R, D, Q);
+input E, S, R, D;
+output reg Q;
+always @* begin
+ if (R == 0)
+ Q <= 0;
+ else if (S == 0)
+ Q <= 1;
+ else if (E == 1)
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DLATCHSR_PNP_ (E, S, R, D, Q)
+//-
+//- A positive enable D-type latch with negative polarity set and positive polarity
+//- reset.
+//-
+//- Truth table: E S R D | Q
+//- ---------+---
+//- - - 1 - | 0
+//- - 0 - - | 1
+//- 1 - - d | d
+//- - - - - | q
+//-
+module \$_DLATCHSR_PNP_ (E, S, R, D, Q);
+input E, S, R, D;
+output reg Q;
+always @* begin
+ if (R == 1)
+ Q <= 0;
+ else if (S == 0)
+ Q <= 1;
+ else if (E == 1)
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DLATCHSR_PPN_ (E, S, R, D, Q)
+//-
+//- A positive enable D-type latch with positive polarity set and negative polarity
+//- reset.
+//-
+//- Truth table: E S R D | Q
+//- ---------+---
+//- - - 0 - | 0
+//- - 1 - - | 1
+//- 1 - - d | d
+//- - - - - | q
+//-
+module \$_DLATCHSR_PPN_ (E, S, R, D, Q);
+input E, S, R, D;
+output reg Q;
+always @* begin
+ if (R == 0)
+ Q <= 0;
+ else if (S == 1)
+ Q <= 1;
+ else if (E == 1)
+ Q <= D;
+end
+endmodule
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $_DLATCHSR_PPP_ (E, S, R, D, Q)
+//-
+//- A positive enable D-type latch with positive polarity set and reset.
+//-
+//- Truth table: E S R D | Q
+//- ---------+---
+//- - - 1 - | 0
+//- - 1 - - | 1
+//- 1 - - d | d
+//- - - - - | q
+//-
+module \$_DLATCHSR_PPP_ (E, S, R, D, Q);
+input E, S, R, D;
+output reg Q;
+always @* begin
+ if (R == 1)
+ Q <= 0;
+ else if (S == 1)
+ Q <= 1;
+ else if (E == 1)
+ Q <= D;
+end
+endmodule
+
diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v
new file mode 100644
index 00000000..2c4db1ac
--- /dev/null
+++ b/techlibs/common/simlib.v
@@ -0,0 +1,1818 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * The Simulation Library.
+ *
+ * This Verilog library contains simple simulation models for the internal
+ * cells ($not, ...) generated by the frontends and used in most passes.
+ *
+ * This library can be used to verify the internal netlists as generated
+ * by the different frontends and passes.
+ *
+ * Note that memory can only be simulated when all $memrd and $memwr cells
+ * have been merged to stand-alone $mem cells (this is what the "memory_collect"
+ * pass is doing).
+ *
+ */
+
+// --------------------------------------------------------
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $not (A, Y)
+//-
+//- A bit-wise inverter. This corresponds to the Verilog unary prefix '~' operator.
+//-
+module \$not (A, Y);
+
+parameter A_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED) begin:BLOCK1
+ assign Y = ~$signed(A);
+ end else begin:BLOCK2
+ assign Y = ~A;
+ end
+endgenerate
+
+endmodule
+
+
+// --------------------------------------------------------
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $pos (A, Y)
+//-
+//- A buffer. This corresponds to the Verilog unary prefix '+' operator.
+//-
+module \$pos (A, Y);
+
+parameter A_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED) begin:BLOCK1
+ assign Y = $signed(A);
+ end else begin:BLOCK2
+ assign Y = A;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $neg (A, Y)
+//-
+//- An arithmetic inverter. This corresponds to the Verilog unary prefix '-' operator.
+//-
+module \$neg (A, Y);
+
+parameter A_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED) begin:BLOCK1
+ assign Y = -$signed(A);
+ end else begin:BLOCK2
+ assign Y = -A;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $and (A, B, Y)
+//-
+//- A bit-wise AND. This corresponds to the Verilog '&' operator.
+//-
+module \$and (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) & $signed(B);
+ end else begin:BLOCK2
+ assign Y = A & B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $or (A, B, Y)
+//-
+//- A bit-wise OR. This corresponds to the Verilog '|' operator.
+//-
+module \$or (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) | $signed(B);
+ end else begin:BLOCK2
+ assign Y = A | B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $xor (A, B, Y)
+//-
+//- A bit-wise XOR. This corresponds to the Verilog '^' operator.
+//-
+module \$xor (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) ^ $signed(B);
+ end else begin:BLOCK2
+ assign Y = A ^ B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $xnor (A, B, Y)
+//-
+//- A bit-wise XNOR. This corresponds to the Verilog '~^' operator.
+//-
+module \$xnor (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) ~^ $signed(B);
+ end else begin:BLOCK2
+ assign Y = A ~^ B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $reduce_and (A, B, Y)
+//-
+//- An AND reduction. This corresponds to the Verilog unary prefix '&' operator.
+//-
+module \$reduce_and (A, Y);
+
+parameter A_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED) begin:BLOCK1
+ assign Y = &$signed(A);
+ end else begin:BLOCK2
+ assign Y = &A;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $reduce_or (A, B, Y)
+//-
+//- An OR reduction. This corresponds to the Verilog unary prefix '|' operator.
+//-
+module \$reduce_or (A, Y);
+
+parameter A_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED) begin:BLOCK1
+ assign Y = |$signed(A);
+ end else begin:BLOCK2
+ assign Y = |A;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $reduce_xor (A, B, Y)
+//-
+//- A XOR reduction. This corresponds to the Verilog unary prefix '^' operator.
+//-
+module \$reduce_xor (A, Y);
+
+parameter A_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED) begin:BLOCK1
+ assign Y = ^$signed(A);
+ end else begin:BLOCK2
+ assign Y = ^A;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $reduce_xnor (A, B, Y)
+//-
+//- A XNOR reduction. This corresponds to the Verilog unary prefix '~^' operator.
+//-
+module \$reduce_xnor (A, Y);
+
+parameter A_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED) begin:BLOCK1
+ assign Y = ~^$signed(A);
+ end else begin:BLOCK2
+ assign Y = ~^A;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $reduce_bool (A, B, Y)
+//-
+//- An OR reduction. This cell type is used instead of $reduce_or when a signal is
+//- implicitly converted to a boolean signal, e.g. for operands of '&&' and '||'.
+//-
+module \$reduce_bool (A, Y);
+
+parameter A_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED) begin:BLOCK1
+ assign Y = !(!$signed(A));
+ end else begin:BLOCK2
+ assign Y = !(!A);
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$shl (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) << B;
+ end else begin:BLOCK2
+ assign Y = A << B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$shr (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) >> B;
+ end else begin:BLOCK2
+ assign Y = A >> B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$sshl (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) <<< B;
+ end else begin:BLOCK2
+ assign Y = A <<< B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$sshr (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) >>> B;
+ end else begin:BLOCK2
+ assign Y = A >>> B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$shift (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (B_SIGNED) begin:BLOCK1
+ assign Y = $signed(B) < 0 ? A << -B : A >> B;
+ end else begin:BLOCK2
+ assign Y = A >> B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$shiftx (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (Y_WIDTH > 0)
+ if (B_SIGNED) begin:BLOCK1
+ assign Y = A[$signed(B) +: Y_WIDTH];
+ end else begin:BLOCK2
+ assign Y = A[B +: Y_WIDTH];
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$fa (A, B, C, X, Y);
+
+parameter WIDTH = 1;
+
+input [WIDTH-1:0] A, B, C;
+output [WIDTH-1:0] X, Y;
+
+wire [WIDTH-1:0] t1, t2, t3;
+
+assign t1 = A ^ B, t2 = A & B, t3 = C & t1;
+assign Y = t1 ^ C, X = (t2 | t3) ^ (Y ^ Y);
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$lcu (P, G, CI, CO);
+
+parameter WIDTH = 1;
+
+input [WIDTH-1:0] P, G;
+input CI;
+
+output reg [WIDTH-1:0] CO;
+
+integer i;
+always @* begin
+ CO = 'bx;
+ if (^{P, G, CI} !== 1'bx) begin
+ CO[0] = G[0] || (P[0] && CI);
+ for (i = 1; i < WIDTH; i = i+1)
+ CO[i] = G[i] || (P[i] && CO[i-1]);
+ end
+end
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$alu (A, B, CI, BI, X, Y, CO);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 1;
+parameter B_WIDTH = 1;
+parameter Y_WIDTH = 1;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] X, Y;
+
+input CI, BI;
+output [Y_WIDTH-1:0] CO;
+
+wire [Y_WIDTH-1:0] AA, BB;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign AA = $signed(A), BB = BI ? ~$signed(B) : $signed(B);
+ end else begin:BLOCK2
+ assign AA = $unsigned(A), BB = BI ? ~$unsigned(B) : $unsigned(B);
+ end
+endgenerate
+
+// this is 'x' if Y and CO should be all 'x', and '0' otherwise
+wire y_co_undef = ^{A, A, B, B, CI, CI, BI, BI};
+
+assign X = AA ^ BB;
+assign Y = (AA + BB + CI) ^ {Y_WIDTH{y_co_undef}};
+
+function get_carry;
+ input a, b, c;
+ get_carry = (a&b) | (a&c) | (b&c);
+endfunction
+
+genvar i;
+generate
+ assign CO[0] = get_carry(AA[0], BB[0], CI) ^ y_co_undef;
+ for (i = 1; i < Y_WIDTH; i = i+1) begin:BLOCK3
+ assign CO[i] = get_carry(AA[i], BB[i], CO[i-1]) ^ y_co_undef;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$lt (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) < $signed(B);
+ end else begin:BLOCK2
+ assign Y = A < B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$le (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) <= $signed(B);
+ end else begin:BLOCK2
+ assign Y = A <= B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$eq (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) == $signed(B);
+ end else begin:BLOCK2
+ assign Y = A == B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$ne (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) != $signed(B);
+ end else begin:BLOCK2
+ assign Y = A != B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$eqx (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) === $signed(B);
+ end else begin:BLOCK2
+ assign Y = A === B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$nex (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) !== $signed(B);
+ end else begin:BLOCK2
+ assign Y = A !== B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$ge (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) >= $signed(B);
+ end else begin:BLOCK2
+ assign Y = A >= B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$gt (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) > $signed(B);
+ end else begin:BLOCK2
+ assign Y = A > B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$add (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) + $signed(B);
+ end else begin:BLOCK2
+ assign Y = A + B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$sub (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) - $signed(B);
+ end else begin:BLOCK2
+ assign Y = A - B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$mul (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) * $signed(B);
+ end else begin:BLOCK2
+ assign Y = A * B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$macc (A, B, Y);
+
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+parameter CONFIG = 4'b0000;
+parameter CONFIG_WIDTH = 4;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output reg [Y_WIDTH-1:0] Y;
+
+// Xilinx XSIM does not like $clog2() below..
+function integer my_clog2;
+ input integer v;
+ begin
+ if (v > 0)
+ v = v - 1;
+ my_clog2 = 0;
+ while (v) begin
+ v = v >> 1;
+ my_clog2 = my_clog2 + 1;
+ end
+ end
+endfunction
+
+localparam integer num_bits = CONFIG[3:0] > 0 ? CONFIG[3:0] : 1;
+localparam integer num_ports = (CONFIG_WIDTH-4) / (2 + 2*num_bits);
+localparam integer num_abits = my_clog2(A_WIDTH) > 0 ? my_clog2(A_WIDTH) : 1;
+
+function [2*num_ports*num_abits-1:0] get_port_offsets;
+ input [CONFIG_WIDTH-1:0] cfg;
+ integer i, cursor;
+ begin
+ cursor = 0;
+ get_port_offsets = 0;
+ for (i = 0; i < num_ports; i = i+1) begin
+ get_port_offsets[(2*i + 0)*num_abits +: num_abits] = cursor;
+ cursor = cursor + cfg[4 + i*(2 + 2*num_bits) + 2 +: num_bits];
+ get_port_offsets[(2*i + 1)*num_abits +: num_abits] = cursor;
+ cursor = cursor + cfg[4 + i*(2 + 2*num_bits) + 2 + num_bits +: num_bits];
+ end
+ end
+endfunction
+
+localparam [2*num_ports*num_abits-1:0] port_offsets = get_port_offsets(CONFIG);
+
+`define PORT_IS_SIGNED (0 + CONFIG[4 + i*(2 + 2*num_bits)])
+`define PORT_DO_SUBTRACT (0 + CONFIG[4 + i*(2 + 2*num_bits) + 1])
+`define PORT_SIZE_A (0 + CONFIG[4 + i*(2 + 2*num_bits) + 2 +: num_bits])
+`define PORT_SIZE_B (0 + CONFIG[4 + i*(2 + 2*num_bits) + 2 + num_bits +: num_bits])
+`define PORT_OFFSET_A (0 + port_offsets[2*i*num_abits +: num_abits])
+`define PORT_OFFSET_B (0 + port_offsets[2*i*num_abits + num_abits +: num_abits])
+
+integer i, j;
+reg [Y_WIDTH-1:0] tmp_a, tmp_b;
+
+always @* begin
+ Y = 0;
+ for (i = 0; i < num_ports; i = i+1)
+ begin
+ tmp_a = 0;
+ tmp_b = 0;
+
+ for (j = 0; j < `PORT_SIZE_A; j = j+1)
+ tmp_a[j] = A[`PORT_OFFSET_A + j];
+
+ if (`PORT_IS_SIGNED && `PORT_SIZE_A > 0)
+ for (j = `PORT_SIZE_A; j < Y_WIDTH; j = j+1)
+ tmp_a[j] = tmp_a[`PORT_SIZE_A-1];
+
+ for (j = 0; j < `PORT_SIZE_B; j = j+1)
+ tmp_b[j] = A[`PORT_OFFSET_B + j];
+
+ if (`PORT_IS_SIGNED && `PORT_SIZE_B > 0)
+ for (j = `PORT_SIZE_B; j < Y_WIDTH; j = j+1)
+ tmp_b[j] = tmp_b[`PORT_SIZE_B-1];
+
+ if (`PORT_SIZE_B > 0)
+ tmp_a = tmp_a * tmp_b;
+
+ if (`PORT_DO_SUBTRACT)
+ Y = Y - tmp_a;
+ else
+ Y = Y + tmp_a;
+ end
+ for (i = 0; i < B_WIDTH; i = i+1) begin
+ Y = Y + B[i];
+ end
+end
+
+`undef PORT_IS_SIGNED
+`undef PORT_DO_SUBTRACT
+`undef PORT_SIZE_A
+`undef PORT_SIZE_B
+`undef PORT_OFFSET_A
+`undef PORT_OFFSET_B
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$div (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) / $signed(B);
+ end else begin:BLOCK2
+ assign Y = A / B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$mod (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) % $signed(B);
+ end else begin:BLOCK2
+ assign Y = A % B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+`ifndef SIMLIB_NOPOW
+
+module \$pow (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) ** $signed(B);
+ end else if (A_SIGNED) begin:BLOCK2
+ assign Y = $signed(A) ** B;
+ end else if (B_SIGNED) begin:BLOCK3
+ assign Y = A ** $signed(B);
+ end else begin:BLOCK4
+ assign Y = A ** B;
+ end
+endgenerate
+
+endmodule
+
+`endif
+// --------------------------------------------------------
+
+module \$logic_not (A, Y);
+
+parameter A_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED) begin:BLOCK1
+ assign Y = !$signed(A);
+ end else begin:BLOCK2
+ assign Y = !A;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$logic_and (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) && $signed(B);
+ end else begin:BLOCK2
+ assign Y = A && B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$logic_or (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ assign Y = $signed(A) || $signed(B);
+ end else begin:BLOCK2
+ assign Y = A || B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$slice (A, Y);
+
+parameter OFFSET = 0;
+parameter A_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+output [Y_WIDTH-1:0] Y;
+
+assign Y = A >> OFFSET;
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$concat (A, B, Y);
+
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [A_WIDTH+B_WIDTH-1:0] Y;
+
+assign Y = {B, A};
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$mux (A, B, S, Y);
+
+parameter WIDTH = 0;
+
+input [WIDTH-1:0] A, B;
+input S;
+output reg [WIDTH-1:0] Y;
+
+always @* begin
+ if (S)
+ Y = B;
+ else
+ Y = A;
+end
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$pmux (A, B, S, Y);
+
+parameter WIDTH = 0;
+parameter S_WIDTH = 0;
+
+input [WIDTH-1:0] A;
+input [WIDTH*S_WIDTH-1:0] B;
+input [S_WIDTH-1:0] S;
+output reg [WIDTH-1:0] Y;
+
+integer i;
+reg found_active_sel_bit;
+
+always @* begin
+ Y = A;
+ found_active_sel_bit = 0;
+ for (i = 0; i < S_WIDTH; i = i+1)
+ if (S[i]) begin
+ Y = found_active_sel_bit ? 'bx : B >> (WIDTH*i);
+ found_active_sel_bit = 1;
+ end
+end
+
+endmodule
+
+// --------------------------------------------------------
+`ifndef SIMLIB_NOLUT
+
+module \$lut (A, Y);
+
+parameter WIDTH = 0;
+parameter LUT = 0;
+
+input [WIDTH-1:0] A;
+output reg Y;
+
+wire lut0_out, lut1_out;
+
+generate
+ if (WIDTH <= 1) begin:simple
+ assign {lut1_out, lut0_out} = LUT;
+ end else begin:complex
+ \$lut #( .WIDTH(WIDTH-1), .LUT(LUT ) ) lut0 ( .A(A[WIDTH-2:0]), .Y(lut0_out) );
+ \$lut #( .WIDTH(WIDTH-1), .LUT(LUT >> (2**(WIDTH-1))) ) lut1 ( .A(A[WIDTH-2:0]), .Y(lut1_out) );
+ end
+
+ if (WIDTH > 0) begin:lutlogic
+ always @* begin
+ casez ({A[WIDTH-1], lut0_out, lut1_out})
+ 3'b?11: Y = 1'b1;
+ 3'b?00: Y = 1'b0;
+ 3'b0??: Y = lut0_out;
+ 3'b1??: Y = lut1_out;
+ default: Y = 1'bx;
+ endcase
+ end
+ end
+endgenerate
+
+endmodule
+
+`endif
+// --------------------------------------------------------
+
+module \$sop (A, Y);
+
+parameter WIDTH = 0;
+parameter DEPTH = 0;
+parameter TABLE = 0;
+
+input [WIDTH-1:0] A;
+output reg Y;
+
+integer i, j;
+reg match;
+
+always @* begin
+ Y = 0;
+ for (i = 0; i < DEPTH; i=i+1) begin
+ match = 1;
+ for (j = 0; j < WIDTH; j=j+1) begin
+ if (TABLE[2*WIDTH*i + 2*j + 0] && A[j]) match = 0;
+ if (TABLE[2*WIDTH*i + 2*j + 1] && !A[j]) match = 0;
+ end
+ if (match) Y = 1;
+ end
+end
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$tribuf (A, EN, Y);
+
+parameter WIDTH = 0;
+
+input [WIDTH-1:0] A;
+input EN;
+output [WIDTH-1:0] Y;
+
+assign Y = EN ? A : 'bz;
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$assert (A, EN);
+
+input A, EN;
+
+`ifndef SIMLIB_NOCHECKS
+always @* begin
+ if (A !== 1'b1 && EN === 1'b1) begin
+ $display("Assertion %m failed!");
+ $stop;
+ end
+end
+`endif
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$assume (A, EN);
+
+input A, EN;
+
+`ifndef SIMLIB_NOCHECKS
+always @* begin
+ if (A !== 1'b1 && EN === 1'b1) begin
+ $display("Assumption %m failed!");
+ $stop;
+ end
+end
+`endif
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$initstate (Y);
+
+output reg Y = 1;
+reg [3:0] cnt = 1;
+reg trig = 0;
+
+initial trig <= 1;
+
+always @(cnt, trig) begin
+ Y <= |cnt;
+ cnt <= cnt + |cnt;
+end
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$anyconst (Y);
+
+parameter WIDTH = 0;
+
+output [WIDTH-1:0] Y;
+
+assign Y = 'bx;
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$anyseq (Y);
+
+parameter WIDTH = 0;
+
+output [WIDTH-1:0] Y;
+
+assign Y = 'bx;
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$equiv (A, B, Y);
+
+input A, B;
+output Y;
+
+assign Y = (A !== 1'bx && A !== B) ? 1'bx : A;
+
+`ifndef SIMLIB_NOCHECKS
+always @* begin
+ if (A !== 1'bx && A !== B) begin
+ $display("Equivalence failed!");
+ $stop;
+ end
+end
+`endif
+
+endmodule
+
+// --------------------------------------------------------
+`ifndef SIMLIB_NOSR
+
+module \$sr (SET, CLR, Q);
+
+parameter WIDTH = 0;
+parameter SET_POLARITY = 1'b1;
+parameter CLR_POLARITY = 1'b1;
+
+input [WIDTH-1:0] SET, CLR;
+output reg [WIDTH-1:0] Q;
+
+wire [WIDTH-1:0] pos_set = SET_POLARITY ? SET : ~SET;
+wire [WIDTH-1:0] pos_clr = CLR_POLARITY ? CLR : ~CLR;
+
+genvar i;
+generate
+ for (i = 0; i < WIDTH; i = i+1) begin:bitslices
+ always @(posedge pos_set[i], posedge pos_clr[i])
+ if (pos_clr[i])
+ Q[i] <= 0;
+ else if (pos_set[i])
+ Q[i] <= 1;
+ end
+endgenerate
+
+endmodule
+
+`endif
+// --------------------------------------------------------
+`ifdef SIMLIB_FF
+
+module \$ff (D, Q);
+
+parameter WIDTH = 0;
+
+input [WIDTH-1:0] D;
+output reg [WIDTH-1:0] Q;
+
+always @($global_clk) begin
+ Q <= D;
+end
+
+endmodule
+
+`endif
+// --------------------------------------------------------
+
+module \$dff (CLK, D, Q);
+
+parameter WIDTH = 0;
+parameter CLK_POLARITY = 1'b1;
+
+input CLK;
+input [WIDTH-1:0] D;
+output reg [WIDTH-1:0] Q;
+wire pos_clk = CLK == CLK_POLARITY;
+
+always @(posedge pos_clk) begin
+ Q <= D;
+end
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$dffe (CLK, EN, D, Q);
+
+parameter WIDTH = 0;
+parameter CLK_POLARITY = 1'b1;
+parameter EN_POLARITY = 1'b1;
+
+input CLK, EN;
+input [WIDTH-1:0] D;
+output reg [WIDTH-1:0] Q;
+wire pos_clk = CLK == CLK_POLARITY;
+
+always @(posedge pos_clk) begin
+ if (EN == EN_POLARITY) Q <= D;
+end
+
+endmodule
+
+// --------------------------------------------------------
+`ifndef SIMLIB_NOSR
+
+module \$dffsr (CLK, SET, CLR, D, Q);
+
+parameter WIDTH = 0;
+parameter CLK_POLARITY = 1'b1;
+parameter SET_POLARITY = 1'b1;
+parameter CLR_POLARITY = 1'b1;
+
+input CLK;
+input [WIDTH-1:0] SET, CLR, D;
+output reg [WIDTH-1:0] Q;
+
+wire pos_clk = CLK == CLK_POLARITY;
+wire [WIDTH-1:0] pos_set = SET_POLARITY ? SET : ~SET;
+wire [WIDTH-1:0] pos_clr = CLR_POLARITY ? CLR : ~CLR;
+
+genvar i;
+generate
+ for (i = 0; i < WIDTH; i = i+1) begin:bitslices
+ always @(posedge pos_set[i], posedge pos_clr[i], posedge pos_clk)
+ if (pos_clr[i])
+ Q[i] <= 0;
+ else if (pos_set[i])
+ Q[i] <= 1;
+ else
+ Q[i] <= D[i];
+ end
+endgenerate
+
+endmodule
+
+`endif
+// --------------------------------------------------------
+
+module \$adff (CLK, ARST, D, Q);
+
+parameter WIDTH = 0;
+parameter CLK_POLARITY = 1'b1;
+parameter ARST_POLARITY = 1'b1;
+parameter ARST_VALUE = 0;
+
+input CLK, ARST;
+input [WIDTH-1:0] D;
+output reg [WIDTH-1:0] Q;
+wire pos_clk = CLK == CLK_POLARITY;
+wire pos_arst = ARST == ARST_POLARITY;
+
+always @(posedge pos_clk, posedge pos_arst) begin
+ if (pos_arst)
+ Q <= ARST_VALUE;
+ else
+ Q <= D;
+end
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$dlatch (EN, D, Q);
+
+parameter WIDTH = 0;
+parameter EN_POLARITY = 1'b1;
+
+input EN;
+input [WIDTH-1:0] D;
+output reg [WIDTH-1:0] Q;
+
+always @* begin
+ if (EN == EN_POLARITY)
+ Q = D;
+end
+
+endmodule
+
+// --------------------------------------------------------
+`ifndef SIMLIB_NOSR
+
+module \$dlatchsr (EN, SET, CLR, D, Q);
+
+parameter WIDTH = 0;
+parameter EN_POLARITY = 1'b1;
+parameter SET_POLARITY = 1'b1;
+parameter CLR_POLARITY = 1'b1;
+
+input EN;
+input [WIDTH-1:0] SET, CLR, D;
+output reg [WIDTH-1:0] Q;
+
+wire pos_en = EN == EN_POLARITY;
+wire [WIDTH-1:0] pos_set = SET_POLARITY ? SET : ~SET;
+wire [WIDTH-1:0] pos_clr = CLR_POLARITY ? CLR : ~CLR;
+
+genvar i;
+generate
+ for (i = 0; i < WIDTH; i = i+1) begin:bitslices
+ always @*
+ if (pos_clr[i])
+ Q[i] = 0;
+ else if (pos_set[i])
+ Q[i] = 1;
+ else if (pos_en)
+ Q[i] = D[i];
+ end
+endgenerate
+
+endmodule
+
+`endif
+// --------------------------------------------------------
+
+module \$fsm (CLK, ARST, CTRL_IN, CTRL_OUT);
+
+parameter NAME = "";
+
+parameter CLK_POLARITY = 1'b1;
+parameter ARST_POLARITY = 1'b1;
+
+parameter CTRL_IN_WIDTH = 1;
+parameter CTRL_OUT_WIDTH = 1;
+
+parameter STATE_BITS = 1;
+parameter STATE_NUM = 1;
+parameter STATE_NUM_LOG2 = 1;
+parameter STATE_RST = 0;
+parameter STATE_TABLE = 1'b0;
+
+parameter TRANS_NUM = 1;
+parameter TRANS_TABLE = 4'b0x0x;
+
+input CLK, ARST;
+input [CTRL_IN_WIDTH-1:0] CTRL_IN;
+output reg [CTRL_OUT_WIDTH-1:0] CTRL_OUT;
+
+wire pos_clk = CLK == CLK_POLARITY;
+wire pos_arst = ARST == ARST_POLARITY;
+
+reg [STATE_BITS-1:0] state;
+reg [STATE_BITS-1:0] state_tmp;
+reg [STATE_BITS-1:0] next_state;
+
+reg [STATE_BITS-1:0] tr_state_in;
+reg [STATE_BITS-1:0] tr_state_out;
+reg [CTRL_IN_WIDTH-1:0] tr_ctrl_in;
+reg [CTRL_OUT_WIDTH-1:0] tr_ctrl_out;
+
+integer i;
+
+task tr_fetch;
+ input [31:0] tr_num;
+ reg [31:0] tr_pos;
+ reg [STATE_NUM_LOG2-1:0] state_num;
+ begin
+ tr_pos = (2*STATE_NUM_LOG2+CTRL_IN_WIDTH+CTRL_OUT_WIDTH)*tr_num;
+ tr_ctrl_out = TRANS_TABLE >> tr_pos;
+ tr_pos = tr_pos + CTRL_OUT_WIDTH;
+ state_num = TRANS_TABLE >> tr_pos;
+ tr_state_out = STATE_TABLE >> (STATE_BITS*state_num);
+ tr_pos = tr_pos + STATE_NUM_LOG2;
+ tr_ctrl_in = TRANS_TABLE >> tr_pos;
+ tr_pos = tr_pos + CTRL_IN_WIDTH;
+ state_num = TRANS_TABLE >> tr_pos;
+ tr_state_in = STATE_TABLE >> (STATE_BITS*state_num);
+ tr_pos = tr_pos + STATE_NUM_LOG2;
+ end
+endtask
+
+always @(posedge pos_clk, posedge pos_arst) begin
+ if (pos_arst) begin
+ state_tmp = STATE_TABLE[STATE_BITS*(STATE_RST+1)-1:STATE_BITS*STATE_RST];
+ for (i = 0; i < STATE_BITS; i = i+1)
+ if (state_tmp[i] === 1'bz)
+ state_tmp[i] = 0;
+ state <= state_tmp;
+ end else begin
+ state_tmp = next_state;
+ for (i = 0; i < STATE_BITS; i = i+1)
+ if (state_tmp[i] === 1'bz)
+ state_tmp[i] = 0;
+ state <= state_tmp;
+ end
+end
+
+always @(state, CTRL_IN) begin
+ next_state <= STATE_TABLE[STATE_BITS*(STATE_RST+1)-1:STATE_BITS*STATE_RST];
+ CTRL_OUT <= 'bx;
+ // $display("---");
+ // $display("Q: %b %b", state, CTRL_IN);
+ for (i = 0; i < TRANS_NUM; i = i+1) begin
+ tr_fetch(i);
+ // $display("T: %b %b -> %b %b [%d]", tr_state_in, tr_ctrl_in, tr_state_out, tr_ctrl_out, i);
+ casez ({state, CTRL_IN})
+ {tr_state_in, tr_ctrl_in}: begin
+ // $display("-> %b %b <- MATCH", state, CTRL_IN);
+ {next_state, CTRL_OUT} <= {tr_state_out, tr_ctrl_out};
+ end
+ endcase
+ end
+end
+
+endmodule
+
+// --------------------------------------------------------
+`ifndef SIMLIB_NOMEM
+
+module \$memrd (CLK, EN, ADDR, DATA);
+
+parameter MEMID = "";
+parameter ABITS = 8;
+parameter WIDTH = 8;
+
+parameter CLK_ENABLE = 0;
+parameter CLK_POLARITY = 0;
+parameter TRANSPARENT = 0;
+
+input CLK, EN;
+input [ABITS-1:0] ADDR;
+output [WIDTH-1:0] DATA;
+
+initial begin
+ if (MEMID != "") begin
+ $display("ERROR: Found non-simulatable instance of $memrd!");
+ $finish;
+ end
+end
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$memwr (CLK, EN, ADDR, DATA);
+
+parameter MEMID = "";
+parameter ABITS = 8;
+parameter WIDTH = 8;
+
+parameter CLK_ENABLE = 0;
+parameter CLK_POLARITY = 0;
+parameter PRIORITY = 0;
+
+input CLK;
+input [WIDTH-1:0] EN;
+input [ABITS-1:0] ADDR;
+input [WIDTH-1:0] DATA;
+
+initial begin
+ if (MEMID != "") begin
+ $display("ERROR: Found non-simulatable instance of $memwr!");
+ $finish;
+ end
+end
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$meminit (ADDR, DATA);
+
+parameter MEMID = "";
+parameter ABITS = 8;
+parameter WIDTH = 8;
+parameter WORDS = 1;
+
+parameter PRIORITY = 0;
+
+input [ABITS-1:0] ADDR;
+input [WORDS*WIDTH-1:0] DATA;
+
+initial begin
+ if (MEMID != "") begin
+ $display("ERROR: Found non-simulatable instance of $meminit!");
+ $finish;
+ end
+end
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$mem (RD_CLK, RD_EN, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA);
+
+parameter MEMID = "";
+parameter signed SIZE = 4;
+parameter signed OFFSET = 0;
+parameter signed ABITS = 2;
+parameter signed WIDTH = 8;
+parameter signed INIT = 1'bx;
+
+parameter signed RD_PORTS = 1;
+parameter RD_CLK_ENABLE = 1'b1;
+parameter RD_CLK_POLARITY = 1'b1;
+parameter RD_TRANSPARENT = 1'b1;
+
+parameter signed WR_PORTS = 1;
+parameter WR_CLK_ENABLE = 1'b1;
+parameter WR_CLK_POLARITY = 1'b1;
+
+input [RD_PORTS-1:0] RD_CLK;
+input [RD_PORTS-1:0] RD_EN;
+input [RD_PORTS*ABITS-1:0] RD_ADDR;
+output reg [RD_PORTS*WIDTH-1:0] RD_DATA;
+
+input [WR_PORTS-1:0] WR_CLK;
+input [WR_PORTS*WIDTH-1:0] WR_EN;
+input [WR_PORTS*ABITS-1:0] WR_ADDR;
+input [WR_PORTS*WIDTH-1:0] WR_DATA;
+
+reg [WIDTH-1:0] memory [SIZE-1:0];
+
+integer i, j;
+reg [WR_PORTS-1:0] LAST_WR_CLK;
+reg [RD_PORTS-1:0] LAST_RD_CLK;
+
+function port_active;
+ input clk_enable;
+ input clk_polarity;
+ input last_clk;
+ input this_clk;
+ begin
+ casez ({clk_enable, clk_polarity, last_clk, this_clk})
+ 4'b0???: port_active = 1;
+ 4'b1101: port_active = 1;
+ 4'b1010: port_active = 1;
+ default: port_active = 0;
+ endcase
+ end
+endfunction
+
+initial begin
+ for (i = 0; i < SIZE; i = i+1)
+ memory[i] = INIT >>> (i*WIDTH);
+end
+
+always @(RD_CLK, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA) begin
+`ifdef SIMLIB_MEMDELAY
+ #`SIMLIB_MEMDELAY;
+`endif
+ for (i = 0; i < RD_PORTS; i = i+1) begin
+ if (!RD_TRANSPARENT[i] && RD_CLK_ENABLE[i] && RD_EN[i] && port_active(RD_CLK_ENABLE[i], RD_CLK_POLARITY[i], LAST_RD_CLK[i], RD_CLK[i])) begin
+ // $display("Read from %s: addr=%b data=%b", MEMID, RD_ADDR[i*ABITS +: ABITS], memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET]);
+ RD_DATA[i*WIDTH +: WIDTH] <= memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET];
+ end
+ end
+
+ for (i = 0; i < WR_PORTS; i = i+1) begin
+ if (port_active(WR_CLK_ENABLE[i], WR_CLK_POLARITY[i], LAST_WR_CLK[i], WR_CLK[i]))
+ for (j = 0; j < WIDTH; j = j+1)
+ if (WR_EN[i*WIDTH+j]) begin
+ // $display("Write to %s: addr=%b data=%b", MEMID, WR_ADDR[i*ABITS +: ABITS], WR_DATA[i*WIDTH+j]);
+ memory[WR_ADDR[i*ABITS +: ABITS] - OFFSET][j] = WR_DATA[i*WIDTH+j];
+ end
+ end
+
+ for (i = 0; i < RD_PORTS; i = i+1) begin
+ if ((RD_TRANSPARENT[i] || !RD_CLK_ENABLE[i]) && port_active(RD_CLK_ENABLE[i], RD_CLK_POLARITY[i], LAST_RD_CLK[i], RD_CLK[i])) begin
+ // $display("Transparent read from %s: addr=%b data=%b", MEMID, RD_ADDR[i*ABITS +: ABITS], memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET]);
+ RD_DATA[i*WIDTH +: WIDTH] <= memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET];
+ end
+ end
+
+ LAST_RD_CLK <= RD_CLK;
+ LAST_WR_CLK <= WR_CLK;
+end
+
+endmodule
+
+`endif
+// --------------------------------------------------------
diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc
new file mode 100644
index 00000000..11ebe533
--- /dev/null
+++ b/techlibs/common/synth.cc
@@ -0,0 +1,223 @@
+/*
+ * 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/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct SynthPass : public ScriptPass
+{
+ SynthPass() : ScriptPass("synth", "generic synthesis script") { }
+
+ virtual void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" synth [options]\n");
+ log("\n");
+ log("This command runs the default synthesis script. This command does not operate\n");
+ log("on partly selected designs.\n");
+ log("\n");
+ log(" -top <module>\n");
+ log(" use the specified module as top module (default='top')\n");
+ log("\n");
+ log(" -auto-top\n");
+ log(" automatically determine the top of the design hierarchy\n");
+ log("\n");
+ log(" -flatten\n");
+ log(" flatten the design before synthesis. this will pass '-auto-top' to\n");
+ log(" 'hierarchy' if no top module is specified.\n");
+ log("\n");
+ log(" -encfile <file>\n");
+ log(" passed to 'fsm_recode' via 'fsm'\n");
+ log("\n");
+ log(" -nofsm\n");
+ log(" do not run FSM optimization\n");
+ log("\n");
+ log(" -noabc\n");
+ log(" do not run abc (as if yosys was compiled without ABC support)\n");
+ log("\n");
+ log(" -noalumacc\n");
+ log(" do not run 'alumacc' pass. i.e. keep arithmetic operators in\n");
+ log(" their direct form ($add, $sub, etc.).\n");
+ log("\n");
+ log(" -nordff\n");
+ log(" passed to 'memory'. prohibits merging of FFs into memory read ports\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 'begin', and empty to label is\n");
+ log(" synonymous to the end of the command list.\n");
+ log("\n");
+ log("\n");
+ log("The following commands are executed by this synthesis command:\n");
+ help_script();
+ log("\n");
+ }
+
+ string top_module, fsm_opts, memory_opts;
+ bool autotop, flatten, noalumacc, nofsm, noabc;
+
+ virtual void clear_flags() YS_OVERRIDE
+ {
+ top_module.clear();
+ fsm_opts.clear();
+ memory_opts.clear();
+
+ autotop = false;
+ flatten = false;
+ noalumacc = false;
+ nofsm = false;
+ noabc = false;
+ }
+
+ virtual 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] == "-top" && argidx+1 < args.size()) {
+ top_module = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-encfile" && argidx+1 < args.size()) {
+ fsm_opts = " -encfile " + args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-run" && argidx+1 < args.size()) {
+ size_t pos = args[argidx+1].find(':');
+ if (pos == std::string::npos) {
+ run_from = args[++argidx];
+ run_to = args[argidx];
+ } else {
+ run_from = args[++argidx].substr(0, pos);
+ run_to = args[argidx].substr(pos+1);
+ }
+ continue;
+ }
+ if (args[argidx] == "-auto-top") {
+ autotop = true;
+ continue;
+ }
+ if (args[argidx] == "-flatten") {
+ flatten = true;
+ continue;
+ }
+ if (args[argidx] == "-nofsm") {
+ nofsm = true;
+ continue;
+ }
+ if (args[argidx] == "-noabc") {
+ noabc = true;
+ continue;
+ }
+ if (args[argidx] == "-noalumacc") {
+ noalumacc = true;
+ continue;
+ }
+ if (args[argidx] == "-nordff") {
+ memory_opts += " -nordff";
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!design->full_selection())
+ log_cmd_error("This comannd only operates on fully selected designs!\n");
+
+ log_header(design, "Executing SYNTH pass.\n");
+ log_push();
+
+ run_script(design, run_from, run_to);
+
+ log_pop();
+ }
+
+ virtual void script() YS_OVERRIDE
+ {
+ if (check_label("begin"))
+ {
+ if (help_mode) {
+ run("hierarchy -check [-top <top> | -auto-top]");
+ } else {
+ if (top_module.empty()) {
+ if (flatten || autotop)
+ run("hierarchy -check -auto-top");
+ else
+ run("hierarchy -check");
+ } else
+ run(stringf("hierarchy -check -top %s", top_module.c_str()));
+ }
+ }
+
+ if (check_label("coarse"))
+ {
+ run("proc");
+ if (help_mode || flatten)
+ run("flatten", "(if -flatten)");
+ run("opt_expr");
+ run("opt_clean");
+ run("check");
+ run("opt");
+ run("wreduce");
+ if (!noalumacc)
+ run("alumacc");
+ run("share");
+ run("opt");
+ if (!nofsm)
+ run("fsm" + fsm_opts);
+ run("opt -fast");
+ run("memory -nomap" + memory_opts);
+ run("opt_clean");
+ }
+
+ if (check_label("fine"))
+ {
+ run("opt -fast -full");
+ run("memory_map");
+ run("opt -full");
+ run("techmap");
+ run("opt -fast");
+
+ if (!noabc) {
+ #ifdef YOSYS_ENABLE_ABC
+ run("abc -fast");
+ run("opt -fast");
+ #endif
+ }
+ }
+
+ if (check_label("check"))
+ {
+ run("hierarchy -check");
+ run("stat");
+ run("check");
+ }
+ }
+} SynthPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/common/techmap.v b/techlibs/common/techmap.v
new file mode 100644
index 00000000..d7ec3947
--- /dev/null
+++ b/techlibs/common/techmap.v
@@ -0,0 +1,459 @@
+/*
+ * 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.
+ *
+ * ---
+ *
+ * The internal logic cell technology mapper.
+ *
+ * This Verilog library contains the mapping of internal cells (e.g. $not with
+ * variable bit width) to the internal logic cells (such as the single bit $_NOT_
+ * gate). Usually this logic network is then mapped to the actual technology
+ * using e.g. the "abc" pass.
+ *
+ * Note that this library does not map $mem cells. They must be mapped to logic
+ * and $dff cells using the "memory_map" pass first. (Or map it to custom cells,
+ * which is of course highly recommended for larger memories.)
+ *
+ */
+
+`define MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b))
+`define MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b))
+
+
+// --------------------------------------------------------
+// Use simplemap for trivial cell types
+// --------------------------------------------------------
+
+(* techmap_simplemap *)
+(* techmap_celltype = "$not $and $or $xor $xnor" *)
+module _90_simplemap_bool_ops;
+endmodule
+
+(* techmap_simplemap *)
+(* techmap_celltype = "$reduce_and $reduce_or $reduce_xor $reduce_xnor $reduce_bool" *)
+module _90_simplemap_reduce_ops;
+endmodule
+
+(* techmap_simplemap *)
+(* techmap_celltype = "$logic_not $logic_and $logic_or" *)
+module _90_simplemap_logic_ops;
+endmodule
+
+(* techmap_simplemap *)
+(* techmap_celltype = "$eq $eqx $ne $nex" *)
+module _90_simplemap_compare_ops;
+endmodule
+
+(* techmap_simplemap *)
+(* techmap_celltype = "$pos $slice $concat $mux $tribuf" *)
+module _90_simplemap_various;
+endmodule
+
+(* techmap_simplemap *)
+(* techmap_celltype = "$sr $ff $dff $dffe $adff $dffsr $dlatch" *)
+module _90_simplemap_registers;
+endmodule
+
+
+// --------------------------------------------------------
+// Shift operators
+// --------------------------------------------------------
+
+(* techmap_celltype = "$shr $shl $sshl $sshr" *)
+module _90_shift_ops_shr_shl_sshl_sshr (A, B, Y);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ parameter _TECHMAP_CELLTYPE_ = "";
+ localparam shift_left = _TECHMAP_CELLTYPE_ == "$shl" || _TECHMAP_CELLTYPE_ == "$sshl";
+ localparam sign_extend = A_SIGNED && _TECHMAP_CELLTYPE_ == "$sshr";
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] Y;
+
+ localparam WIDTH = `MAX(A_WIDTH, Y_WIDTH);
+ localparam BB_WIDTH = `MIN($clog2(shift_left ? Y_WIDTH : A_SIGNED ? WIDTH : A_WIDTH) + 1, B_WIDTH);
+
+ wire [1023:0] _TECHMAP_DO_00_ = "proc;;";
+ wire [1023:0] _TECHMAP_DO_01_ = "RECURSION; CONSTMAP; opt_muxtree; opt_expr -mux_undef -mux_bool -fine;;;";
+
+ integer i;
+ reg [WIDTH-1:0] buffer;
+ reg overflow;
+
+ always @* begin
+ overflow = B_WIDTH > BB_WIDTH ? |B[B_WIDTH-1:BB_WIDTH] : 1'b0;
+ buffer = overflow ? {WIDTH{sign_extend ? A[A_WIDTH-1] : 1'b0}} : {{WIDTH-A_WIDTH{A_SIGNED ? A[A_WIDTH-1] : 1'b0}}, A};
+
+ for (i = 0; i < BB_WIDTH; i = i+1)
+ if (B[i]) begin
+ if (shift_left)
+ buffer = {buffer, (2**i)'b0};
+ else if (2**i < WIDTH)
+ buffer = {{2**i{sign_extend ? buffer[WIDTH-1] : 1'b0}}, buffer[WIDTH-1 : 2**i]};
+ else
+ buffer = {WIDTH{sign_extend ? buffer[WIDTH-1] : 1'b0}};
+ end
+ end
+
+ assign Y = buffer;
+endmodule
+
+(* techmap_celltype = "$shift $shiftx" *)
+module _90_shift_shiftx (A, B, Y);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] Y;
+
+ localparam BB_WIDTH = `MIN($clog2(`MAX(A_WIDTH, Y_WIDTH)) + (B_SIGNED ? 2 : 1), B_WIDTH);
+ localparam WIDTH = `MAX(A_WIDTH, Y_WIDTH) + (B_SIGNED ? 2**(BB_WIDTH-1) : 0);
+
+ parameter _TECHMAP_CELLTYPE_ = "";
+ localparam extbit = _TECHMAP_CELLTYPE_ == "$shift" ? 1'b0 : 1'bx;
+
+ wire [1023:0] _TECHMAP_DO_00_ = "proc;;";
+ wire [1023:0] _TECHMAP_DO_01_ = "CONSTMAP; opt_muxtree; opt_expr -mux_undef -mux_bool -fine;;;";
+
+ integer i;
+ reg [WIDTH-1:0] buffer;
+ reg overflow;
+
+ always @* begin
+ overflow = 0;
+ buffer = {WIDTH{extbit}};
+ buffer[`MAX(A_WIDTH, Y_WIDTH)-1:0] = A;
+
+ if (B_WIDTH > BB_WIDTH) begin
+ if (B_SIGNED) begin
+ for (i = BB_WIDTH; i < B_WIDTH; i = i+1)
+ if (B[i] != B[BB_WIDTH-1])
+ overflow = 1;
+ end else
+ overflow = |B[B_WIDTH-1:BB_WIDTH];
+ if (overflow)
+ buffer = {WIDTH{extbit}};
+ end
+
+ for (i = BB_WIDTH-1; i >= 0; i = i-1)
+ if (B[i]) begin
+ if (B_SIGNED && i == BB_WIDTH-1)
+ buffer = {buffer, {2**i{extbit}}};
+ else if (2**i < WIDTH)
+ buffer = {{2**i{extbit}}, buffer[WIDTH-1 : 2**i]};
+ else
+ buffer = {WIDTH{extbit}};
+ end
+ end
+
+ assign Y = buffer;
+endmodule
+
+
+// --------------------------------------------------------
+// Arithmetic operators
+// --------------------------------------------------------
+
+(* techmap_celltype = "$fa" *)
+module _90_fa (A, B, C, X, Y);
+ parameter WIDTH = 1;
+
+ input [WIDTH-1:0] A, B, C;
+ output [WIDTH-1:0] X, Y;
+
+ wire [WIDTH-1:0] t1, t2, t3;
+
+ assign t1 = A ^ B, t2 = A & B, t3 = C & t1;
+ assign Y = t1 ^ C, X = t2 | t3;
+endmodule
+
+(* techmap_celltype = "$lcu" *)
+module _90_lcu (P, G, CI, CO);
+ parameter WIDTH = 2;
+
+ input [WIDTH-1:0] P, G;
+ input CI;
+
+ output [WIDTH-1:0] CO;
+
+ integer i, j;
+ reg [WIDTH-1:0] p, g;
+
+ wire [1023:0] _TECHMAP_DO_ = "proc; opt -fast";
+
+ always @* begin
+ p = P;
+ g = G;
+
+ // in almost all cases CI will be constant zero
+ g[0] = g[0] | (p[0] & CI);
+
+ // [[CITE]] Brent Kung Adder
+ // R. P. Brent and H. T. Kung, "A Regular Layout for Parallel Adders",
+ // IEEE Transaction on Computers, Vol. C-31, No. 3, p. 260-264, March, 1982
+
+ // Main tree
+ for (i = 1; i <= $clog2(WIDTH); i = i+1) begin
+ for (j = 2**i - 1; j < WIDTH; j = j + 2**i) begin
+ g[j] = g[j] | p[j] & g[j - 2**(i-1)];
+ p[j] = p[j] & p[j - 2**(i-1)];
+ end
+ end
+
+ // Inverse tree
+ for (i = $clog2(WIDTH); i > 0; i = i-1) begin
+ for (j = 2**i + 2**(i-1) - 1; j < WIDTH; j = j + 2**i) begin
+ g[j] = g[j] | p[j] & g[j - 2**(i-1)];
+ p[j] = p[j] & p[j - 2**(i-1)];
+ end
+ end
+ end
+
+ assign CO = g;
+endmodule
+
+(* techmap_celltype = "$alu" *)
+module _90_alu (A, B, CI, BI, X, Y, CO);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] X, Y;
+
+ input CI, BI;
+ output [Y_WIDTH-1:0] CO;
+
+ wire [Y_WIDTH-1:0] A_buf, B_buf;
+ \$pos #(.A_SIGNED(A_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(Y_WIDTH)) A_conv (.A(A), .Y(A_buf));
+ \$pos #(.A_SIGNED(B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) B_conv (.A(B), .Y(B_buf));
+
+ wire [Y_WIDTH-1:0] AA = A_buf;
+ wire [Y_WIDTH-1:0] BB = BI ? ~B_buf : B_buf;
+
+ \$lcu #(.WIDTH(Y_WIDTH)) lcu (.P(X), .G(AA & BB), .CI(CI), .CO(CO));
+
+ assign X = AA ^ BB;
+ assign Y = X ^ {CO, CI};
+endmodule
+
+(* techmap_maccmap *)
+(* techmap_celltype = "$macc" *)
+module _90_macc;
+endmodule
+
+(* techmap_wrap = "alumacc" *)
+(* techmap_celltype = "$lt $le $ge $gt $add $sub $neg $mul" *)
+module _90_alumacc;
+endmodule
+
+
+// --------------------------------------------------------
+// Divide and Modulo
+// --------------------------------------------------------
+
+module \$__div_mod_u (A, B, Y, R);
+ parameter WIDTH = 1;
+
+ input [WIDTH-1:0] A, B;
+ output [WIDTH-1:0] Y, R;
+
+ wire [WIDTH*WIDTH-1:0] chaindata;
+ assign R = chaindata[WIDTH*WIDTH-1:WIDTH*(WIDTH-1)];
+
+ genvar i;
+ generate begin
+ for (i = 0; i < WIDTH; i=i+1) begin:stage
+ wire [WIDTH-1:0] stage_in;
+
+ if (i == 0) begin:cp
+ assign stage_in = A;
+ end else begin:cp
+ assign stage_in = chaindata[i*WIDTH-1:(i-1)*WIDTH];
+ end
+
+ assign Y[WIDTH-(i+1)] = stage_in >= {B, {WIDTH-(i+1){1'b0}}};
+ assign chaindata[(i+1)*WIDTH-1:i*WIDTH] = Y[WIDTH-(i+1)] ? stage_in - {B, {WIDTH-(i+1){1'b0}}} : stage_in;
+ end
+ end endgenerate
+endmodule
+
+module \$__div_mod (A, B, Y, R);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ localparam WIDTH =
+ A_WIDTH >= B_WIDTH && A_WIDTH >= Y_WIDTH ? A_WIDTH :
+ B_WIDTH >= A_WIDTH && B_WIDTH >= Y_WIDTH ? B_WIDTH : Y_WIDTH;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] Y, R;
+
+ wire [WIDTH-1:0] A_buf, B_buf;
+ \$pos #(.A_SIGNED(A_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(WIDTH)) A_conv (.A(A), .Y(A_buf));
+ \$pos #(.A_SIGNED(B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(WIDTH)) B_conv (.A(B), .Y(B_buf));
+
+ wire [WIDTH-1:0] A_buf_u, B_buf_u, Y_u, R_u;
+ assign A_buf_u = A_SIGNED && A_buf[WIDTH-1] ? -A_buf : A_buf;
+ assign B_buf_u = B_SIGNED && B_buf[WIDTH-1] ? -B_buf : B_buf;
+
+ \$__div_mod_u #(
+ .WIDTH(WIDTH)
+ ) div_mod_u (
+ .A(A_buf_u),
+ .B(B_buf_u),
+ .Y(Y_u),
+ .R(R_u)
+ );
+
+ assign Y = A_SIGNED && B_SIGNED && (A_buf[WIDTH-1] != B_buf[WIDTH-1]) ? -Y_u : Y_u;
+ assign R = A_SIGNED && B_SIGNED && A_buf[WIDTH-1] ? -R_u : R_u;
+endmodule
+
+(* techmap_celltype = "$div" *)
+module _90_div (A, B, Y);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] Y;
+
+ \$__div_mod #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(B_WIDTH),
+ .Y_WIDTH(Y_WIDTH)
+ ) div_mod (
+ .A(A),
+ .B(B),
+ .Y(Y)
+ );
+endmodule
+
+(* techmap_celltype = "$mod" *)
+module _90_mod (A, B, Y);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] Y;
+
+ \$__div_mod #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(B_WIDTH),
+ .Y_WIDTH(Y_WIDTH)
+ ) div_mod (
+ .A(A),
+ .B(B),
+ .R(Y)
+ );
+endmodule
+
+
+// --------------------------------------------------------
+// Power
+// --------------------------------------------------------
+
+(* techmap_celltype = "$pow" *)
+module _90_pow (A, B, Y);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] Y;
+
+ wire _TECHMAP_FAIL_ = 1;
+endmodule
+
+
+// --------------------------------------------------------
+// Parallel Multiplexers
+// --------------------------------------------------------
+
+(* techmap_celltype = "$pmux" *)
+module _90_pmux (A, B, S, Y);
+ parameter WIDTH = 1;
+ parameter S_WIDTH = 1;
+
+ input [WIDTH-1:0] A;
+ input [WIDTH*S_WIDTH-1:0] B;
+ input [S_WIDTH-1:0] S;
+ output [WIDTH-1:0] Y;
+
+ wire [WIDTH-1:0] Y_B;
+
+ genvar i, j;
+ generate
+ wire [WIDTH*S_WIDTH-1:0] B_AND_S;
+ for (i = 0; i < S_WIDTH; i = i + 1) begin:B_AND
+ assign B_AND_S[WIDTH*(i+1)-1:WIDTH*i] = B[WIDTH*(i+1)-1:WIDTH*i] & {WIDTH{S[i]}};
+ end:B_AND
+ for (i = 0; i < WIDTH; i = i + 1) begin:B_OR
+ wire [S_WIDTH-1:0] B_AND_BITS;
+ for (j = 0; j < S_WIDTH; j = j + 1) begin:B_AND_BITS_COLLECT
+ assign B_AND_BITS[j] = B_AND_S[WIDTH*j+i];
+ end:B_AND_BITS_COLLECT
+ assign Y_B[i] = |B_AND_BITS;
+ end:B_OR
+ endgenerate
+
+ assign Y = |S ? Y_B : A;
+endmodule
+
+
+// --------------------------------------------------------
+// LUTs
+// --------------------------------------------------------
+
+`ifndef NOLUT
+(* techmap_simplemap *)
+(* techmap_celltype = "$lut $sop" *)
+module _90_lut;
+endmodule
+`endif
+
diff --git a/techlibs/gowin/Makefile.inc b/techlibs/gowin/Makefile.inc
new file mode 100644
index 00000000..679d7eff
--- /dev/null
+++ b/techlibs/gowin/Makefile.inc
@@ -0,0 +1,6 @@
+
+OBJS += techlibs/gowin/synth_gowin.o
+
+$(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_map.v))
+$(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_sim.v))
+
diff --git a/techlibs/gowin/cells_map.v b/techlibs/gowin/cells_map.v
new file mode 100644
index 00000000..e1f85eff
--- /dev/null
+++ b/techlibs/gowin/cells_map.v
@@ -0,0 +1,31 @@
+module \$_DFF_N_ (input D, C, output Q); DFFN _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C)); endmodule
+module \$_DFF_P_ (input D, C, output Q); DFF _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C)); endmodule
+
+module \$lut (A, Y);
+ parameter WIDTH = 0;
+ parameter LUT = 0;
+
+ input [WIDTH-1:0] A;
+ output Y;
+
+ generate
+ if (WIDTH == 1) begin
+ LUT1 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
+ .I0(A[0]));
+ end else
+ if (WIDTH == 2) begin
+ LUT2 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
+ .I0(A[0]), .I1(A[1]));
+ end else
+ if (WIDTH == 3) begin
+ LUT3 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]));
+ end else
+ if (WIDTH == 4) begin
+ LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(A[3]));
+ end else begin
+ wire _TECHMAP_FAIL_ = 1;
+ end
+ endgenerate
+endmodule
diff --git a/techlibs/gowin/cells_sim.v b/techlibs/gowin/cells_sim.v
new file mode 100644
index 00000000..3a09c157
--- /dev/null
+++ b/techlibs/gowin/cells_sim.v
@@ -0,0 +1,51 @@
+module LUT1(output F, input I0);
+ parameter [1:0] INIT = 0;
+ assign F = I0 ? INIT[1] : INIT[0];
+endmodule
+
+module LUT2(output F, input I0, I1);
+ parameter [3:0] INIT = 0;
+ wire [ 1: 0] s1 = I1 ? INIT[ 3: 2] : INIT[ 1: 0];
+ assign F = I0 ? s1[1] : s1[0];
+endmodule
+
+module LUT3(output F, input I0, I1, I2);
+ parameter [7:0] INIT = 0;
+ wire [ 3: 0] s2 = I2 ? INIT[ 7: 4] : INIT[ 3: 0];
+ wire [ 1: 0] s1 = I1 ? s2[ 3: 2] : s2[ 1: 0];
+ assign F = I0 ? s1[1] : s1[0];
+endmodule
+
+module LUT4(output F, input I0, I1, I2, I3);
+ parameter [15:0] INIT = 0;
+ wire [ 7: 0] s3 = I3 ? INIT[15: 8] : INIT[ 7: 0];
+ wire [ 3: 0] s2 = I2 ? s3[ 7: 4] : s3[ 3: 0];
+ wire [ 1: 0] s1 = I1 ? s2[ 3: 2] : s2[ 1: 0];
+ assign F = I0 ? s1[1] : s1[0];
+endmodule
+
+module DFF (output reg Q, input CLK, D);
+ always @(posedge C)
+ Q <= D;
+endmodule
+
+module DFFN (output reg Q, input CLK, D);
+ always @(negedge C)
+ Q <= D;
+endmodule
+
+module VCC(output V);
+ assign V = 1;
+endmodule
+
+module GND(output G);
+ assign G = 0;
+endmodule
+
+module IBUF(output O, input I);
+ assign O = I;
+endmodule
+
+module OBUF(output O, input I);
+ assign O = I;
+endmodule
diff --git a/techlibs/gowin/synth_gowin.cc b/techlibs/gowin/synth_gowin.cc
new file mode 100644
index 00000000..129ab839
--- /dev/null
+++ b/techlibs/gowin/synth_gowin.cc
@@ -0,0 +1,178 @@
+/*
+ * 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/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct SynthGowinPass : public ScriptPass
+{
+ SynthGowinPass() : ScriptPass("synth_gowin", "synthesis for Gowin FPGAs") { }
+
+ virtual void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" synth_gowin [options]\n");
+ log("\n");
+ log("This command runs synthesis for Gowin FPGAs. This work is experimental.\n");
+ log("\n");
+ log(" -top <module>\n");
+ log(" use the specified module as top module (default='top')\n");
+ log("\n");
+ log(" -vout <file>\n");
+ log(" write the design to the specified Verilog netlist file. writing of an\n");
+ log(" output file is omitted if this parameter is not specified.\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 'begin', and empty to label is\n");
+ log(" synonymous to the end of the command list.\n");
+ log("\n");
+ log(" -retime\n");
+ log(" run 'abc' with -dff option\n");
+ log("\n");
+ log("\n");
+ log("The following commands are executed by this synthesis command:\n");
+ help_script();
+ log("\n");
+ }
+
+ string top_opt, vout_file;
+ bool retime;
+
+ virtual void clear_flags() YS_OVERRIDE
+ {
+ top_opt = "-auto-top";
+ vout_file = "";
+ retime = false;
+ }
+
+ virtual 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] == "-top" && argidx+1 < args.size()) {
+ top_opt = "-top " + args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-vout" && argidx+1 < args.size()) {
+ vout_file = args[++argidx];
+ continue;
+ }
+ 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] == "-retime") {
+ retime = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!design->full_selection())
+ log_cmd_error("This comannd only operates on fully selected designs!\n");
+
+ log_header(design, "Executing SYNTH_GOWIN pass.\n");
+ log_push();
+
+ run_script(design, run_from, run_to);
+
+ log_pop();
+ }
+
+ virtual void script() YS_OVERRIDE
+ {
+ if (check_label("begin"))
+ {
+ run("read_verilog -lib +/gowin/cells_sim.v");
+ run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
+ }
+
+ if (check_label("flatten"))
+ {
+ run("proc");
+ run("flatten");
+ run("tribuf -logic");
+ run("deminout");
+ }
+
+ if (check_label("coarse"))
+ {
+ run("synth -run coarse");
+ }
+
+ if (check_label("fine"))
+ {
+ run("opt -fast -mux_undef -undriven -fine");
+ run("memory_map");
+ run("opt -undriven -fine");
+ run("techmap");
+ run("clean -purge");
+ run("splitnets -ports");
+ run("setundef -undriven -zero");
+ if (retime || help_mode)
+ run("abc -dff", "(only if -retime)");
+ }
+
+ if (check_label("map_luts"))
+ {
+ run("abc -lut 4");
+ run("clean");
+ }
+
+ if (check_label("map_cells"))
+ {
+ run("techmap -map +/gowin/cells_map.v");
+ run("hilomap -hicell VCC V -locell GND G");
+ run("iopadmap -inpad IBUF O:I -outpad OBUF I:O");
+ run("clean -purge");
+ }
+
+ if (check_label("check"))
+ {
+ run("hierarchy -check");
+ run("stat");
+ run("check -noinit");
+ }
+
+ if (check_label("vout"))
+ {
+ if (!vout_file.empty() || help_mode)
+ run(stringf("write_verilog -attr2comment -defparam -renameprefix gen %s",
+ help_mode ? "<file-name>" : vout_file.c_str()));
+ }
+ }
+} SynthGowinPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/greenpak4/Makefile.inc b/techlibs/greenpak4/Makefile.inc
new file mode 100644
index 00000000..1c9871e2
--- /dev/null
+++ b/techlibs/greenpak4/Makefile.inc
@@ -0,0 +1,8 @@
+
+OBJS += techlibs/greenpak4/synth_greenpak4.o
+OBJS += techlibs/greenpak4/greenpak4_counters.o
+OBJS += techlibs/greenpak4/greenpak4_dffinv.o
+
+$(eval $(call add_share_file,share/greenpak4,techlibs/greenpak4/cells_map.v))
+$(eval $(call add_share_file,share/greenpak4,techlibs/greenpak4/cells_sim.v))
+$(eval $(call add_share_file,share/greenpak4,techlibs/greenpak4/gp_dff.lib))
diff --git a/techlibs/greenpak4/cells_map.v b/techlibs/greenpak4/cells_map.v
new file mode 100644
index 00000000..111a77a1
--- /dev/null
+++ b/techlibs/greenpak4/cells_map.v
@@ -0,0 +1,94 @@
+module GP_DFFS(input D, CLK, nSET, output reg Q);
+ parameter [0:0] INIT = 1'bx;
+ GP_DFFSR #(
+ .INIT(INIT),
+ .SRMODE(1'b1),
+ ) _TECHMAP_REPLACE_ (
+ .D(D),
+ .CLK(CLK),
+ .nSR(nSET),
+ .Q(Q)
+ );
+endmodule
+
+module GP_DFFR(input D, CLK, nRST, output reg Q);
+ parameter [0:0] INIT = 1'bx;
+ GP_DFFSR #(
+ .INIT(INIT),
+ .SRMODE(1'b0),
+ ) _TECHMAP_REPLACE_ (
+ .D(D),
+ .CLK(CLK),
+ .nSR(nRST),
+ .Q(Q)
+ );
+endmodule
+
+module GP_DFFSI(input D, CLK, nSET, output reg nQ);
+ parameter [0:0] INIT = 1'bx;
+ GP_DFFSRI #(
+ .INIT(INIT),
+ .SRMODE(1'b1),
+ ) _TECHMAP_REPLACE_ (
+ .D(D),
+ .CLK(CLK),
+ .nSR(nSET),
+ .nQ(nQ)
+ );
+endmodule
+
+module GP_DFFRI(input D, CLK, nRST, output reg nQ);
+ parameter [0:0] INIT = 1'bx;
+ GP_DFFSRI #(
+ .INIT(INIT),
+ .SRMODE(1'b0),
+ ) _TECHMAP_REPLACE_ (
+ .D(D),
+ .CLK(CLK),
+ .nSR(nRST),
+ .nQ(nQ)
+ );
+endmodule
+
+module GP_OBUFT(input IN, input OE, output OUT);
+ GP_IOBUF _TECHMAP_REPLACE_ (
+ .IN(IN),
+ .OE(OE),
+ .IO(OUT),
+ .OUT()
+ );
+endmodule
+
+module \$lut (A, Y);
+ parameter WIDTH = 0;
+ parameter LUT = 0;
+
+ input [WIDTH-1:0] A;
+ output Y;
+
+ generate
+ if (WIDTH == 1) begin
+ if(LUT == 2'b01) begin
+ GP_INV _TECHMAP_REPLACE_ (.OUT(Y), .IN(A[0]) );
+ end
+ else begin
+ GP_2LUT #(.INIT({2'b00, LUT})) _TECHMAP_REPLACE_ (.OUT(Y),
+ .IN0(A[0]), .IN1(1'b0));
+ end
+ end else
+ if (WIDTH == 2) begin
+ GP_2LUT #(.INIT(LUT)) _TECHMAP_REPLACE_ (.OUT(Y),
+ .IN0(A[0]), .IN1(A[1]));
+ end else
+ if (WIDTH == 3) begin
+ GP_3LUT #(.INIT(LUT)) _TECHMAP_REPLACE_ (.OUT(Y),
+ .IN0(A[0]), .IN1(A[1]), .IN2(A[2]));
+ end else
+ if (WIDTH == 4) begin
+ GP_4LUT #(.INIT(LUT)) _TECHMAP_REPLACE_ (.OUT(Y),
+ .IN0(A[0]), .IN1(A[1]), .IN2(A[2]), .IN3(A[3]));
+ end else begin
+ wire _TECHMAP_FAIL_ = 1;
+ end
+ endgenerate
+endmodule
diff --git a/techlibs/greenpak4/cells_sim.v b/techlibs/greenpak4/cells_sim.v
new file mode 100644
index 00000000..80746be0
--- /dev/null
+++ b/techlibs/greenpak4/cells_sim.v
@@ -0,0 +1,461 @@
+`timescale 1ns/1ps
+
+module GP_2LUT(input IN0, IN1, output OUT);
+ parameter [3:0] INIT = 0;
+ assign OUT = INIT[{IN1, IN0}];
+endmodule
+
+module GP_3LUT(input IN0, IN1, IN2, output OUT);
+ parameter [7:0] INIT = 0;
+ assign OUT = INIT[{IN2, IN1, IN0}];
+endmodule
+
+module GP_4LUT(input IN0, IN1, IN2, IN3, output OUT);
+ parameter [15:0] INIT = 0;
+ assign OUT = INIT[{IN3, IN2, IN1, IN0}];
+endmodule
+
+module GP_ABUF(input wire IN, output wire OUT);
+
+ assign OUT = IN;
+
+ //cannot simulate mixed signal IP
+
+endmodule
+
+module GP_ACMP(input wire PWREN, input wire VIN, input wire VREF, output reg OUT);
+
+ parameter BANDWIDTH = "HIGH";
+ parameter VIN_ATTEN = 1;
+ parameter VIN_ISRC_EN = 0;
+ parameter HYSTERESIS = 0;
+
+ initial OUT = 0;
+
+ //cannot simulate mixed signal IP
+
+endmodule
+
+module GP_BANDGAP(output reg OK);
+ parameter AUTO_PWRDN = 1;
+ parameter CHOPPER_EN = 1;
+ parameter OUT_DELAY = 100;
+
+ //cannot simulate mixed signal IP
+
+endmodule
+
+module GP_COUNT8(input CLK, input wire RST, output reg OUT);
+
+ parameter RESET_MODE = "RISING";
+
+ parameter COUNT_TO = 8'h1;
+ parameter CLKIN_DIVIDE = 1;
+
+ //more complex hard IP blocks are not supported for simulation yet
+
+ reg[7:0] count = COUNT_TO;
+
+ //Combinatorially output whenever we wrap low
+ always @(*) begin
+ OUT <= (count == 8'h0);
+ end
+
+ //POR or SYSRST reset value is COUNT_TO. Datasheet is unclear but conversations w/ Silego confirm.
+ //Runtime reset value is clearly 0 except in count/FSM cells where it's configurable but we leave at 0 for now.
+ //Datasheet seems to indicate that reset is asynchronous, but for now we model as sync due to Yosys issues...
+ always @(posedge CLK) begin
+
+ count <= count - 1'd1;
+
+ if(count == 0)
+ count <= COUNT_TO;
+
+ /*
+ if((RESET_MODE == "RISING") && RST)
+ count <= 0;
+ if((RESET_MODE == "FALLING") && !RST)
+ count <= 0;
+ if((RESET_MODE == "BOTH") && RST)
+ count <= 0;
+ */
+ end
+
+endmodule
+
+module GP_COUNT14(input CLK, input wire RST, output reg OUT);
+
+ parameter RESET_MODE = "RISING";
+
+ parameter COUNT_TO = 14'h1;
+ parameter CLKIN_DIVIDE = 1;
+
+ //more complex hard IP blocks are not supported for simulation yet
+
+endmodule
+
+module GP_COUNT8_ADV(input CLK, input RST, output reg OUT,
+ input UP, input KEEP);
+
+ parameter RESET_MODE = "RISING";
+ parameter RESET_VALUE = "ZERO";
+
+ parameter COUNT_TO = 8'h1;
+ parameter CLKIN_DIVIDE = 1;
+
+ //more complex hard IP blocks are not supported for simulation yet
+
+endmodule
+
+module GP_COUNT14_ADV(input CLK, input RST, output reg OUT,
+ input UP, input KEEP);
+
+ parameter RESET_MODE = "RISING";
+ parameter RESET_VALUE = "ZERO";
+
+ parameter COUNT_TO = 14'h1;
+ parameter CLKIN_DIVIDE = 1;
+
+ //more complex hard IP blocks are not supported for simulation yet
+
+endmodule
+
+module GP_DAC(input[7:0] DIN, input wire VREF, output reg VOUT);
+
+ initial VOUT = 0;
+
+ //analog hard IP is not supported for simulation
+
+endmodule
+
+module GP_DELAY(input IN, output reg OUT);
+
+ parameter DELAY_STEPS = 1;
+ parameter GLITCH_FILTER = 0;
+
+ initial OUT = 0;
+
+ generate
+
+ //TODO: These delays are PTV dependent! For now, hard code 3v3 timing
+ //Change simulation-mode delay depending on global Vdd range (how to specify this?)
+ always @(*) begin
+ case(DELAY_STEPS)
+ 1: #166 OUT = IN;
+ 2: #318 OUT = IN;
+ 2: #471 OUT = IN;
+ 3: #622 OUT = IN;
+ default: begin
+ $display("ERROR: GP_DELAY must have DELAY_STEPS in range [1,4]");
+ $finish;
+ end
+ endcase
+ end
+
+ endgenerate
+
+endmodule
+
+module GP_DFF(input D, CLK, output reg Q);
+ parameter [0:0] INIT = 1'bx;
+ initial Q = INIT;
+ always @(posedge CLK) begin
+ Q <= D;
+ end
+endmodule
+
+module GP_DFFI(input D, CLK, output reg nQ);
+ parameter [0:0] INIT = 1'bx;
+ initial nQ = INIT;
+ always @(posedge CLK) begin
+ nQ <= ~D;
+ end
+endmodule
+
+module GP_DFFR(input D, CLK, nRST, output reg Q);
+ parameter [0:0] INIT = 1'bx;
+ initial Q = INIT;
+ always @(posedge CLK, negedge nRST) begin
+ if (!nRST)
+ Q <= 1'b0;
+ else
+ Q <= D;
+ end
+endmodule
+
+module GP_DFFRI(input D, CLK, nRST, output reg nQ);
+ parameter [0:0] INIT = 1'bx;
+ initial nQ = INIT;
+ always @(posedge CLK, negedge nRST) begin
+ if (!nRST)
+ nQ <= 1'b1;
+ else
+ nQ <= ~D;
+ end
+endmodule
+
+module GP_DFFS(input D, CLK, nSET, output reg Q);
+ parameter [0:0] INIT = 1'bx;
+ initial Q = INIT;
+ always @(posedge CLK, negedge nSET) begin
+ if (!nSET)
+ Q <= 1'b1;
+ else
+ Q <= D;
+ end
+endmodule
+
+module GP_DFFSI(input D, CLK, nSET, output reg nQ);
+ parameter [0:0] INIT = 1'bx;
+ initial nQ = INIT;
+ always @(posedge CLK, negedge nSET) begin
+ if (!nSET)
+ nQ <= 1'b0;
+ else
+ nQ <= ~D;
+ end
+endmodule
+
+module GP_DFFSR(input D, CLK, nSR, output reg Q);
+ parameter [0:0] INIT = 1'bx;
+ parameter [0:0] SRMODE = 1'bx;
+ initial Q = INIT;
+ always @(posedge CLK, negedge nSR) begin
+ if (!nSR)
+ Q <= SRMODE;
+ else
+ Q <= D;
+ end
+endmodule
+
+module GP_DFFSRI(input D, CLK, nSR, output reg nQ);
+ parameter [0:0] INIT = 1'bx;
+ parameter [0:0] SRMODE = 1'bx;
+ initial nQ = INIT;
+ always @(posedge CLK, negedge nSR) begin
+ if (!nSR)
+ nQ <= ~SRMODE;
+ else
+ nQ <= ~D;
+ end
+endmodule
+
+module GP_EDGEDET(input IN, output reg OUT);
+
+ parameter EDGE_DIRECTION = "RISING";
+ parameter DELAY_STEPS = 1;
+ parameter GLITCH_FILTER = 0;
+
+ //not implemented for simulation
+
+endmodule
+
+module GP_IBUF(input IN, output OUT);
+ assign OUT = IN;
+endmodule
+
+module GP_IOBUF(input IN, input OE, output OUT, inout IO);
+ assign OUT = IO;
+ assign IO = OE ? IN : 1'bz;
+endmodule
+
+module GP_INV(input IN, output OUT);
+ assign OUT = ~IN;
+endmodule
+
+module GP_LFOSC(input PWRDN, output reg CLKOUT);
+
+ parameter PWRDN_EN = 0;
+ parameter AUTO_PWRDN = 0;
+ parameter OUT_DIV = 1;
+
+ initial CLKOUT = 0;
+
+ //auto powerdown not implemented for simulation
+ //output dividers not implemented for simulation
+
+ always begin
+ if(PWRDN)
+ CLKOUT = 0;
+ else begin
+ //half period of 1730 Hz
+ #289017;
+ CLKOUT = ~CLKOUT;
+ end
+ end
+
+endmodule
+
+module GP_OBUF(input IN, output OUT);
+ assign OUT = IN;
+endmodule
+
+module GP_OBUFT(input IN, input OE, output OUT);
+ assign OUT = OE ? IN : 1'bz;
+endmodule
+
+module GP_PGA(input wire VIN_P, input wire VIN_N, input wire VIN_SEL, output reg VOUT);
+
+ parameter GAIN = 1;
+ parameter INPUT_MODE = "SINGLE";
+
+ initial VOUT = 0;
+
+ //cannot simulate mixed signal IP
+
+endmodule
+
+module GP_PGEN(input wire nRST, input wire CLK, output reg OUT);
+ initial OUT = 0;
+ parameter PATTERN_DATA = 16'h0;
+ parameter PATTERN_LEN = 5'd16;
+
+ reg[3:0] count = 0;
+ always @(posedge CLK) begin
+ if(!nRST)
+ OUT <= PATTERN_DATA[0];
+
+ else begin
+ count <= count + 1;
+ OUT <= PATTERN_DATA[count];
+
+ if( (count + 1) == PATTERN_LEN)
+ count <= 0;
+ end
+ end
+
+endmodule
+
+module GP_POR(output reg RST_DONE);
+ parameter POR_TIME = 500;
+
+ initial begin
+ RST_DONE = 0;
+
+ if(POR_TIME == 4)
+ #4000;
+ else if(POR_TIME == 500)
+ #500000;
+ else begin
+ $display("ERROR: bad POR_TIME for GP_POR cell");
+ $finish;
+ end
+
+ RST_DONE = 1;
+
+ end
+
+endmodule
+
+module GP_RCOSC(input PWRDN, output reg CLKOUT_HARDIP, output reg CLKOUT_FABRIC);
+
+ parameter PWRDN_EN = 0;
+ parameter AUTO_PWRDN = 0;
+ parameter HARDIP_DIV = 1;
+ parameter FABRIC_DIV = 1;
+ parameter OSC_FREQ = "25k";
+
+ initial CLKOUT_HARDIP = 0;
+ initial CLKOUT_FABRIC = 0;
+
+ //output dividers not implemented for simulation
+ //auto powerdown not implemented for simulation
+
+ always begin
+ if(PWRDN) begin
+ CLKOUT_HARDIP = 0;
+ CLKOUT_FABRIC = 0;
+ end
+ else begin
+
+ if(OSC_FREQ == "25k") begin
+ //half period of 25 kHz
+ #20000;
+ end
+
+ else begin
+ //half period of 2 MHz
+ #250;
+ end
+
+ CLKOUT_HARDIP = ~CLKOUT_HARDIP;
+ CLKOUT_FABRIC = ~CLKOUT_FABRIC;
+ end
+ end
+
+endmodule
+
+module GP_RINGOSC(input PWRDN, output reg CLKOUT_HARDIP, output reg CLKOUT_FABRIC);
+
+ parameter PWRDN_EN = 0;
+ parameter AUTO_PWRDN = 0;
+ parameter HARDIP_DIV = 1;
+ parameter FABRIC_DIV = 1;
+
+ initial CLKOUT_HARDIP = 0;
+ initial CLKOUT_FABRIC = 0;
+
+ //output dividers not implemented for simulation
+ //auto powerdown not implemented for simulation
+
+ always begin
+ if(PWRDN) begin
+ CLKOUT_HARDIP = 0;
+ CLKOUT_FABRIC = 0;
+ end
+ else begin
+ //half period of 27 MHz
+ #18.518;
+ CLKOUT_HARDIP = ~CLKOUT_HARDIP;
+ CLKOUT_FABRIC = ~CLKOUT_FABRIC;
+ end
+ end
+
+endmodule
+
+module GP_SHREG(input nRST, input CLK, input IN, output OUTA, output OUTB);
+
+ parameter OUTA_TAP = 1;
+ parameter OUTA_INVERT = 0;
+ parameter OUTB_TAP = 1;
+
+ reg[15:0] shreg = 0;
+
+ always @(posedge CLK, negedge nRST) begin
+
+ if(!nRST)
+ shreg = 0;
+
+ else
+ shreg <= {shreg[14:0], IN};
+
+ end
+
+ assign OUTA = (OUTA_INVERT) ? ~shreg[OUTA_TAP - 1] : shreg[OUTA_TAP - 1];
+ assign OUTB = shreg[OUTB_TAP - 1];
+
+endmodule
+
+//keep constraint needed to prevent optimization since we have no outputs
+(* keep *)
+module GP_SYSRESET(input RST);
+ parameter RESET_MODE = "EDGE";
+ parameter EDGE_SPEED = 4;
+
+ //cannot simulate whole system reset
+
+endmodule
+
+module GP_VDD(output OUT);
+ assign OUT = 1;
+endmodule
+
+module GP_VREF(input VIN, output reg VOUT);
+ parameter VIN_DIV = 1;
+ parameter VREF = 0;
+ //cannot simulate mixed signal IP
+endmodule
+
+module GP_VSS(output OUT);
+ assign OUT = 0;
+endmodule
diff --git a/techlibs/greenpak4/gp_dff.lib b/techlibs/greenpak4/gp_dff.lib
new file mode 100644
index 00000000..b4b8c102
--- /dev/null
+++ b/techlibs/greenpak4/gp_dff.lib
@@ -0,0 +1,36 @@
+library(gp_dff) {
+ cell(GP_DFF) {
+ area: 1;
+ ff("IQ", "IQN") { clocked_on: CLK;
+ next_state: D; }
+ pin(CLK) { direction: input;
+ clock: true; }
+ pin(D) { direction: input; }
+ pin(Q) { direction: output;
+ function: "IQ"; }
+ }
+ cell(GP_DFFS) {
+ area: 1;
+ ff("IQ", "IQN") { clocked_on: CLK;
+ next_state: D;
+ preset: "nSET'"; }
+ pin(CLK) { direction: input;
+ clock: true; }
+ pin(D) { direction: input; }
+ pin(Q) { direction: output;
+ function: "IQ"; }
+ pin(nSET) { direction: input; }
+ }
+ cell(GP_DFFR) {
+ area: 1;
+ ff("IQ", "IQN") { clocked_on: CLK;
+ next_state: D;
+ clear: "nRST'"; }
+ pin(CLK) { direction: input;
+ clock: true; }
+ pin(D) { direction: input; }
+ pin(Q) { direction: output;
+ function: "IQ"; }
+ pin(nRST) { direction: input; }
+ }
+}
diff --git a/techlibs/greenpak4/greenpak4_counters.cc b/techlibs/greenpak4/greenpak4_counters.cc
new file mode 100644
index 00000000..998bb73b
--- /dev/null
+++ b/techlibs/greenpak4/greenpak4_counters.cc
@@ -0,0 +1,442 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2016 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/modtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+//get the list of cells hooked up to at least one bit of a given net
+pool<Cell*> get_other_cells(const RTLIL::SigSpec& port, ModIndex& index, Cell* src)
+{
+ pool<Cell*> rval;
+ for(auto b : port)
+ {
+ pool<ModIndex::PortInfo> ports = index.query_ports(b);
+ for(auto x : ports)
+ {
+ if(x.cell == src)
+ continue;
+ rval.insert(x.cell);
+ }
+ }
+ return rval;
+}
+
+//return true if there is a full-width bus connection from cell a port ap to cell b port bp
+//if other_conns_allowed is false, then we require a strict point to point connection (no other links)
+bool is_full_bus(
+ const RTLIL::SigSpec& sig,
+ ModIndex& index,
+ Cell* a,
+ RTLIL::IdString ap,
+ Cell* b,
+ RTLIL::IdString bp,
+ bool other_conns_allowed = false)
+{
+ for(auto s : sig)
+ {
+ pool<ModIndex::PortInfo> ports = index.query_ports(s);
+ bool found_a = false;
+ bool found_b = false;
+ for(auto x : ports)
+ {
+ if( (x.cell == a) && (x.port == ap) )
+ found_a = true;
+ else if( (x.cell == b) && (x.port == bp) )
+ found_b = true;
+ else if(!other_conns_allowed)
+ return false;
+ }
+
+ if( (!found_a) || (!found_b) )
+ return false;
+ }
+
+ return true;
+}
+
+//return true if the signal connects to one port only (nothing on the other end)
+bool is_unconnected(const RTLIL::SigSpec& port, ModIndex& index)
+{
+ for(auto b : port)
+ {
+ pool<ModIndex::PortInfo> ports = index.query_ports(b);
+ if(ports.size() > 1)
+ return false;
+ }
+
+ return true;
+}
+
+struct CounterExtraction
+{
+ int width; //counter width
+ RTLIL::Wire* rwire; //the register output
+ bool has_reset; //true if we have a reset
+ RTLIL::SigSpec rst; //reset pin
+ int count_value; //value we count from
+ RTLIL::SigSpec clk; //clock signal
+ RTLIL::SigSpec outsig; //counter output signal
+ RTLIL::Cell* count_mux; //counter mux
+ RTLIL::Cell* count_reg; //counter register
+ RTLIL::Cell* underflow_inv; //inverter reduction for output-underflow detect
+};
+
+//attempt to extract a counter centered on the given cell
+int greenpak4_counters_tryextract(ModIndex& index, Cell *cell, CounterExtraction& extract)
+{
+ SigMap& sigmap = index.sigmap;
+
+ //GreenPak does not support counters larger than 14 bits so immediately skip anything bigger
+ int a_width = cell->getParam("\\A_WIDTH").as_int();
+ extract.width = a_width;
+ if(a_width > 14)
+ return 1;
+
+ //Second input must be a single bit
+ int b_width = cell->getParam("\\B_WIDTH").as_int();
+ if(b_width != 1)
+ return 2;
+
+ //Both inputs must be unsigned, so don't extract anything with a signed input
+ bool a_sign = cell->getParam("\\A_SIGNED").as_bool();
+ bool b_sign = cell->getParam("\\B_SIGNED").as_bool();
+ if(a_sign || b_sign)
+ return 3;
+
+ //To be a counter, one input of the ALU must be a constant 1
+ //TODO: can A or B be swapped in synthesized RTL or is B always the 1?
+ const RTLIL::SigSpec b_port = sigmap(cell->getPort("\\B"));
+ if(!b_port.is_fully_const() || (b_port.as_int() != 1) )
+ return 4;
+
+ //BI and CI must be constant 1 as well
+ const RTLIL::SigSpec bi_port = sigmap(cell->getPort("\\BI"));
+ if(!bi_port.is_fully_const() || (bi_port.as_int() != 1) )
+ return 5;
+ const RTLIL::SigSpec ci_port = sigmap(cell->getPort("\\CI"));
+ if(!ci_port.is_fully_const() || (ci_port.as_int() != 1) )
+ return 6;
+
+ //CO and X must be unconnected (exactly one connection to each port)
+ if(!is_unconnected(sigmap(cell->getPort("\\CO")), index))
+ return 7;
+ if(!is_unconnected(sigmap(cell->getPort("\\X")), index))
+ return 8;
+
+ //Y must have exactly one connection, and it has to be a $mux cell.
+ //We must have a direct bus connection from our Y to their A.
+ const RTLIL::SigSpec aluy = sigmap(cell->getPort("\\Y"));
+ pool<Cell*> y_loads = get_other_cells(aluy, index, cell);
+ if(y_loads.size() != 1)
+ return 9;
+ Cell* count_mux = *y_loads.begin();
+ extract.count_mux = count_mux;
+ if(count_mux->type != "$mux")
+ return 10;
+ if(!is_full_bus(aluy, index, cell, "\\Y", count_mux, "\\A"))
+ return 11;
+
+ //B connection of the mux is our underflow value
+ const RTLIL::SigSpec underflow = sigmap(count_mux->getPort("\\B"));
+ if(!underflow.is_fully_const())
+ return 12;
+ extract.count_value = underflow.as_int();
+
+ //S connection of the mux must come from an inverter (need not be the only load)
+ const RTLIL::SigSpec muxsel = sigmap(count_mux->getPort("\\S"));
+ extract.outsig = muxsel;
+ pool<Cell*> muxsel_conns = get_other_cells(muxsel, index, count_mux);
+ Cell* underflow_inv = NULL;
+ for(auto c : muxsel_conns)
+ {
+ if(c->type != "$logic_not")
+ continue;
+ if(!is_full_bus(muxsel, index, c, "\\Y", count_mux, "\\S", true))
+ continue;
+
+ underflow_inv = c;
+ break;
+ }
+ if(underflow_inv == NULL)
+ return 13;
+ extract.underflow_inv = underflow_inv;
+
+ //Y connection of the mux must have exactly one load, the counter's internal register
+ const RTLIL::SigSpec muxy = sigmap(count_mux->getPort("\\Y"));
+ pool<Cell*> muxy_loads = get_other_cells(muxy, index, count_mux);
+ if(muxy_loads.size() != 1)
+ return 14;
+ Cell* count_reg = *muxy_loads.begin();
+ extract.count_reg = count_reg;
+ if(count_reg->type == "$dff")
+ extract.has_reset = false;
+ else if(count_reg->type == "$adff")
+ {
+ extract.has_reset = true;
+
+ //Verify ARST_VALUE is zero and ARST_POLARITY is 1
+ //TODO: infer an inverter to make it 1 if necessary, so we can support negative level resets?
+ if(count_reg->getParam("\\ARST_POLARITY").as_int() != 1)
+ return 22;
+ if(count_reg->getParam("\\ARST_VALUE").as_int() != 0)
+ return 23;
+
+ //Save the reset
+ extract.rst = sigmap(count_reg->getPort("\\ARST"));
+ }
+ //TODO: support synchronous reset
+ else
+ return 15;
+ if(!is_full_bus(muxy, index, count_mux, "\\Y", count_reg, "\\D"))
+ return 16;
+
+ //TODO: Verify count_reg CLK_POLARITY is 1
+
+ //Register output must have exactly two loads, the inverter and ALU
+ const RTLIL::SigSpec cnout = sigmap(count_reg->getPort("\\Q"));
+ pool<Cell*> cnout_loads = get_other_cells(cnout, index, count_reg);
+ if(cnout_loads.size() != 2)
+ return 17;
+ if(!is_full_bus(cnout, index, count_reg, "\\Q", underflow_inv, "\\A", true))
+ return 18;
+ if(!is_full_bus(cnout, index, count_reg, "\\Q", cell, "\\A", true))
+ return 19;
+
+ //Look up the clock from the register
+ extract.clk = sigmap(count_reg->getPort("\\CLK"));
+
+ //Register output net must have an INIT attribute equal to the count value
+ extract.rwire = cnout.as_wire();
+ if(extract.rwire->attributes.find("\\init") == extract.rwire->attributes.end())
+ return 20;
+ int rinit = extract.rwire->attributes["\\init"].as_int();
+ if(rinit != extract.count_value)
+ return 21;
+
+ return 0;
+}
+
+void greenpak4_counters_worker(
+ ModIndex& index,
+ Cell *cell,
+ unsigned int& total_counters,
+ pool<Cell*>& cells_to_remove)
+{
+ SigMap& sigmap = index.sigmap;
+
+ //Core of the counter must be an ALU
+ if (cell->type != "$alu")
+ return;
+
+ //A input is the count value. Check if it has COUNT_EXTRACT set.
+ //If it's not a wire, don't even try
+ auto port = sigmap(cell->getPort("\\A"));
+ if(!port.is_wire())
+ return;
+ RTLIL::Wire* a_wire = port.as_wire();
+ bool force_extract = false;
+ bool never_extract = false;
+ string count_reg_src = a_wire->attributes["\\src"].decode_string().c_str();
+ if(a_wire->attributes.find("\\COUNT_EXTRACT") != a_wire->attributes.end())
+ {
+ pool<string> sa = a_wire->get_strpool_attribute("\\COUNT_EXTRACT");
+ string extract_value;
+ if(sa.size() >= 1)
+ {
+ extract_value = *sa.begin();
+ log(" Signal %s declared at %s has COUNT_EXTRACT = %s\n",
+ log_id(a_wire),
+ count_reg_src.c_str(),
+ extract_value.c_str());
+
+ if(extract_value == "FORCE")
+ force_extract = true;
+ else if(extract_value == "NO")
+ never_extract = true;
+ else if(extract_value == "AUTO")
+ {} //default
+ else
+ log_error(" Illegal COUNT_EXTRACT value %s (must be one of FORCE, NO, AUTO)\n",
+ extract_value.c_str());
+ }
+ }
+
+ //If we're explicitly told not to extract, don't infer a counter
+ if(never_extract)
+ return;
+
+ //Attempt to extract a counter
+ CounterExtraction extract;
+ int reason = greenpak4_counters_tryextract(index, cell, extract);
+
+ //Nonzero code - we could not find a matchable counter.
+ //Do nothing, unless extraction was forced in which case give an error
+ if(reason != 0)
+ {
+ static const char* reasons[24]=
+ {
+ "no problem", //0
+ "counter is larger than 14 bits", //1
+ "counter does not count by one", //2
+ "counter uses signed math", //3
+ "counter does not count by one", //4
+ "ALU is not a subtractor", //5
+ "ALU is not a subtractor", //6
+ "ALU ports used outside counter", //7
+ "ALU ports used outside counter", //8
+ "ALU output used outside counter", //9
+ "ALU output is not a mux", //10
+ "ALU output is not full bus", //11
+ "Underflow value is not constant", //12
+ "No underflow detector found", //13
+ "Mux output is used outside counter", //14
+ "Counter reg is not DFF/ADFF", //15
+ "Counter input is not full bus", //16
+ "Count register is used outside counter", //17
+ "Register output is not full bus", //18
+ "Register output is not full bus", //19
+ "No init value found", //20
+ "Underflow value is not equal to init value", //21
+ "Reset polarity is not positive", //22
+ "Reset is not to zero" //23
+ };
+
+ if(force_extract)
+ {
+ log_error(
+ "Counter extraction is set to FORCE on register %s, but a counter could not be inferred (%s)\n",
+ log_id(a_wire),
+ reasons[reason]);
+ }
+ return;
+ }
+
+ //Figure out the final cell type based on the counter size
+ string celltype = "\\GP_COUNT8";
+ if(extract.width > 8)
+ celltype = "\\GP_COUNT14";
+
+ //Log it
+ total_counters ++;
+ string reset_type = "non-resettable";
+ if(extract.has_reset)
+ {
+ //TODO: support other kind of reset
+ reset_type = "async resettable";
+ }
+ log(" Found %d-bit %s down counter (from %d) for register %s declared at %s\n",
+ extract.width,
+ reset_type.c_str(),
+ extract.count_value,
+ log_id(extract.rwire->name),
+ count_reg_src.c_str());
+
+ //Wipe all of the old connections to the ALU
+ cell->unsetPort("\\A");
+ cell->unsetPort("\\B");
+ cell->unsetPort("\\BI");
+ cell->unsetPort("\\CI");
+ cell->unsetPort("\\CO");
+ cell->unsetPort("\\X");
+ cell->unsetPort("\\Y");
+ cell->unsetParam("\\A_SIGNED");
+ cell->unsetParam("\\A_WIDTH");
+ cell->unsetParam("\\B_SIGNED");
+ cell->unsetParam("\\B_WIDTH");
+ cell->unsetParam("\\Y_WIDTH");
+
+ //Change the cell type
+ cell->type = celltype;
+
+ //Hook up resets
+ if(extract.has_reset)
+ {
+ //TODO: support other kinds of reset
+ cell->setParam("\\RESET_MODE", RTLIL::Const("LEVEL"));
+ cell->setPort("\\RST", extract.rst);
+ }
+ else
+ {
+ cell->setParam("\\RESET_MODE", RTLIL::Const("RISING"));
+ cell->setPort("\\RST", RTLIL::SigSpec(false));
+ }
+
+ //Hook up other stuff
+ cell->setParam("\\CLKIN_DIVIDE", RTLIL::Const(1));
+ cell->setParam("\\COUNT_TO", RTLIL::Const(extract.count_value));
+
+ cell->setPort("\\CLK", extract.clk);
+ cell->setPort("\\OUT", extract.outsig);
+
+ //Delete the cells we've replaced (let opt_clean handle deleting the now-redundant wires)
+ cells_to_remove.insert(extract.count_mux);
+ cells_to_remove.insert(extract.count_reg);
+ cells_to_remove.insert(extract.underflow_inv);
+}
+
+struct Greenpak4CountersPass : public Pass {
+ Greenpak4CountersPass() : Pass("greenpak4_counters", "Extract GreenPak4 counter cells") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" greenpak4_counters [options] [selection]\n");
+ log("\n");
+ log("This pass converts non-resettable or async resettable down counters to GreenPak4\n");
+ log("counter cells (All other GreenPak4 counter modes must be instantiated manually.)\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing GREENPAK4_COUNTERS pass (mapping counters to hard IP blocks).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-v") {
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ //Extract all of the counters we could find
+ unsigned int total_counters = 0;
+ for (auto module : design->selected_modules())
+ {
+ pool<Cell*> cells_to_remove;
+
+ ModIndex index(module);
+ for (auto cell : module->selected_cells())
+ greenpak4_counters_worker(index, cell, total_counters, cells_to_remove);
+
+ for(auto cell : cells_to_remove)
+ module->remove(cell);
+ }
+
+ if(total_counters)
+ log("Extracted %u counters\n", total_counters);
+ }
+} Greenpak4CountersPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/greenpak4/greenpak4_dffinv.cc b/techlibs/greenpak4/greenpak4_dffinv.cc
new file mode 100644
index 00000000..ff63958e
--- /dev/null
+++ b/techlibs/greenpak4/greenpak4_dffinv.cc
@@ -0,0 +1,197 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void invert_gp_dff(Cell *cell, bool invert_input)
+{
+ string cell_type = cell->type.str();
+ bool cell_type_i = cell_type.find('I') != string::npos;
+ bool cell_type_r = cell_type.find('R') != string::npos;
+ bool cell_type_s = cell_type.find('S') != string::npos;
+
+ if (!invert_input)
+ {
+ Const initval = cell->getParam("\\INIT");
+ if (GetSize(initval) >= 1) {
+ if (initval.bits[0] == State::S0)
+ initval.bits[0] = State::S1;
+ else if (initval.bits[0] == State::S1)
+ initval.bits[0] = State::S0;
+ cell->setParam("\\INIT", initval);
+ }
+
+ if (cell_type_r && cell_type_s)
+ {
+ Const srmode = cell->getParam("\\SRMODE");
+ if (GetSize(srmode) >= 1) {
+ if (srmode.bits[0] == State::S0)
+ srmode.bits[0] = State::S1;
+ else if (srmode.bits[0] == State::S1)
+ srmode.bits[0] = State::S0;
+ cell->setParam("\\SRMODE", srmode);
+ }
+ }
+ else
+ {
+ if (cell_type_r) {
+ cell->setPort("\\nSET", cell->getPort("\\nRST"));
+ cell->unsetPort("\\nRST");
+ cell_type_r = false;
+ cell_type_s = true;
+ } else
+ if (cell_type_s) {
+ cell->setPort("\\nRST", cell->getPort("\\nSET"));
+ cell->unsetPort("\\nSET");
+ cell_type_r = true;
+ cell_type_s = false;
+ }
+ }
+ }
+
+ if (cell_type_i) {
+ cell->setPort("\\Q", cell->getPort("\\nQ"));
+ cell->unsetPort("\\nQ");
+ cell_type_i = false;
+ } else {
+ cell->setPort("\\nQ", cell->getPort("\\Q"));
+ cell->unsetPort("\\Q");
+ cell_type_i = true;
+ }
+
+ cell->type = stringf("\\GP_DFF%s%s%s", cell_type_s ? "S" : "", cell_type_r ? "R" : "", cell_type_i ? "I" : "");
+
+ log("Merged %s inverter into cell %s.%s: %s -> %s\n", invert_input ? "input" : "output",
+ log_id(cell->module), log_id(cell), cell_type.c_str()+1, log_id(cell->type));
+}
+
+struct Greenpak4DffInvPass : public Pass {
+ Greenpak4DffInvPass() : Pass("greenpak4_dffinv", "merge greenpak4 inverters and DFFs") { }
+ virtual void help()
+ {
+ log("\n");
+ log(" greenpak4_dffinv [options] [selection]\n");
+ log("\n");
+ log("Merge GP_INV cells with GP_DFF* cells.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing GREENPAK4_DFFINV pass (merge synchronous set/reset into FF cells).\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);
+
+ pool<IdString> gp_dff_types;
+ gp_dff_types.insert("\\GP_DFF");
+ gp_dff_types.insert("\\GP_DFFI");
+ gp_dff_types.insert("\\GP_DFFR");
+ gp_dff_types.insert("\\GP_DFFRI");
+ gp_dff_types.insert("\\GP_DFFS");
+ gp_dff_types.insert("\\GP_DFFSI");
+ gp_dff_types.insert("\\GP_DFFSR");
+ gp_dff_types.insert("\\GP_DFFSRI");
+
+ for (auto module : design->selected_modules())
+ {
+ SigMap sigmap(module);
+ dict<SigBit, int> sig_use_cnt;
+ dict<SigBit, SigBit> inv_in2out, inv_out2in;
+ dict<SigBit, Cell*> inv_in2cell;
+ pool<Cell*> dff_cells;
+
+ for (auto wire : module->wires())
+ {
+ if (!wire->port_output)
+ continue;
+
+ for (auto bit : sigmap(wire))
+ sig_use_cnt[bit]++;
+ }
+
+ for (auto cell : module->cells())
+ for (auto &conn : cell->connections())
+ if (cell->input(conn.first) || !cell->known())
+ for (auto bit : sigmap(conn.second))
+ sig_use_cnt[bit]++;
+
+ for (auto cell : module->selected_cells())
+ {
+ if (gp_dff_types.count(cell->type)) {
+ dff_cells.insert(cell);
+ continue;
+ }
+
+ if (cell->type == "\\GP_INV") {
+ SigBit in_bit = sigmap(cell->getPort("\\IN"));
+ SigBit out_bit = sigmap(cell->getPort("\\OUT"));
+ inv_in2out[in_bit] = out_bit;
+ inv_out2in[out_bit] = in_bit;
+ inv_in2cell[in_bit] = cell;
+ continue;
+ }
+ }
+
+ for (auto cell : dff_cells)
+ {
+ SigBit d_bit = sigmap(cell->getPort("\\D"));
+ SigBit q_bit = sigmap(cell->hasPort("\\Q") ? cell->getPort("\\Q") : cell->getPort("\\nQ"));
+
+ while (inv_out2in.count(d_bit))
+ {
+ sig_use_cnt[d_bit]--;
+ invert_gp_dff(cell, true);
+ d_bit = inv_out2in.at(d_bit);
+ cell->setPort("\\D", d_bit);
+ sig_use_cnt[d_bit]++;
+ }
+
+ while (inv_in2out.count(q_bit) && sig_use_cnt[q_bit] == 1)
+ {
+ SigBit new_q_bit = inv_in2out.at(q_bit);
+ module->remove(inv_in2cell.at(q_bit));
+ sig_use_cnt.erase(q_bit);
+ inv_in2out.erase(q_bit);
+ inv_out2in.erase(new_q_bit);
+ inv_in2cell.erase(q_bit);
+
+ invert_gp_dff(cell, false);
+ if (cell->hasPort("\\Q"))
+ cell->setPort("\\Q", new_q_bit);
+ else
+ cell->setPort("\\nQ", new_q_bit);
+ }
+ }
+ }
+ }
+} Greenpak4DffInvPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/greenpak4/synth_greenpak4.cc b/techlibs/greenpak4/synth_greenpak4.cc
new file mode 100644
index 00000000..10e2a149
--- /dev/null
+++ b/techlibs/greenpak4/synth_greenpak4.cc
@@ -0,0 +1,209 @@
+/*
+ * 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/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct SynthGreenPAK4Pass : public ScriptPass
+{
+ SynthGreenPAK4Pass() : ScriptPass("synth_greenpak4", "synthesis for GreenPAK4 FPGAs") { }
+
+ virtual void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" synth_greenpak4 [options]\n");
+ log("\n");
+ log("This command runs synthesis for GreenPAK4 FPGAs. This work is experimental.\n");
+ log("\n");
+ log(" -top <module>\n");
+ log(" use the specified module as top module (default='top')\n");
+ log("\n");
+ log(" -part <part>\n");
+ log(" synthesize for the specified part. Valid values are SLG46140V,\n");
+ log(" SLG46620V, and SLG46621V (default).\n");
+ log("\n");
+ log(" -json <file>\n");
+ log(" write the design to the specified JSON file. writing of an output file\n");
+ log(" is omitted if this parameter is not specified.\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 'begin', and empty to label is\n");
+ log(" synonymous to the end of the command list.\n");
+ log("\n");
+ log(" -noflatten\n");
+ log(" do not flatten design before synthesis\n");
+ log("\n");
+ log(" -retime\n");
+ log(" run 'abc' with -dff option\n");
+ log("\n");
+ log("\n");
+ log("The following commands are executed by this synthesis command:\n");
+ help_script();
+ log("\n");
+ }
+
+ string top_opt, part, json_file;
+ bool flatten, retime;
+
+ virtual void clear_flags() YS_OVERRIDE
+ {
+ top_opt = "-auto-top";
+ part = "SLG46621V";
+ json_file = "";
+ flatten = true;
+ retime = false;
+ }
+
+ virtual 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] == "-top" && argidx+1 < args.size()) {
+ top_opt = "-top " + args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-json" && argidx+1 < args.size()) {
+ json_file = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-part" && argidx+1 < args.size()) {
+ part = args[++argidx];
+ continue;
+ }
+ 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] == "-noflatten") {
+ flatten = false;
+ continue;
+ }
+ if (args[argidx] == "-retime") {
+ retime = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!design->full_selection())
+ log_cmd_error("This comannd only operates on fully selected designs!\n");
+
+ if (part != "SLG46140V" && part != "SLG46620V" && part != "SLG46621V")
+ log_cmd_error("Invalid part name: '%s'\n", part.c_str());
+
+ log_header(design, "Executing SYNTH_GREENPAK4 pass.\n");
+ log_push();
+
+ run_script(design, run_from, run_to);
+
+ log_pop();
+ }
+
+ virtual void script() YS_OVERRIDE
+ {
+ if (check_label("begin"))
+ {
+ run("read_verilog -lib +/greenpak4/cells_sim.v");
+ run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
+ }
+
+ if (flatten && check_label("flatten", "(unless -noflatten)"))
+ {
+ run("proc");
+ run("flatten");
+ run("tribuf -logic");
+ }
+
+ if (check_label("coarse"))
+ {
+ run("synth -run coarse");
+ }
+
+ if (check_label("fine"))
+ {
+ run("greenpak4_counters");
+ run("clean");
+ run("opt -fast -mux_undef -undriven -fine");
+ run("memory_map");
+ run("opt -undriven -fine");
+ run("techmap");
+ run("dfflibmap -prepare -liberty +/greenpak4/gp_dff.lib");
+ run("opt -fast");
+ if (retime || help_mode)
+ run("abc -dff", "(only if -retime)");
+ }
+
+ if (check_label("map_luts"))
+ {
+ if (help_mode || part == "SLG46140V") run("nlutmap -assert -luts 0,6,8,2", " (for -part SLG46140V)");
+ if (help_mode || part == "SLG46620V") run("nlutmap -assert -luts 2,8,16,2", "(for -part SLG46620V)");
+ if (help_mode || part == "SLG46621V") run("nlutmap -assert -luts 2,8,16,2", "(for -part SLG46621V)");
+ run("clean");
+ }
+
+ if (check_label("map_cells"))
+ {
+ run("shregmap -tech greenpak4");
+ run("dfflibmap -liberty +/greenpak4/gp_dff.lib");
+ run("dffinit -ff GP_DFF Q INIT");
+ run("dffinit -ff GP_DFFR Q INIT");
+ run("dffinit -ff GP_DFFS Q INIT");
+ run("dffinit -ff GP_DFFSR Q INIT");
+ run("iopadmap -bits -inpad GP_IBUF OUT:IN -outpad GP_OBUF IN:OUT -inoutpad GP_OBUF OUT:IN -toutpad GP_OBUFT OE:IN:OUT -tinoutpad GP_IOBUF OE:OUT:IN:IO");
+ run("attrmvcp -attr src -attr LOC t:GP_OBUF t:GP_OBUFT t:GP_IOBUF n:*");
+ run("attrmvcp -attr src -attr LOC -driven t:GP_IBUF n:*");
+ run("techmap -map +/greenpak4/cells_map.v");
+ run("greenpak4_dffinv");
+ run("clean");
+ }
+
+ if (check_label("check"))
+ {
+ run("hierarchy -check");
+ run("stat");
+ run("check -noinit");
+ }
+
+ if (check_label("json"))
+ {
+ if (!json_file.empty() || help_mode)
+ run(stringf("write_json %s", help_mode ? "<file-name>" : json_file.c_str()));
+ }
+
+ log_pop();
+ }
+} SynthGreenPAK4Pass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/ice40/.gitignore b/techlibs/ice40/.gitignore
new file mode 100644
index 00000000..6bf3b671
--- /dev/null
+++ b/techlibs/ice40/.gitignore
@@ -0,0 +1,4 @@
+brams_init.mk
+brams_init1.vh
+brams_init2.vh
+brams_init3.vh
diff --git a/techlibs/ice40/Makefile.inc b/techlibs/ice40/Makefile.inc
new file mode 100644
index 00000000..14761c6c
--- /dev/null
+++ b/techlibs/ice40/Makefile.inc
@@ -0,0 +1,33 @@
+
+OBJS += techlibs/ice40/synth_ice40.o
+OBJS += techlibs/ice40/ice40_ffssr.o
+OBJS += techlibs/ice40/ice40_ffinit.o
+OBJS += techlibs/ice40/ice40_opt.o
+
+GENFILES += techlibs/ice40/brams_init1.vh
+GENFILES += techlibs/ice40/brams_init2.vh
+GENFILES += techlibs/ice40/brams_init3.vh
+
+EXTRA_OBJS += techlibs/ice40/brams_init.mk
+.SECONDARY: techlibs/ice40/brams_init.mk
+
+techlibs/ice40/brams_init.mk: techlibs/ice40/brams_init.py
+ $(Q) mkdir -p techlibs/ice40
+ $(P) python3 $<
+ $(Q) touch techlibs/ice40/brams_init.mk
+
+techlibs/ice40/brams_init1.vh: techlibs/ice40/brams_init.mk
+techlibs/ice40/brams_init2.vh: techlibs/ice40/brams_init.mk
+techlibs/ice40/brams_init3.vh: techlibs/ice40/brams_init.mk
+
+$(eval $(call add_share_file,share/ice40,techlibs/ice40/arith_map.v))
+$(eval $(call add_share_file,share/ice40,techlibs/ice40/cells_map.v))
+$(eval $(call add_share_file,share/ice40,techlibs/ice40/cells_sim.v))
+$(eval $(call add_share_file,share/ice40,techlibs/ice40/latches_map.v))
+$(eval $(call add_share_file,share/ice40,techlibs/ice40/brams.txt))
+$(eval $(call add_share_file,share/ice40,techlibs/ice40/brams_map.v))
+
+$(eval $(call add_gen_share_file,share/ice40,techlibs/ice40/brams_init1.vh))
+$(eval $(call add_gen_share_file,share/ice40,techlibs/ice40/brams_init2.vh))
+$(eval $(call add_gen_share_file,share/ice40,techlibs/ice40/brams_init3.vh))
+
diff --git a/techlibs/ice40/arith_map.v b/techlibs/ice40/arith_map.v
new file mode 100644
index 00000000..4449fdc1
--- /dev/null
+++ b/techlibs/ice40/arith_map.v
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ *
+ */
+
+(* techmap_celltype = "$alu" *)
+module _80_ice40_alu (A, B, CI, BI, X, Y, CO);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] X, Y;
+
+ input CI, BI;
+ output [Y_WIDTH-1:0] CO;
+
+ wire _TECHMAP_FAIL_ = Y_WIDTH <= 2;
+
+ wire [Y_WIDTH-1:0] A_buf, B_buf;
+ \$pos #(.A_SIGNED(A_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(Y_WIDTH)) A_conv (.A(A), .Y(A_buf));
+ \$pos #(.A_SIGNED(B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) B_conv (.A(B), .Y(B_buf));
+
+ wire [Y_WIDTH-1:0] AA = A_buf;
+ wire [Y_WIDTH-1:0] BB = BI ? ~B_buf : B_buf;
+ wire [Y_WIDTH-1:0] C = {CO, CI};
+
+ genvar i;
+ generate for (i = 0; i < Y_WIDTH; i = i + 1) begin:slice
+ SB_CARRY carry (
+ .I0(AA[i]),
+ .I1(BB[i]),
+ .CI(C[i]),
+ .CO(CO[i])
+ );
+ SB_LUT4 #(
+ // I0: 1010 1010 1010 1010
+ // I1: 1100 1100 1100 1100
+ // I2: 1111 0000 1111 0000
+ // I3: 1111 1111 0000 0000
+ .LUT_INIT(16'b 0110_1001_1001_0110)
+ ) adder (
+ .I0(1'b0),
+ .I1(AA[i]),
+ .I2(BB[i]),
+ .I3(C[i]),
+ .O(Y[i])
+ );
+ end endgenerate
+
+ assign X = AA ^ BB;
+endmodule
+
diff --git a/techlibs/ice40/brams.txt b/techlibs/ice40/brams.txt
new file mode 100644
index 00000000..03d59611
--- /dev/null
+++ b/techlibs/ice40/brams.txt
@@ -0,0 +1,40 @@
+bram $__ICE40_RAM4K_M0
+ init 1
+ abits 8
+ dbits 16
+ groups 2
+ ports 1 1
+ wrmode 0 1
+ enable 1 16
+ transp 0 0
+ clocks 2 3
+ clkpol 2 3
+endbram
+
+bram $__ICE40_RAM4K_M123
+ init 1
+ abits 9 @M1
+ dbits 8 @M1
+ abits 10 @M2
+ dbits 4 @M2
+ abits 11 @M3
+ dbits 2 @M3
+ groups 2
+ ports 1 1
+ wrmode 0 1
+ enable 1 1
+ transp 0 0
+ clocks 2 3
+ clkpol 2 3
+endbram
+
+match $__ICE40_RAM4K_M0
+ min efficiency 2
+ make_transp
+ or_next_if_better
+endmatch
+
+match $__ICE40_RAM4K_M123
+ min efficiency 2
+ make_transp
+endmatch
diff --git a/techlibs/ice40/brams_init.py b/techlibs/ice40/brams_init.py
new file mode 100644
index 00000000..4a148511
--- /dev/null
+++ b/techlibs/ice40/brams_init.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python3
+
+def write_init_vh(filename, initbits):
+ with open(filename, "w") as f:
+ for i in range(16):
+ print("localparam [255:0] INIT_%X = {" % i, file=f)
+ for k in range(32):
+ print(" %s%s" % (", ".join(["INIT[%4d]" % initbits[i*256 + 255 - k*8 - l] for l in range(8)]), "," if k != 31 else ""), file=f)
+ print("};", file=f);
+
+write_init_vh("techlibs/ice40/brams_init1.vh", [i//2 + 2048*(i%2) for i in range(4096)])
+write_init_vh("techlibs/ice40/brams_init2.vh", [i//4 + 1024*(i%4) for i in range(4096)])
+write_init_vh("techlibs/ice40/brams_init3.vh", [i//8 + 512*(i%8) for i in range(4096)])
+
diff --git a/techlibs/ice40/brams_map.v b/techlibs/ice40/brams_map.v
new file mode 100644
index 00000000..19a61d73
--- /dev/null
+++ b/techlibs/ice40/brams_map.v
@@ -0,0 +1,311 @@
+
+module \$__ICE40_RAM4K (
+ output [15:0] RDATA,
+ input RCLK, RCLKE, RE,
+ input [10:0] RADDR,
+ input WCLK, WCLKE, WE,
+ input [10:0] WADDR,
+ input [15:0] MASK, WDATA
+);
+ parameter integer READ_MODE = 0;
+ parameter integer WRITE_MODE = 0;
+ parameter [0:0] NEGCLK_R = 0;
+ parameter [0:0] NEGCLK_W = 0;
+
+ parameter [255:0] INIT_0 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter [255:0] INIT_1 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter [255:0] INIT_2 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter [255:0] INIT_3 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter [255:0] INIT_4 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter [255:0] INIT_5 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter [255:0] INIT_6 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter [255:0] INIT_7 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter [255:0] INIT_8 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter [255:0] INIT_9 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter [255:0] INIT_A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter [255:0] INIT_B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter [255:0] INIT_C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter [255:0] INIT_D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter [255:0] INIT_E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter [255:0] INIT_F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+
+ generate
+ case ({NEGCLK_R, NEGCLK_W})
+ 2'b00:
+ SB_RAM40_4K #(
+ .READ_MODE(READ_MODE),
+ .WRITE_MODE(WRITE_MODE),
+ .INIT_0(INIT_0),
+ .INIT_1(INIT_1),
+ .INIT_2(INIT_2),
+ .INIT_3(INIT_3),
+ .INIT_4(INIT_4),
+ .INIT_5(INIT_5),
+ .INIT_6(INIT_6),
+ .INIT_7(INIT_7),
+ .INIT_8(INIT_8),
+ .INIT_9(INIT_9),
+ .INIT_A(INIT_A),
+ .INIT_B(INIT_B),
+ .INIT_C(INIT_C),
+ .INIT_D(INIT_D),
+ .INIT_E(INIT_E),
+ .INIT_F(INIT_F)
+ ) _TECHMAP_REPLACE_ (
+ .RDATA(RDATA),
+ .RCLK (RCLK ),
+ .RCLKE(RCLKE),
+ .RE (RE ),
+ .RADDR(RADDR),
+ .WCLK (WCLK ),
+ .WCLKE(WCLKE),
+ .WE (WE ),
+ .WADDR(WADDR),
+ .MASK (MASK ),
+ .WDATA(WDATA)
+ );
+ 2'b01:
+ SB_RAM40_4KNW #(
+ .READ_MODE(READ_MODE),
+ .WRITE_MODE(WRITE_MODE),
+ .INIT_0(INIT_0),
+ .INIT_1(INIT_1),
+ .INIT_2(INIT_2),
+ .INIT_3(INIT_3),
+ .INIT_4(INIT_4),
+ .INIT_5(INIT_5),
+ .INIT_6(INIT_6),
+ .INIT_7(INIT_7),
+ .INIT_8(INIT_8),
+ .INIT_9(INIT_9),
+ .INIT_A(INIT_A),
+ .INIT_B(INIT_B),
+ .INIT_C(INIT_C),
+ .INIT_D(INIT_D),
+ .INIT_E(INIT_E),
+ .INIT_F(INIT_F)
+ ) _TECHMAP_REPLACE_ (
+ .RDATA(RDATA),
+ .RCLK (RCLK ),
+ .RCLKE(RCLKE),
+ .RE (RE ),
+ .RADDR(RADDR),
+ .WCLKN(WCLK ),
+ .WCLKE(WCLKE),
+ .WE (WE ),
+ .WADDR(WADDR),
+ .MASK (MASK ),
+ .WDATA(WDATA)
+ );
+ 2'b10:
+ SB_RAM40_4KNR #(
+ .READ_MODE(READ_MODE),
+ .WRITE_MODE(WRITE_MODE),
+ .INIT_0(INIT_0),
+ .INIT_1(INIT_1),
+ .INIT_2(INIT_2),
+ .INIT_3(INIT_3),
+ .INIT_4(INIT_4),
+ .INIT_5(INIT_5),
+ .INIT_6(INIT_6),
+ .INIT_7(INIT_7),
+ .INIT_8(INIT_8),
+ .INIT_9(INIT_9),
+ .INIT_A(INIT_A),
+ .INIT_B(INIT_B),
+ .INIT_C(INIT_C),
+ .INIT_D(INIT_D),
+ .INIT_E(INIT_E),
+ .INIT_F(INIT_F)
+ ) _TECHMAP_REPLACE_ (
+ .RDATA(RDATA),
+ .RCLKN(RCLK ),
+ .RCLKE(RCLKE),
+ .RE (RE ),
+ .RADDR(RADDR),
+ .WCLK (WCLK ),
+ .WCLKE(WCLKE),
+ .WE (WE ),
+ .WADDR(WADDR),
+ .MASK (MASK ),
+ .WDATA(WDATA)
+ );
+ 2'b11:
+ SB_RAM40_4KNRNW #(
+ .READ_MODE(READ_MODE),
+ .WRITE_MODE(WRITE_MODE),
+ .INIT_0(INIT_0),
+ .INIT_1(INIT_1),
+ .INIT_2(INIT_2),
+ .INIT_3(INIT_3),
+ .INIT_4(INIT_4),
+ .INIT_5(INIT_5),
+ .INIT_6(INIT_6),
+ .INIT_7(INIT_7),
+ .INIT_8(INIT_8),
+ .INIT_9(INIT_9),
+ .INIT_A(INIT_A),
+ .INIT_B(INIT_B),
+ .INIT_C(INIT_C),
+ .INIT_D(INIT_D),
+ .INIT_E(INIT_E),
+ .INIT_F(INIT_F)
+ ) _TECHMAP_REPLACE_ (
+ .RDATA(RDATA),
+ .RCLKN(RCLK ),
+ .RCLKE(RCLKE),
+ .RE (RE ),
+ .RADDR(RADDR),
+ .WCLKN(WCLK ),
+ .WCLKE(WCLKE),
+ .WE (WE ),
+ .WADDR(WADDR),
+ .MASK (MASK ),
+ .WDATA(WDATA)
+ );
+ endcase
+ endgenerate
+endmodule
+
+
+module \$__ICE40_RAM4K_M0 (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
+ parameter [0:0] CLKPOL2 = 1;
+ parameter [0:0] CLKPOL3 = 1;
+
+ parameter [4095:0] INIT = 4096'bx;
+
+ input CLK2;
+ input CLK3;
+
+ input [7:0] A1ADDR;
+ output [15:0] A1DATA;
+ input A1EN;
+
+ input [7:0] B1ADDR;
+ input [15:0] B1DATA;
+ input [15:0] B1EN;
+
+ wire [10:0] A1ADDR_11 = A1ADDR;
+ wire [10:0] B1ADDR_11 = B1ADDR;
+
+ \$__ICE40_RAM4K #(
+ .READ_MODE(0),
+ .WRITE_MODE(0),
+ .NEGCLK_R(!CLKPOL2),
+ .NEGCLK_W(!CLKPOL3),
+ .INIT_0(INIT[ 0*256 +: 256]),
+ .INIT_1(INIT[ 1*256 +: 256]),
+ .INIT_2(INIT[ 2*256 +: 256]),
+ .INIT_3(INIT[ 3*256 +: 256]),
+ .INIT_4(INIT[ 4*256 +: 256]),
+ .INIT_5(INIT[ 5*256 +: 256]),
+ .INIT_6(INIT[ 6*256 +: 256]),
+ .INIT_7(INIT[ 7*256 +: 256]),
+ .INIT_8(INIT[ 8*256 +: 256]),
+ .INIT_9(INIT[ 9*256 +: 256]),
+ .INIT_A(INIT[10*256 +: 256]),
+ .INIT_B(INIT[11*256 +: 256]),
+ .INIT_C(INIT[12*256 +: 256]),
+ .INIT_D(INIT[13*256 +: 256]),
+ .INIT_E(INIT[14*256 +: 256]),
+ .INIT_F(INIT[15*256 +: 256])
+ ) _TECHMAP_REPLACE_ (
+ .RDATA(A1DATA),
+ .RADDR(A1ADDR_11),
+ .RCLK(CLK2),
+ .RCLKE(A1EN),
+ .RE(1'b1),
+ .WDATA(B1DATA),
+ .WADDR(B1ADDR_11),
+ .MASK(~B1EN),
+ .WCLK(CLK3),
+ .WCLKE(|B1EN),
+ .WE(1'b1)
+ );
+endmodule
+
+module \$__ICE40_RAM4K_M123 (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
+ parameter CFG_ABITS = 9;
+ parameter CFG_DBITS = 8;
+
+ parameter [0:0] CLKPOL2 = 1;
+ parameter [0:0] CLKPOL3 = 1;
+
+ parameter [4095:0] INIT = 4096'bx;
+
+ localparam MODE =
+ CFG_ABITS == 9 ? 1 :
+ CFG_ABITS == 10 ? 2 :
+ CFG_ABITS == 11 ? 3 : 'bx;
+
+ input CLK2;
+ input CLK3;
+
+ input [CFG_ABITS-1:0] A1ADDR;
+ output [CFG_DBITS-1:0] A1DATA;
+ input A1EN;
+
+ input [CFG_ABITS-1:0] B1ADDR;
+ input [CFG_DBITS-1:0] B1DATA;
+ input B1EN;
+
+ wire [10:0] A1ADDR_11 = A1ADDR;
+ wire [10:0] B1ADDR_11 = B1ADDR;
+
+ wire [15:0] A1DATA_16, B1DATA_16;
+
+ generate
+ if (MODE == 1) begin
+ assign A1DATA = {A1DATA_16[14], A1DATA_16[12], A1DATA_16[10], A1DATA_16[ 8],
+ A1DATA_16[ 6], A1DATA_16[ 4], A1DATA_16[ 2], A1DATA_16[ 0]};
+ assign {B1DATA_16[14], B1DATA_16[12], B1DATA_16[10], B1DATA_16[ 8],
+ B1DATA_16[ 6], B1DATA_16[ 4], B1DATA_16[ 2], B1DATA_16[ 0]} = B1DATA;
+ `include "brams_init1.vh"
+ end
+ if (MODE == 2) begin
+ assign A1DATA = {A1DATA_16[13], A1DATA_16[9], A1DATA_16[5], A1DATA_16[1]};
+ assign {B1DATA_16[13], B1DATA_16[9], B1DATA_16[5], B1DATA_16[1]} = B1DATA;
+ `include "brams_init2.vh"
+ end
+ if (MODE == 3) begin
+ assign A1DATA = {A1DATA_16[11], A1DATA_16[3]};
+ assign {B1DATA_16[11], B1DATA_16[3]} = B1DATA;
+ `include "brams_init3.vh"
+ end
+ endgenerate
+
+ \$__ICE40_RAM4K #(
+ .READ_MODE(MODE),
+ .WRITE_MODE(MODE),
+ .NEGCLK_R(!CLKPOL2),
+ .NEGCLK_W(!CLKPOL3),
+ .INIT_0(INIT_0),
+ .INIT_1(INIT_1),
+ .INIT_2(INIT_2),
+ .INIT_3(INIT_3),
+ .INIT_4(INIT_4),
+ .INIT_5(INIT_5),
+ .INIT_6(INIT_6),
+ .INIT_7(INIT_7),
+ .INIT_8(INIT_8),
+ .INIT_9(INIT_9),
+ .INIT_A(INIT_A),
+ .INIT_B(INIT_B),
+ .INIT_C(INIT_C),
+ .INIT_D(INIT_D),
+ .INIT_E(INIT_E),
+ .INIT_F(INIT_F)
+ ) _TECHMAP_REPLACE_ (
+ .RDATA(A1DATA_16),
+ .RADDR(A1ADDR_11),
+ .RCLK(CLK2),
+ .RCLKE(A1EN),
+ .RE(1'b1),
+ .WDATA(B1DATA_16),
+ .WADDR(B1ADDR_11),
+ .WCLK(CLK3),
+ .WCLKE(|B1EN),
+ .WE(1'b1)
+ );
+endmodule
+
diff --git a/techlibs/ice40/cells_map.v b/techlibs/ice40/cells_map.v
new file mode 100644
index 00000000..0227ffad
--- /dev/null
+++ b/techlibs/ice40/cells_map.v
@@ -0,0 +1,57 @@
+module \$_DFF_N_ (input D, C, output Q); SB_DFFN _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C)); endmodule
+module \$_DFF_P_ (input D, C, output Q); SB_DFF _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C)); endmodule
+
+module \$_DFFE_NN_ (input D, C, E, output Q); SB_DFFNE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(!E)); endmodule
+module \$_DFFE_PN_ (input D, C, E, output Q); SB_DFFE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(!E)); endmodule
+
+module \$_DFFE_NP_ (input D, C, E, output Q); SB_DFFNE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E)); endmodule
+module \$_DFFE_PP_ (input D, C, E, output Q); SB_DFFE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E)); endmodule
+
+module \$_DFF_NN0_ (input D, C, R, output Q); SB_DFFNR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(!R)); endmodule
+module \$_DFF_NN1_ (input D, C, R, output Q); SB_DFFNS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .S(!R)); endmodule
+module \$_DFF_PN0_ (input D, C, R, output Q); SB_DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(!R)); endmodule
+module \$_DFF_PN1_ (input D, C, R, output Q); SB_DFFS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .S(!R)); endmodule
+
+module \$_DFF_NP0_ (input D, C, R, output Q); SB_DFFNR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(R)); endmodule
+module \$_DFF_NP1_ (input D, C, R, output Q); SB_DFFNS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .S(R)); endmodule
+module \$_DFF_PP0_ (input D, C, R, output Q); SB_DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(R)); endmodule
+module \$_DFF_PP1_ (input D, C, R, output Q); SB_DFFS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .S(R)); endmodule
+
+module \$__DFFE_NN0 (input D, C, E, R, output Q); SB_DFFNER _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E), .R(!R)); endmodule
+module \$__DFFE_NN1 (input D, C, E, R, output Q); SB_DFFNES _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E), .S(!R)); endmodule
+module \$__DFFE_PN0 (input D, C, E, R, output Q); SB_DFFER _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E), .R(!R)); endmodule
+module \$__DFFE_PN1 (input D, C, E, R, output Q); SB_DFFES _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E), .S(!R)); endmodule
+
+module \$__DFFE_NP0 (input D, C, E, R, output Q); SB_DFFNER _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E), .R(R)); endmodule
+module \$__DFFE_NP1 (input D, C, E, R, output Q); SB_DFFNES _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E), .S(R)); endmodule
+module \$__DFFE_PP0 (input D, C, E, R, output Q); SB_DFFER _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E), .R(R)); endmodule
+module \$__DFFE_PP1 (input D, C, E, R, output Q); SB_DFFES _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E), .S(R)); endmodule
+
+module \$lut (A, Y);
+ parameter WIDTH = 0;
+ parameter LUT = 0;
+
+ input [WIDTH-1:0] A;
+ output Y;
+
+ generate
+ if (WIDTH == 1) begin
+ SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(A[0]), .I1(1'b0), .I2(1'b0), .I3(1'b0));
+ end else
+ if (WIDTH == 2) begin
+ SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(A[0]), .I1(A[1]), .I2(1'b0), .I3(1'b0));
+ end else
+ if (WIDTH == 3) begin
+ SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(1'b0));
+ end else
+ if (WIDTH == 4) begin
+ SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(A[3]));
+ end else begin
+ wire _TECHMAP_FAIL_ = 1;
+ end
+ endgenerate
+endmodule
diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v
new file mode 100644
index 00000000..7778b551
--- /dev/null
+++ b/techlibs/ice40/cells_sim.v
@@ -0,0 +1,883 @@
+
+`define SB_DFF_REG reg Q = 0;
+// `define SB_DFF_REG reg Q;
+
+// SiliconBlue IO Cells
+
+module SB_IO (
+ inout PACKAGE_PIN,
+ input LATCH_INPUT_VALUE,
+ input CLOCK_ENABLE,
+ input INPUT_CLK,
+ input OUTPUT_CLK,
+ input OUTPUT_ENABLE,
+ input D_OUT_0,
+ input D_OUT_1,
+ output D_IN_0,
+ output D_IN_1
+);
+ parameter [5:0] PIN_TYPE = 6'b000000;
+ parameter [0:0] PULLUP = 1'b0;
+ parameter [0:0] NEG_TRIGGER = 1'b0;
+ parameter IO_STANDARD = "SB_LVCMOS";
+
+`ifndef BLACKBOX
+ reg dout, din_0, din_1;
+ reg din_q_0, din_q_1;
+ reg dout_q_0, dout_q_1;
+ reg outena_q;
+
+ generate if (!NEG_TRIGGER) begin
+ always @(posedge INPUT_CLK) if (CLOCK_ENABLE) din_q_0 <= PACKAGE_PIN;
+ always @(negedge INPUT_CLK) if (CLOCK_ENABLE) din_q_1 <= PACKAGE_PIN;
+ always @(posedge OUTPUT_CLK) if (CLOCK_ENABLE) dout_q_0 <= D_OUT_0;
+ always @(negedge OUTPUT_CLK) if (CLOCK_ENABLE) dout_q_1 <= D_OUT_1;
+ always @(posedge OUTPUT_CLK) if (CLOCK_ENABLE) outena_q <= OUTPUT_ENABLE;
+ end else begin
+ always @(negedge INPUT_CLK) if (CLOCK_ENABLE) din_q_0 <= PACKAGE_PIN;
+ always @(posedge INPUT_CLK) if (CLOCK_ENABLE) din_q_1 <= PACKAGE_PIN;
+ always @(negedge OUTPUT_CLK) if (CLOCK_ENABLE) dout_q_0 <= D_OUT_0;
+ always @(posedge OUTPUT_CLK) if (CLOCK_ENABLE) dout_q_1 <= D_OUT_1;
+ always @(negedge OUTPUT_CLK) if (CLOCK_ENABLE) outena_q <= OUTPUT_ENABLE;
+ end endgenerate
+
+ always @* begin
+ if (!PIN_TYPE[1] || !LATCH_INPUT_VALUE)
+ din_0 = PIN_TYPE[0] ? PACKAGE_PIN : din_q_0;
+ din_1 = din_q_1;
+ end
+
+ // work around simulation glitches on dout in DDR mode
+ reg outclk_delayed_1;
+ reg outclk_delayed_2;
+ always @* outclk_delayed_1 <= OUTPUT_CLK;
+ always @* outclk_delayed_2 <= outclk_delayed_1;
+
+ always @* begin
+ if (PIN_TYPE[3])
+ dout = PIN_TYPE[2] ? !dout_q_0 : D_OUT_0;
+ else
+ dout = (outclk_delayed_2 ^ NEG_TRIGGER) || PIN_TYPE[2] ? dout_q_0 : dout_q_1;
+ end
+
+ assign D_IN_0 = din_0, D_IN_1 = din_1;
+
+ generate
+ if (PIN_TYPE[5:4] == 2'b01) assign PACKAGE_PIN = dout;
+ if (PIN_TYPE[5:4] == 2'b10) assign PACKAGE_PIN = OUTPUT_ENABLE ? dout : 1'bz;
+ if (PIN_TYPE[5:4] == 2'b11) assign PACKAGE_PIN = outena_q ? dout : 1'bz;
+ endgenerate
+`endif
+endmodule
+
+module SB_GB_IO (
+ inout PACKAGE_PIN,
+ output GLOBAL_BUFFER_OUTPUT,
+ input LATCH_INPUT_VALUE,
+ input CLOCK_ENABLE,
+ input INPUT_CLK,
+ input OUTPUT_CLK,
+ input OUTPUT_ENABLE,
+ input D_OUT_0,
+ input D_OUT_1,
+ output D_IN_0,
+ output D_IN_1
+);
+ parameter [5:0] PIN_TYPE = 6'b000000;
+ parameter [0:0] PULLUP = 1'b0;
+ parameter [0:0] NEG_TRIGGER = 1'b0;
+ parameter IO_STANDARD = "SB_LVCMOS";
+
+ assign GLOBAL_BUFFER_OUTPUT = PACKAGE_PIN;
+
+ SB_IO #(
+ .PIN_TYPE(PIN_TYPE),
+ .PULLUP(PULLUP),
+ .NEG_TRIGGER(NEG_TRIGGER),
+ .IO_STANDARD(IO_STANDARD)
+ ) IO (
+ .PACKAGE_PIN(PACKAGE_PIN),
+ .LATCH_INPUT_VALUE(LATCH_INPUT_VALUE),
+ .CLOCK_ENABLE(CLOCK_ENABLE),
+ .INPUT_CLK(INPUT_CLK),
+ .OUTPUT_CLK(OUTPUT_CLK),
+ .OUTPUT_ENABLE(OUTPUT_ENABLE),
+ .D_OUT_0(D_OUT_0),
+ .D_OUT_1(D_OUT_1),
+ .D_IN_0(D_IN_0),
+ .D_IN_1(D_IN_1)
+ );
+endmodule
+
+module SB_GB (
+ input USER_SIGNAL_TO_GLOBAL_BUFFER,
+ output GLOBAL_BUFFER_OUTPUT
+);
+ assign GLOBAL_BUFFER_OUTPUT = USER_SIGNAL_TO_GLOBAL_BUFFER;
+endmodule
+
+// SiliconBlue Logic Cells
+
+module SB_LUT4 (output O, input I0, I1, I2, I3);
+ parameter [15:0] LUT_INIT = 0;
+ wire [7:0] s3 = I3 ? LUT_INIT[15:8] : LUT_INIT[7:0];
+ wire [3:0] s2 = I2 ? s3[ 7:4] : s3[3:0];
+ wire [1:0] s1 = I1 ? s2[ 3:2] : s2[1:0];
+ assign O = I0 ? s1[1] : s1[0];
+endmodule
+
+module SB_CARRY (output CO, input I0, I1, CI);
+ assign CO = (I0 && I1) || ((I0 || I1) && CI);
+endmodule
+
+// Positive Edge SiliconBlue FF Cells
+
+module SB_DFF (output Q, input C, D);
+ `SB_DFF_REG
+ always @(posedge C)
+ Q <= D;
+endmodule
+
+module SB_DFFE (output Q, input C, E, D);
+ `SB_DFF_REG
+ always @(posedge C)
+ if (E)
+ Q <= D;
+endmodule
+
+module SB_DFFSR (output Q, input C, R, D);
+ `SB_DFF_REG
+ always @(posedge C)
+ if (R)
+ Q <= 0;
+ else
+ Q <= D;
+endmodule
+
+module SB_DFFR (output Q, input C, R, D);
+ `SB_DFF_REG
+ always @(posedge C, posedge R)
+ if (R)
+ Q <= 0;
+ else
+ Q <= D;
+endmodule
+
+module SB_DFFSS (output Q, input C, S, D);
+ `SB_DFF_REG
+ always @(posedge C)
+ if (S)
+ Q <= 1;
+ else
+ Q <= D;
+endmodule
+
+module SB_DFFS (output Q, input C, S, D);
+ `SB_DFF_REG
+ always @(posedge C, posedge S)
+ if (S)
+ Q <= 1;
+ else
+ Q <= D;
+endmodule
+
+module SB_DFFESR (output Q, input C, E, R, D);
+ `SB_DFF_REG
+ always @(posedge C)
+ if (E) begin
+ if (R)
+ Q <= 0;
+ else
+ Q <= D;
+ end
+endmodule
+
+module SB_DFFER (output Q, input C, E, R, D);
+ `SB_DFF_REG
+ always @(posedge C, posedge R)
+ if (R)
+ Q <= 0;
+ else if (E)
+ Q <= D;
+endmodule
+
+module SB_DFFESS (output Q, input C, E, S, D);
+ `SB_DFF_REG
+ always @(posedge C)
+ if (E) begin
+ if (S)
+ Q <= 1;
+ else
+ Q <= D;
+ end
+endmodule
+
+module SB_DFFES (output Q, input C, E, S, D);
+ `SB_DFF_REG
+ always @(posedge C, posedge S)
+ if (S)
+ Q <= 1;
+ else if (E)
+ Q <= D;
+endmodule
+
+// Negative Edge SiliconBlue FF Cells
+
+module SB_DFFN (output Q, input C, D);
+ `SB_DFF_REG
+ always @(negedge C)
+ Q <= D;
+endmodule
+
+module SB_DFFNE (output Q, input C, E, D);
+ `SB_DFF_REG
+ always @(negedge C)
+ if (E)
+ Q <= D;
+endmodule
+
+module SB_DFFNSR (output Q, input C, R, D);
+ `SB_DFF_REG
+ always @(negedge C)
+ if (R)
+ Q <= 0;
+ else
+ Q <= D;
+endmodule
+
+module SB_DFFNR (output Q, input C, R, D);
+ `SB_DFF_REG
+ always @(negedge C, posedge R)
+ if (R)
+ Q <= 0;
+ else
+ Q <= D;
+endmodule
+
+module SB_DFFNSS (output Q, input C, S, D);
+ `SB_DFF_REG
+ always @(negedge C)
+ if (S)
+ Q <= 1;
+ else
+ Q <= D;
+endmodule
+
+module SB_DFFNS (output Q, input C, S, D);
+ `SB_DFF_REG
+ always @(negedge C, posedge S)
+ if (S)
+ Q <= 1;
+ else
+ Q <= D;
+endmodule
+
+module SB_DFFNESR (output Q, input C, E, R, D);
+ `SB_DFF_REG
+ always @(negedge C)
+ if (E) begin
+ if (R)
+ Q <= 0;
+ else
+ Q <= D;
+ end
+endmodule
+
+module SB_DFFNER (output Q, input C, E, R, D);
+ `SB_DFF_REG
+ always @(negedge C, posedge R)
+ if (R)
+ Q <= 0;
+ else if (E)
+ Q <= D;
+endmodule
+
+module SB_DFFNESS (output Q, input C, E, S, D);
+ `SB_DFF_REG
+ always @(negedge C)
+ if (E) begin
+ if (S)
+ Q <= 1;
+ else
+ Q <= D;
+ end
+endmodule
+
+module SB_DFFNES (output Q, input C, E, S, D);
+ `SB_DFF_REG
+ always @(negedge C, posedge S)
+ if (S)
+ Q <= 1;
+ else if (E)
+ Q <= D;
+endmodule
+
+// SiliconBlue RAM Cells
+
+module SB_RAM40_4K (
+ output [15:0] RDATA,
+ input RCLK, RCLKE, RE,
+ input [10:0] RADDR,
+ input WCLK, WCLKE, WE,
+ input [10:0] WADDR,
+ input [15:0] MASK, WDATA
+);
+ // MODE 0: 256 x 16
+ // MODE 1: 512 x 8
+ // MODE 2: 1024 x 4
+ // MODE 3: 2048 x 2
+ parameter WRITE_MODE = 0;
+ parameter READ_MODE = 0;
+
+ parameter INIT_0 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_1 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_2 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_3 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_4 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_5 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_6 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_7 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_8 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_9 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+
+`ifndef BLACKBOX
+ wire [15:0] WMASK_I;
+ wire [15:0] RMASK_I;
+
+ reg [15:0] RDATA_I;
+ wire [15:0] WDATA_I;
+
+ generate
+ case (WRITE_MODE)
+ 0: assign WMASK_I = MASK;
+
+ 1: assign WMASK_I = WADDR[ 8] == 0 ? 16'b 1010_1010_1010_1010 :
+ WADDR[ 8] == 1 ? 16'b 0101_0101_0101_0101 : 16'bx;
+
+ 2: assign WMASK_I = WADDR[ 9:8] == 0 ? 16'b 1110_1110_1110_1110 :
+ WADDR[ 9:8] == 1 ? 16'b 1101_1101_1101_1101 :
+ WADDR[ 9:8] == 2 ? 16'b 1011_1011_1011_1011 :
+ WADDR[ 9:8] == 3 ? 16'b 0111_0111_0111_0111 : 16'bx;
+
+ 3: assign WMASK_I = WADDR[10:8] == 0 ? 16'b 1111_1110_1111_1110 :
+ WADDR[10:8] == 1 ? 16'b 1111_1101_1111_1101 :
+ WADDR[10:8] == 2 ? 16'b 1111_1011_1111_1011 :
+ WADDR[10:8] == 3 ? 16'b 1111_0111_1111_0111 :
+ WADDR[10:8] == 4 ? 16'b 1110_1111_1110_1111 :
+ WADDR[10:8] == 5 ? 16'b 1101_1111_1101_1111 :
+ WADDR[10:8] == 6 ? 16'b 1011_1111_1011_1111 :
+ WADDR[10:8] == 7 ? 16'b 0111_1111_0111_1111 : 16'bx;
+ endcase
+
+ case (READ_MODE)
+ 0: assign RMASK_I = 16'b 0000_0000_0000_0000;
+
+ 1: assign RMASK_I = RADDR[ 8] == 0 ? 16'b 1010_1010_1010_1010 :
+ RADDR[ 8] == 1 ? 16'b 0101_0101_0101_0101 : 16'bx;
+
+ 2: assign RMASK_I = RADDR[ 9:8] == 0 ? 16'b 1110_1110_1110_1110 :
+ RADDR[ 9:8] == 1 ? 16'b 1101_1101_1101_1101 :
+ RADDR[ 9:8] == 2 ? 16'b 1011_1011_1011_1011 :
+ RADDR[ 9:8] == 3 ? 16'b 0111_0111_0111_0111 : 16'bx;
+
+ 3: assign RMASK_I = RADDR[10:8] == 0 ? 16'b 1111_1110_1111_1110 :
+ RADDR[10:8] == 1 ? 16'b 1111_1101_1111_1101 :
+ RADDR[10:8] == 2 ? 16'b 1111_1011_1111_1011 :
+ RADDR[10:8] == 3 ? 16'b 1111_0111_1111_0111 :
+ RADDR[10:8] == 4 ? 16'b 1110_1111_1110_1111 :
+ RADDR[10:8] == 5 ? 16'b 1101_1111_1101_1111 :
+ RADDR[10:8] == 6 ? 16'b 1011_1111_1011_1111 :
+ RADDR[10:8] == 7 ? 16'b 0111_1111_0111_1111 : 16'bx;
+ endcase
+
+ case (WRITE_MODE)
+ 0: assign WDATA_I = WDATA;
+
+ 1: assign WDATA_I = {WDATA[14], WDATA[14], WDATA[12], WDATA[12],
+ WDATA[10], WDATA[10], WDATA[ 8], WDATA[ 8],
+ WDATA[ 6], WDATA[ 6], WDATA[ 4], WDATA[ 4],
+ WDATA[ 2], WDATA[ 2], WDATA[ 0], WDATA[ 0]};
+
+ 2: assign WDATA_I = {WDATA[13], WDATA[13], WDATA[13], WDATA[13],
+ WDATA[ 9], WDATA[ 9], WDATA[ 9], WDATA[ 9],
+ WDATA[ 5], WDATA[ 5], WDATA[ 5], WDATA[ 5],
+ WDATA[ 1], WDATA[ 1], WDATA[ 1], WDATA[ 1]};
+
+ 3: assign WDATA_I = {WDATA[11], WDATA[11], WDATA[11], WDATA[11],
+ WDATA[11], WDATA[11], WDATA[11], WDATA[11],
+ WDATA[ 3], WDATA[ 3], WDATA[ 3], WDATA[ 3],
+ WDATA[ 3], WDATA[ 3], WDATA[ 3], WDATA[ 3]};
+ endcase
+
+ case (READ_MODE)
+ 0: assign RDATA = RDATA_I;
+ 1: assign RDATA = {1'b0, |RDATA_I[15:14], 1'b0, |RDATA_I[13:12], 1'b0, |RDATA_I[11:10], 1'b0, |RDATA_I[ 9: 8],
+ 1'b0, |RDATA_I[ 7: 6], 1'b0, |RDATA_I[ 5: 4], 1'b0, |RDATA_I[ 3: 2], 1'b0, |RDATA_I[ 1: 0]};
+ 2: assign RDATA = {2'b0, |RDATA_I[15:12], 3'b0, |RDATA_I[11: 8], 3'b0, |RDATA_I[ 7: 4], 3'b0, |RDATA_I[ 3: 0], 1'b0};
+ 3: assign RDATA = {4'b0, |RDATA_I[15: 8], 7'b0, |RDATA_I[ 7: 0], 3'b0};
+ endcase
+ endgenerate
+
+ integer i;
+ reg [15:0] memory [0:255];
+
+ initial begin
+ for (i=0; i<16; i=i+1) begin
+ memory[ 0*16 + i] <= INIT_0[16*i +: 16];
+ memory[ 1*16 + i] <= INIT_1[16*i +: 16];
+ memory[ 2*16 + i] <= INIT_2[16*i +: 16];
+ memory[ 3*16 + i] <= INIT_3[16*i +: 16];
+ memory[ 4*16 + i] <= INIT_4[16*i +: 16];
+ memory[ 5*16 + i] <= INIT_5[16*i +: 16];
+ memory[ 6*16 + i] <= INIT_6[16*i +: 16];
+ memory[ 7*16 + i] <= INIT_7[16*i +: 16];
+ memory[ 8*16 + i] <= INIT_8[16*i +: 16];
+ memory[ 9*16 + i] <= INIT_9[16*i +: 16];
+ memory[10*16 + i] <= INIT_A[16*i +: 16];
+ memory[11*16 + i] <= INIT_B[16*i +: 16];
+ memory[12*16 + i] <= INIT_C[16*i +: 16];
+ memory[13*16 + i] <= INIT_D[16*i +: 16];
+ memory[14*16 + i] <= INIT_E[16*i +: 16];
+ memory[15*16 + i] <= INIT_F[16*i +: 16];
+ end
+ end
+
+ always @(posedge WCLK) begin
+ if (WE && WCLKE) begin
+ if (!WMASK_I[ 0]) memory[WADDR[7:0]][ 0] <= WDATA_I[ 0];
+ if (!WMASK_I[ 1]) memory[WADDR[7:0]][ 1] <= WDATA_I[ 1];
+ if (!WMASK_I[ 2]) memory[WADDR[7:0]][ 2] <= WDATA_I[ 2];
+ if (!WMASK_I[ 3]) memory[WADDR[7:0]][ 3] <= WDATA_I[ 3];
+ if (!WMASK_I[ 4]) memory[WADDR[7:0]][ 4] <= WDATA_I[ 4];
+ if (!WMASK_I[ 5]) memory[WADDR[7:0]][ 5] <= WDATA_I[ 5];
+ if (!WMASK_I[ 6]) memory[WADDR[7:0]][ 6] <= WDATA_I[ 6];
+ if (!WMASK_I[ 7]) memory[WADDR[7:0]][ 7] <= WDATA_I[ 7];
+ if (!WMASK_I[ 8]) memory[WADDR[7:0]][ 8] <= WDATA_I[ 8];
+ if (!WMASK_I[ 9]) memory[WADDR[7:0]][ 9] <= WDATA_I[ 9];
+ if (!WMASK_I[10]) memory[WADDR[7:0]][10] <= WDATA_I[10];
+ if (!WMASK_I[11]) memory[WADDR[7:0]][11] <= WDATA_I[11];
+ if (!WMASK_I[12]) memory[WADDR[7:0]][12] <= WDATA_I[12];
+ if (!WMASK_I[13]) memory[WADDR[7:0]][13] <= WDATA_I[13];
+ if (!WMASK_I[14]) memory[WADDR[7:0]][14] <= WDATA_I[14];
+ if (!WMASK_I[15]) memory[WADDR[7:0]][15] <= WDATA_I[15];
+ end
+ end
+
+ always @(posedge RCLK) begin
+ if (RE && RCLKE) begin
+ RDATA_I <= memory[RADDR[7:0]] & ~RMASK_I;
+ end
+ end
+`endif
+endmodule
+
+module SB_RAM40_4KNR (
+ output [15:0] RDATA,
+ input RCLKN, RCLKE, RE,
+ input [10:0] RADDR,
+ input WCLK, WCLKE, WE,
+ input [10:0] WADDR,
+ input [15:0] MASK, WDATA
+);
+ parameter WRITE_MODE = 0;
+ parameter READ_MODE = 0;
+
+ parameter INIT_0 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_1 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_2 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_3 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_4 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_5 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_6 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_7 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_8 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_9 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+
+ SB_RAM40_4K #(
+ .WRITE_MODE(WRITE_MODE),
+ .READ_MODE (READ_MODE ),
+ .INIT_0 (INIT_0 ),
+ .INIT_1 (INIT_1 ),
+ .INIT_2 (INIT_2 ),
+ .INIT_3 (INIT_3 ),
+ .INIT_4 (INIT_4 ),
+ .INIT_5 (INIT_5 ),
+ .INIT_6 (INIT_6 ),
+ .INIT_7 (INIT_7 ),
+ .INIT_8 (INIT_8 ),
+ .INIT_9 (INIT_9 ),
+ .INIT_A (INIT_A ),
+ .INIT_B (INIT_B ),
+ .INIT_C (INIT_C ),
+ .INIT_D (INIT_D ),
+ .INIT_E (INIT_E ),
+ .INIT_F (INIT_F )
+ ) RAM (
+ .RDATA(RDATA),
+ .RCLK (~RCLKN),
+ .RCLKE(RCLKE),
+ .RE (RE ),
+ .RADDR(RADDR),
+ .WCLK (WCLK ),
+ .WCLKE(WCLKE),
+ .WE (WE ),
+ .WADDR(WADDR),
+ .MASK (MASK ),
+ .WDATA(WDATA)
+ );
+endmodule
+
+module SB_RAM40_4KNW (
+ output [15:0] RDATA,
+ input RCLK, RCLKE, RE,
+ input [10:0] RADDR,
+ input WCLKN, WCLKE, WE,
+ input [10:0] WADDR,
+ input [15:0] MASK, WDATA
+);
+ parameter WRITE_MODE = 0;
+ parameter READ_MODE = 0;
+
+ parameter INIT_0 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_1 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_2 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_3 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_4 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_5 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_6 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_7 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_8 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_9 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+
+ SB_RAM40_4K #(
+ .WRITE_MODE(WRITE_MODE),
+ .READ_MODE (READ_MODE ),
+ .INIT_0 (INIT_0 ),
+ .INIT_1 (INIT_1 ),
+ .INIT_2 (INIT_2 ),
+ .INIT_3 (INIT_3 ),
+ .INIT_4 (INIT_4 ),
+ .INIT_5 (INIT_5 ),
+ .INIT_6 (INIT_6 ),
+ .INIT_7 (INIT_7 ),
+ .INIT_8 (INIT_8 ),
+ .INIT_9 (INIT_9 ),
+ .INIT_A (INIT_A ),
+ .INIT_B (INIT_B ),
+ .INIT_C (INIT_C ),
+ .INIT_D (INIT_D ),
+ .INIT_E (INIT_E ),
+ .INIT_F (INIT_F )
+ ) RAM (
+ .RDATA(RDATA),
+ .RCLK (RCLK ),
+ .RCLKE(RCLKE),
+ .RE (RE ),
+ .RADDR(RADDR),
+ .WCLK (~WCLKN),
+ .WCLKE(WCLKE),
+ .WE (WE ),
+ .WADDR(WADDR),
+ .MASK (MASK ),
+ .WDATA(WDATA)
+ );
+endmodule
+
+module SB_RAM40_4KNRNW (
+ output [15:0] RDATA,
+ input RCLKN, RCLKE, RE,
+ input [10:0] RADDR,
+ input WCLKN, WCLKE, WE,
+ input [10:0] WADDR,
+ input [15:0] MASK, WDATA
+);
+ parameter WRITE_MODE = 0;
+ parameter READ_MODE = 0;
+
+ parameter INIT_0 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_1 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_2 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_3 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_4 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_5 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_6 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_7 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_8 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_9 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+
+ SB_RAM40_4K #(
+ .WRITE_MODE(WRITE_MODE),
+ .READ_MODE (READ_MODE ),
+ .INIT_0 (INIT_0 ),
+ .INIT_1 (INIT_1 ),
+ .INIT_2 (INIT_2 ),
+ .INIT_3 (INIT_3 ),
+ .INIT_4 (INIT_4 ),
+ .INIT_5 (INIT_5 ),
+ .INIT_6 (INIT_6 ),
+ .INIT_7 (INIT_7 ),
+ .INIT_8 (INIT_8 ),
+ .INIT_9 (INIT_9 ),
+ .INIT_A (INIT_A ),
+ .INIT_B (INIT_B ),
+ .INIT_C (INIT_C ),
+ .INIT_D (INIT_D ),
+ .INIT_E (INIT_E ),
+ .INIT_F (INIT_F )
+ ) RAM (
+ .RDATA(RDATA),
+ .RCLK (~RCLKN),
+ .RCLKE(RCLKE),
+ .RE (RE ),
+ .RADDR(RADDR),
+ .WCLK (~WCLKN),
+ .WCLKE(WCLKE),
+ .WE (WE ),
+ .WADDR(WADDR),
+ .MASK (MASK ),
+ .WDATA(WDATA)
+ );
+endmodule
+
+// Packed IceStorm Logic Cells
+
+module ICESTORM_LC (
+ input I0, I1, I2, I3, CIN, CLK, CEN, SR,
+ output LO, O, COUT
+);
+ parameter [15:0] LUT_INIT = 0;
+
+ parameter [0:0] NEG_CLK = 0;
+ parameter [0:0] CARRY_ENABLE = 0;
+ parameter [0:0] DFF_ENABLE = 0;
+ parameter [0:0] SET_NORESET = 0;
+ parameter [0:0] ASYNC_SR = 0;
+
+ wire COUT = CARRY_ENABLE ? (I1 && I2) || ((I1 || I2) && CIN) : 1'bx;
+
+ wire [7:0] lut_s3 = I3 ? LUT_INIT[15:8] : LUT_INIT[7:0];
+ wire [3:0] lut_s2 = I2 ? lut_s3[ 7:4] : lut_s3[3:0];
+ wire [1:0] lut_s1 = I1 ? lut_s2[ 3:2] : lut_s2[1:0];
+ wire lut_o = I0 ? lut_s1[ 1] : lut_s1[ 0];
+
+ assign LO = lut_o;
+
+ wire polarized_clk;
+ assign polarized_clk = CLK ^ NEG_CLK;
+
+ reg o_reg;
+ always @(posedge polarized_clk)
+ if (CEN)
+ o_reg <= SR ? SET_NORESET : lut_o;
+
+ reg o_reg_async;
+ always @(posedge polarized_clk, posedge SR)
+ if (SR)
+ o_reg <= SET_NORESET;
+ else if (CEN)
+ o_reg <= lut_o;
+
+ assign O = DFF_ENABLE ? ASYNC_SR ? o_reg_async : o_reg : lut_o;
+endmodule
+
+// SiliconBlue PLL Cells
+
+(* blackbox *)
+module SB_PLL40_CORE (
+ input REFERENCECLK,
+ output PLLOUTCORE,
+ output PLLOUTGLOBAL,
+ input EXTFEEDBACK,
+ input [7:0] DYNAMICDELAY,
+ output LOCK,
+ input BYPASS,
+ input RESETB,
+ input LATCHINPUTVALUE,
+ output SDO,
+ input SDI,
+ input SCLK
+);
+ parameter FEEDBACK_PATH = "SIMPLE";
+ parameter DELAY_ADJUSTMENT_MODE_FEEDBACK = "FIXED";
+ parameter DELAY_ADJUSTMENT_MODE_RELATIVE = "FIXED";
+ parameter SHIFTREG_DIV_MODE = 1'b0;
+ parameter FDA_FEEDBACK = 4'b0000;
+ parameter FDA_RELATIVE = 4'b0000;
+ parameter PLLOUT_SELECT = "GENCLK";
+ parameter DIVR = 4'b0000;
+ parameter DIVF = 7'b0000000;
+ parameter DIVQ = 3'b000;
+ parameter FILTER_RANGE = 3'b000;
+ parameter ENABLE_ICEGATE = 1'b0;
+ parameter TEST_MODE = 1'b0;
+ parameter EXTERNAL_DIVIDE_FACTOR = 1;
+endmodule
+
+(* blackbox *)
+module SB_PLL40_PAD (
+ input PACKAGEPIN,
+ output PLLOUTCORE,
+ output PLLOUTGLOBAL,
+ input EXTFEEDBACK,
+ input [7:0] DYNAMICDELAY,
+ output LOCK,
+ input BYPASS,
+ input RESETB,
+ input LATCHINPUTVALUE,
+ output SDO,
+ input SDI,
+ input SCLK
+);
+ parameter FEEDBACK_PATH = "SIMPLE";
+ parameter DELAY_ADJUSTMENT_MODE_FEEDBACK = "FIXED";
+ parameter DELAY_ADJUSTMENT_MODE_RELATIVE = "FIXED";
+ parameter SHIFTREG_DIV_MODE = 1'b0;
+ parameter FDA_FEEDBACK = 4'b0000;
+ parameter FDA_RELATIVE = 4'b0000;
+ parameter PLLOUT_SELECT = "GENCLK";
+ parameter DIVR = 4'b0000;
+ parameter DIVF = 7'b0000000;
+ parameter DIVQ = 3'b000;
+ parameter FILTER_RANGE = 3'b000;
+ parameter ENABLE_ICEGATE = 1'b0;
+ parameter TEST_MODE = 1'b0;
+ parameter EXTERNAL_DIVIDE_FACTOR = 1;
+endmodule
+
+(* blackbox *)
+module SB_PLL40_2_PAD (
+ input PACKAGEPIN,
+ output PLLOUTCOREA,
+ output PLLOUTGLOBALA,
+ output PLLOUTCOREB,
+ output PLLOUTGLOBALB,
+ input EXTFEEDBACK,
+ input [7:0] DYNAMICDELAY,
+ output LOCK,
+ input BYPASS,
+ input RESETB,
+ input LATCHINPUTVALUE,
+ output SDO,
+ input SDI,
+ input SCLK
+);
+ parameter FEEDBACK_PATH = "SIMPLE";
+ parameter DELAY_ADJUSTMENT_MODE_FEEDBACK = "FIXED";
+ parameter DELAY_ADJUSTMENT_MODE_RELATIVE = "FIXED";
+ parameter SHIFTREG_DIV_MODE = 1'b0;
+ parameter FDA_FEEDBACK = 4'b0000;
+ parameter FDA_RELATIVE = 4'b0000;
+ parameter PLLOUT_SELECT_PORTB = "GENCLK";
+ parameter DIVR = 4'b0000;
+ parameter DIVF = 7'b0000000;
+ parameter DIVQ = 3'b000;
+ parameter FILTER_RANGE = 3'b000;
+ parameter ENABLE_ICEGATE_PORTA = 1'b0;
+ parameter ENABLE_ICEGATE_PORTB = 1'b0;
+ parameter TEST_MODE = 1'b0;
+ parameter EXTERNAL_DIVIDE_FACTOR = 1;
+endmodule
+
+(* blackbox *)
+module SB_PLL40_2F_CORE (
+ input REFERENCECLK,
+ output PLLOUTCOREA,
+ output PLLOUTGLOBALA,
+ output PLLOUTCOREB,
+ output PLLOUTGLOBALB,
+ input EXTFEEDBACK,
+ input [7:0] DYNAMICDELAY,
+ output LOCK,
+ input BYPASS,
+ input RESETB,
+ input LATCHINPUTVALUE,
+ output SDO,
+ input SDI,
+ input SCLK
+);
+ parameter FEEDBACK_PATH = "SIMPLE";
+ parameter DELAY_ADJUSTMENT_MODE_FEEDBACK = "FIXED";
+ parameter DELAY_ADJUSTMENT_MODE_RELATIVE = "FIXED";
+ parameter SHIFTREG_DIV_MODE = 1'b0;
+ parameter FDA_FEEDBACK = 4'b0000;
+ parameter FDA_RELATIVE = 4'b0000;
+ parameter PLLOUT_SELECT_PORTA = "GENCLK";
+ parameter PLLOUT_SELECT_PORTB = "GENCLK";
+ parameter DIVR = 4'b0000;
+ parameter DIVF = 7'b0000000;
+ parameter DIVQ = 3'b000;
+ parameter FILTER_RANGE = 3'b000;
+ parameter ENABLE_ICEGATE_PORTA = 1'b0;
+ parameter ENABLE_ICEGATE_PORTB = 1'b0;
+ parameter TEST_MODE = 1'b0;
+ parameter EXTERNAL_DIVIDE_FACTOR = 1;
+endmodule
+
+(* blackbox *)
+module SB_PLL40_2F_PAD (
+ input PACKAGEPIN,
+ output PLLOUTCOREA,
+ output PLLOUTGLOBALA,
+ output PLLOUTCOREB,
+ output PLLOUTGLOBALB,
+ input EXTFEEDBACK,
+ input [7:0] DYNAMICDELAY,
+ output LOCK,
+ input BYPASS,
+ input RESETB,
+ input LATCHINPUTVALUE,
+ output SDO,
+ input SDI,
+ input SCLK
+);
+ parameter FEEDBACK_PATH = "SIMPLE";
+ parameter DELAY_ADJUSTMENT_MODE_FEEDBACK = "FIXED";
+ parameter DELAY_ADJUSTMENT_MODE_RELATIVE = "FIXED";
+ parameter SHIFTREG_DIV_MODE = 2'b00;
+ parameter FDA_FEEDBACK = 4'b0000;
+ parameter FDA_RELATIVE = 4'b0000;
+ parameter PLLOUT_SELECT_PORTA = "GENCLK";
+ parameter PLLOUT_SELECT_PORTB = "GENCLK";
+ parameter DIVR = 4'b0000;
+ parameter DIVF = 7'b0000000;
+ parameter DIVQ = 3'b000;
+ parameter FILTER_RANGE = 3'b000;
+ parameter ENABLE_ICEGATE_PORTA = 1'b0;
+ parameter ENABLE_ICEGATE_PORTB = 1'b0;
+ parameter TEST_MODE = 1'b0;
+ parameter EXTERNAL_DIVIDE_FACTOR = 1;
+endmodule
+
+// SiliconBlue Device Configuration Cells
+
+(* blackbox, keep *)
+module SB_WARMBOOT (
+ input BOOT,
+ input S1,
+ input S0
+);
+endmodule
diff --git a/techlibs/ice40/ice40_ffinit.cc b/techlibs/ice40/ice40_ffinit.cc
new file mode 100644
index 00000000..c914b20e
--- /dev/null
+++ b/techlibs/ice40/ice40_ffinit.cc
@@ -0,0 +1,173 @@
+/*
+ * 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 Ice40FfinitPass : public Pass {
+ Ice40FfinitPass() : Pass("ice40_ffinit", "iCE40: handle FF init values") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" ice40_ffinit [options] [selection]\n");
+ log("\n");
+ log("Remove zero init values for FF output signals. Add inverters to implement\n");
+ log("nonzero init values.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing ICE40_FFINIT pass (implement FF init values).\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())
+ {
+ log("Handling FF init values in %s.\n", log_id(module));
+
+ SigMap sigmap(module);
+ pool<Wire*> init_wires;
+ dict<SigBit, State> initbits;
+ dict<SigBit, SigBit> initbit_to_wire;
+ pool<SigBit> handled_initbits;
+
+ for (auto wire : module->selected_wires())
+ {
+ if (wire->attributes.count("\\init") == 0)
+ continue;
+
+ SigSpec wirebits = sigmap(wire);
+ Const initval = wire->attributes.at("\\init");
+ init_wires.insert(wire);
+
+ for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++)
+ {
+ SigBit bit = wirebits[i];
+ State val = initval[i];
+
+ if (val != State::S0 && val != State::S1)
+ continue;
+
+ if (initbits.count(bit)) {
+ if (initbits.at(bit) != val)
+ log_error("Conflicting init values for signal %s (%s = %s, %s = %s).\n",
+ log_signal(bit), log_signal(SigBit(wire, i)), log_signal(val),
+ log_signal(initbit_to_wire[bit]), log_signal(initbits.at(bit)));
+ continue;
+ }
+
+ initbits[bit] = val;
+ initbit_to_wire[bit] = SigBit(wire, i);
+ }
+ }
+
+ pool<IdString> sb_dff_types = {
+ "\\SB_DFF", "\\SB_DFFE", "\\SB_DFFSR", "\\SB_DFFR", "\\SB_DFFSS", "\\SB_DFFS", "\\SB_DFFESR",
+ "\\SB_DFFER", "\\SB_DFFESS", "\\SB_DFFES", "\\SB_DFFN", "\\SB_DFFNE", "\\SB_DFFNSR", "\\SB_DFFNR",
+ "\\SB_DFFNSS", "\\SB_DFFNS", "\\SB_DFFNESR", "\\SB_DFFNER", "\\SB_DFFNESS", "\\SB_DFFNES"
+ };
+
+ for (auto cell : module->selected_cells())
+ {
+ if (!sb_dff_types.count(cell->type))
+ continue;
+
+ SigSpec sig_d = cell->getPort("\\D");
+ SigSpec sig_q = cell->getPort("\\Q");
+
+ if (GetSize(sig_d) < 1 || GetSize(sig_q) < 1)
+ continue;
+
+ SigBit bit_d = sigmap(sig_d[0]);
+ SigBit bit_q = sigmap(sig_q[0]);
+
+ if (!initbits.count(bit_q))
+ continue;
+
+ State val = initbits.at(bit_q);
+ handled_initbits.insert(bit_q);
+
+ log("FF init value for cell %s (%s): %s = %c\n", log_id(cell), log_id(cell->type),
+ log_signal(bit_q), val != State::S0 ? '1' : '0');
+
+ if (val == State::S0)
+ continue;
+
+ string type_str = cell->type.str();
+
+ if (type_str.back() == 'S') {
+ type_str.back() = 'R';
+ cell->type = type_str;
+ cell->setPort("\\R", cell->getPort("\\S"));
+ cell->unsetPort("\\S");
+ } else
+ if (type_str.back() == 'R') {
+ type_str.back() = 'S';
+ cell->type = type_str;
+ cell->setPort("\\S", cell->getPort("\\R"));
+ cell->unsetPort("\\R");
+ }
+
+ Wire *new_bit_d = module->addWire(NEW_ID);
+ Wire *new_bit_q = module->addWire(NEW_ID);
+
+ module->addNotGate(NEW_ID, bit_d, new_bit_d);
+ module->addNotGate(NEW_ID, new_bit_q, bit_q);
+
+ cell->setPort("\\D", new_bit_d);
+ cell->setPort("\\Q", new_bit_q);
+ }
+
+ for (auto wire : init_wires)
+ {
+ if (wire->attributes.count("\\init") == 0)
+ continue;
+
+ SigSpec wirebits = sigmap(wire);
+ Const &initval = wire->attributes.at("\\init");
+ bool remove_attribute = true;
+
+ for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++) {
+ if (handled_initbits.count(wirebits[i]))
+ initval[i] = State::Sx;
+ else if (initval[i] != State::Sx)
+ remove_attribute = false;
+ }
+
+ if (remove_attribute)
+ wire->attributes.erase("\\init");
+ }
+ }
+ }
+} Ice40FfinitPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/ice40/ice40_ffssr.cc b/techlibs/ice40/ice40_ffssr.cc
new file mode 100644
index 00000000..9afbc0fc
--- /dev/null
+++ b/techlibs/ice40/ice40_ffssr.cc
@@ -0,0 +1,128 @@
+/*
+ * 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 Ice40FfssrPass : public Pass {
+ Ice40FfssrPass() : Pass("ice40_ffssr", "iCE40: merge synchronous set/reset into FF cells") { }
+ virtual void help()
+ {
+ log("\n");
+ log(" ice40_ffssr [options] [selection]\n");
+ log("\n");
+ log("Merge synchronous set/reset $_MUX_ cells into iCE40 FFs.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing ICE40_FFSSR pass (merge synchronous set/reset into FF cells).\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);
+
+ pool<IdString> sb_dff_types;
+ sb_dff_types.insert("\\SB_DFF");
+ sb_dff_types.insert("\\SB_DFFE");
+ sb_dff_types.insert("\\SB_DFFN");
+ sb_dff_types.insert("\\SB_DFFNE");
+
+ for (auto module : design->selected_modules())
+ {
+ log("Merging set/reset $_MUX_ cells into SB_FFs in %s.\n", log_id(module));
+
+ SigMap sigmap(module);
+ dict<SigBit, Cell*> sr_muxes;
+ vector<Cell*> ff_cells;
+
+ for (auto cell : module->selected_cells())
+ {
+ if (sb_dff_types.count(cell->type)) {
+ ff_cells.push_back(cell);
+ continue;
+ }
+
+ if (cell->type != "$_MUX_")
+ continue;
+
+ SigBit bit_a = sigmap(cell->getPort("\\A"));
+ SigBit bit_b = sigmap(cell->getPort("\\B"));
+
+ if (bit_a.wire == nullptr || bit_b.wire == nullptr)
+ sr_muxes[sigmap(cell->getPort("\\Y"))] = cell;
+ }
+
+ for (auto cell : ff_cells)
+ {
+ SigSpec sig_d = cell->getPort("\\D");
+
+ if (GetSize(sig_d) < 1)
+ continue;
+
+ SigBit bit_d = sigmap(sig_d[0]);
+
+ if (sr_muxes.count(bit_d) == 0)
+ continue;
+
+ Cell *mux_cell = sr_muxes.at(bit_d);
+ SigBit bit_a = sigmap(mux_cell->getPort("\\A"));
+ SigBit bit_b = sigmap(mux_cell->getPort("\\B"));
+ SigBit bit_s = sigmap(mux_cell->getPort("\\S"));
+
+ log(" Merging %s (A=%s, B=%s, S=%s) into %s (%s).\n", log_id(mux_cell),
+ log_signal(bit_a), log_signal(bit_b), log_signal(bit_s), log_id(cell), log_id(cell->type));
+
+ SigBit sr_val, sr_sig;
+ if (bit_a.wire == nullptr) {
+ bit_d = bit_b;
+ sr_val = bit_a;
+ sr_sig = module->NotGate(NEW_ID, bit_s);
+ } else {
+ log_assert(bit_b.wire == nullptr);
+ bit_d = bit_a;
+ sr_val = bit_b;
+ sr_sig = bit_s;
+ }
+
+ if (sr_val == State::S1) {
+ cell->type = cell->type.str() + "SS";
+ cell->setPort("\\S", sr_sig);
+ cell->setPort("\\D", bit_d);
+ } else {
+ cell->type = cell->type.str() + "SR";
+ cell->setPort("\\R", sr_sig);
+ cell->setPort("\\D", bit_d);
+ }
+ }
+ }
+ }
+} Ice40FfssrPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/ice40/ice40_opt.cc b/techlibs/ice40/ice40_opt.cc
new file mode 100644
index 00000000..ae72f5d6
--- /dev/null
+++ b/techlibs/ice40/ice40_opt.cc
@@ -0,0 +1,193 @@
+/*
+ * 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/techmap/simplemap.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+static void run_ice40_opts(Module *module, bool unlut_mode)
+{
+ pool<SigBit> optimized_co;
+ vector<Cell*> sb_lut_cells;
+ SigMap sigmap(module);
+
+ for (auto cell : module->selected_cells())
+ {
+ if (cell->type == "\\SB_LUT4")
+ {
+ sb_lut_cells.push_back(cell);
+ continue;
+ }
+
+ if (cell->type == "\\SB_CARRY")
+ {
+ SigSpec non_const_inputs, replacement_output;
+ int count_zeros = 0, count_ones = 0;
+
+ SigBit inbit[3] = {cell->getPort("\\I0"), cell->getPort("\\I1"), cell->getPort("\\CI")};
+ for (int i = 0; i < 3; i++)
+ if (inbit[i].wire == nullptr) {
+ if (inbit[i] == State::S1)
+ count_ones++;
+ else
+ count_zeros++;
+ } else
+ non_const_inputs.append(inbit[i]);
+
+ if (count_zeros >= 2)
+ replacement_output = State::S0;
+ else if (count_ones >= 2)
+ replacement_output = State::S1;
+ else if (GetSize(non_const_inputs) == 1)
+ replacement_output = non_const_inputs;
+
+ if (GetSize(replacement_output)) {
+ optimized_co.insert(sigmap(cell->getPort("\\CO")));
+ module->connect(cell->getPort("\\CO"), replacement_output);
+ module->design->scratchpad_set_bool("opt.did_something", true);
+ log("Optimized away SB_CARRY cell %s.%s: CO=%s\n",
+ log_id(module), log_id(cell), log_signal(replacement_output));
+ module->remove(cell);
+ }
+ continue;
+ }
+ }
+
+ for (auto cell : sb_lut_cells)
+ {
+ SigSpec inbits;
+
+ inbits.append(cell->getPort("\\I0"));
+ inbits.append(cell->getPort("\\I1"));
+ inbits.append(cell->getPort("\\I2"));
+ inbits.append(cell->getPort("\\I3"));
+ sigmap.apply(inbits);
+
+ if (unlut_mode)
+ goto remap_lut;
+
+ if (optimized_co.count(inbits[0])) goto remap_lut;
+ if (optimized_co.count(inbits[1])) goto remap_lut;
+ if (optimized_co.count(inbits[2])) goto remap_lut;
+ if (optimized_co.count(inbits[3])) goto remap_lut;
+
+ if (!sigmap(inbits).is_fully_const())
+ continue;
+
+ remap_lut:
+ module->design->scratchpad_set_bool("opt.did_something", true);
+ log("Mapping SB_LUT4 cell %s.%s back to logic.\n", log_id(module), log_id(cell));
+
+ cell->type ="$lut";
+ cell->setParam("\\WIDTH", 4);
+ cell->setParam("\\LUT", cell->getParam("\\LUT_INIT"));
+ cell->unsetParam("\\LUT_INIT");
+
+ cell->setPort("\\A", SigSpec({cell->getPort("\\I3"), cell->getPort("\\I2"), cell->getPort("\\I1"), cell->getPort("\\I0")}));
+ cell->setPort("\\Y", cell->getPort("\\O"));
+ cell->unsetPort("\\I0");
+ cell->unsetPort("\\I1");
+ cell->unsetPort("\\I2");
+ cell->unsetPort("\\I3");
+ cell->unsetPort("\\O");
+
+ cell->check();
+ simplemap_lut(module, cell);
+ module->remove(cell);
+ }
+}
+
+struct Ice40OptPass : public Pass {
+ Ice40OptPass() : Pass("ice40_opt", "iCE40: perform simple optimizations") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" ice40_opt [options] [selection]\n");
+ log("\n");
+ log("This command executes the following script:\n");
+ log("\n");
+ log(" do\n");
+ log(" <ice40 specific optimizations>\n");
+ log(" opt_expr -mux_undef -undriven [-full]\n");
+ log(" opt_merge\n");
+ log(" opt_rmdff\n");
+ log(" opt_clean\n");
+ log(" while <changed design>\n");
+ log("\n");
+ log("When called with the option -unlut, this command will transform all already\n");
+ log("mapped SB_LUT4 cells back to logic.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ string opt_expr_args = "-mux_undef -undriven";
+ bool unlut_mode = false;
+
+ log_header(design, "Executing ICE40_OPT pass (performing simple optimizations).\n");
+ log_push();
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-full") {
+ opt_expr_args += " -full";
+ continue;
+ }
+ if (args[argidx] == "-unlut") {
+ unlut_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ while (1)
+ {
+ design->scratchpad_unset("opt.did_something");
+
+ log_header(design, "Running ICE40 specific optimizations.\n");
+ for (auto module : design->selected_modules())
+ run_ice40_opts(module, unlut_mode);
+
+ Pass::call(design, "opt_expr " + opt_expr_args);
+ Pass::call(design, "opt_merge");
+ Pass::call(design, "opt_rmdff");
+ Pass::call(design, "opt_clean");
+
+ if (design->scratchpad_get_bool("opt.did_something") == false)
+ break;
+
+ log_header(design, "Rerunning OPT passes. (Removed registers in this run.)\n");
+ }
+
+ design->optimize();
+ design->sort();
+ design->check();
+
+ log_header(design, "Finished OPT passes. (There is nothing left to do.)\n");
+ log_pop();
+ }
+} Ice40OptPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/ice40/latches_map.v b/techlibs/ice40/latches_map.v
new file mode 100644
index 00000000..c28f88cf
--- /dev/null
+++ b/techlibs/ice40/latches_map.v
@@ -0,0 +1,11 @@
+module \$_DLATCH_N_ (E, D, Q);
+ wire [1023:0] _TECHMAP_DO_ = "simplemap; opt";
+ input E, D;
+ output Q = !E ? D : Q;
+endmodule
+
+module \$_DLATCH_P_ (E, D, Q);
+ wire [1023:0] _TECHMAP_DO_ = "simplemap; opt";
+ input E, D;
+ output Q = E ? D : Q;
+endmodule
diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc
new file mode 100644
index 00000000..2533d3af
--- /dev/null
+++ b/techlibs/ice40/synth_ice40.cc
@@ -0,0 +1,250 @@
+/*
+ * 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/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct SynthIce40Pass : public ScriptPass
+{
+ SynthIce40Pass() : ScriptPass("synth_ice40", "synthesis for iCE40 FPGAs") { }
+
+ virtual void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" synth_ice40 [options]\n");
+ log("\n");
+ log("This command runs synthesis for iCE40 FPGAs.\n");
+ log("\n");
+ log(" -top <module>\n");
+ log(" use the specified module as top module (default='top')\n");
+ log("\n");
+ log(" -blif <file>\n");
+ log(" write the design to the specified BLIF file. writing of an output file\n");
+ log(" is omitted if this parameter is not specified.\n");
+ log("\n");
+ log(" -edif <file>\n");
+ log(" write the design to the specified edif file. writing of an output file\n");
+ log(" is omitted if this parameter is not specified.\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 'begin', and empty to label is\n");
+ log(" synonymous to the end of the command list.\n");
+ log("\n");
+ log(" -noflatten\n");
+ log(" do not flatten design before synthesis\n");
+ log("\n");
+ log(" -retime\n");
+ log(" run 'abc' with -dff option\n");
+ log("\n");
+ log(" -nocarry\n");
+ log(" do not use SB_CARRY cells in output netlist\n");
+ log("\n");
+ log(" -nobram\n");
+ log(" do not use SB_RAM40_4K* cells in output netlist\n");
+ log("\n");
+ log(" -abc2\n");
+ log(" run two passes of 'abc' for slightly improved logic density\n");
+ log("\n");
+ log("\n");
+ log("The following commands are executed by this synthesis command:\n");
+ help_script();
+ log("\n");
+ }
+
+ string top_opt, blif_file, edif_file;
+ bool nocarry, nobram, flatten, retime, abc2;
+
+ virtual void clear_flags() YS_OVERRIDE
+ {
+ top_opt = "-auto-top";
+ blif_file = "";
+ edif_file = "";
+ nocarry = false;
+ nobram = false;
+ flatten = true;
+ retime = false;
+ abc2 = false;
+ }
+
+ virtual 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] == "-top" && argidx+1 < args.size()) {
+ top_opt = "-top " + args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-blif" && argidx+1 < args.size()) {
+ blif_file = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-edif" && argidx+1 < args.size()) {
+ edif_file = args[++argidx];
+ continue;
+ }
+ 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] == "-flatten") {
+ flatten = true;
+ continue;
+ }
+ if (args[argidx] == "-noflatten") {
+ flatten = false;
+ continue;
+ }
+ if (args[argidx] == "-retime") {
+ retime = true;
+ continue;
+ }
+ if (args[argidx] == "-nocarry") {
+ nocarry = true;
+ continue;
+ }
+ if (args[argidx] == "-nobram") {
+ nobram = true;
+ continue;
+ }
+ if (args[argidx] == "-abc2") {
+ abc2 = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!design->full_selection())
+ log_cmd_error("This comannd only operates on fully selected designs!\n");
+
+ log_header(design, "Executing SYNTH_ICE40 pass.\n");
+ log_push();
+
+ run_script(design, run_from, run_to);
+
+ log_pop();
+ }
+
+ virtual void script() YS_OVERRIDE
+ {
+ if (check_label("begin"))
+ {
+ run("read_verilog -lib +/ice40/cells_sim.v");
+ run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
+ }
+
+ if (flatten && check_label("flatten", "(unless -noflatten)"))
+ {
+ run("proc");
+ run("flatten");
+ run("tribuf -logic");
+ run("deminout");
+ }
+
+ if (check_label("coarse"))
+ {
+ run("synth -run coarse");
+ }
+
+ if (!nobram && check_label("bram", "(skip if -nobram)"))
+ {
+ run("memory_bram -rules +/ice40/brams.txt");
+ run("techmap -map +/ice40/brams_map.v");
+ }
+
+ if (check_label("fine"))
+ {
+ run("opt -fast -mux_undef -undriven -fine");
+ run("memory_map");
+ run("opt -undriven -fine");
+ if (nocarry)
+ run("techmap");
+ else
+ run("techmap -map +/techmap.v -map +/ice40/arith_map.v");
+ if (retime || help_mode)
+ run("abc -dff", "(only if -retime)");
+ run("ice40_opt");
+ }
+
+ if (check_label("map_ffs"))
+ {
+ run("dffsr2dff");
+ run("dff2dffe -direct-match $_DFF_*");
+ run("techmap -map +/ice40/cells_map.v");
+ run("opt_expr -mux_undef");
+ run("simplemap");
+ run("ice40_ffinit");
+ run("ice40_ffssr");
+ run("ice40_opt -full");
+ }
+
+ if (check_label("map_luts"))
+ {
+ if (abc2 || help_mode) {
+ run("abc", " (only if -abc2)");
+ run("ice40_opt", "(only if -abc2)");
+ }
+ run("techmap -map +/ice40/latches_map.v");
+ run("abc -lut 4");
+ run("clean");
+ }
+
+ if (check_label("map_cells"))
+ {
+ run("techmap -map +/ice40/cells_map.v");
+ run("clean");
+ }
+
+ if (check_label("check"))
+ {
+ run("hierarchy -check");
+ run("stat");
+ run("check -noinit");
+ }
+
+ if (check_label("blif"))
+ {
+ if (!blif_file.empty() || help_mode)
+ run(stringf("write_blif -gates -attr -param %s", help_mode ? "<file-name>" : blif_file.c_str()));
+ }
+
+ if (check_label("edif"))
+ {
+ if (!edif_file.empty() || help_mode)
+ run(stringf("write_edif %s", help_mode ? "<file-name>" : edif_file.c_str()));
+ }
+ }
+} SynthIce40Pass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/ice40/tests/.gitignore b/techlibs/ice40/tests/.gitignore
new file mode 100644
index 00000000..b58f9ad4
--- /dev/null
+++ b/techlibs/ice40/tests/.gitignore
@@ -0,0 +1,2 @@
+test_ffs_[01][01][01][01][01]_*
+test_bram_[0-9]*
diff --git a/techlibs/ice40/tests/test_arith.v b/techlibs/ice40/tests/test_arith.v
new file mode 100644
index 00000000..77f79b97
--- /dev/null
+++ b/techlibs/ice40/tests/test_arith.v
@@ -0,0 +1,3 @@
+module test(input [4:0] a, b, c, output [4:0] y);
+ assign y = ((a+b) ^ (a-c)) - ((a*b) + (a*c) - (b*c));
+endmodule
diff --git a/techlibs/ice40/tests/test_arith.ys b/techlibs/ice40/tests/test_arith.ys
new file mode 100644
index 00000000..160c767f
--- /dev/null
+++ b/techlibs/ice40/tests/test_arith.ys
@@ -0,0 +1,10 @@
+read_verilog test_arith.v
+synth_ice40
+techmap -map ../cells_sim.v
+rename test gate
+
+read_verilog test_arith.v
+rename test gold
+
+miter -equiv -flatten -make_outputs gold gate miter
+sat -verify -prove trigger 0 -show-ports miter
diff --git a/techlibs/ice40/tests/test_bram.sh b/techlibs/ice40/tests/test_bram.sh
new file mode 100644
index 00000000..d4d641a9
--- /dev/null
+++ b/techlibs/ice40/tests/test_bram.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+set -ex
+
+for abits in 7 8 9 10 11 12; do
+for dbits in 2 4 8 16 24 32; do
+ id="test_bram_${abits}_${dbits}"
+ iadr=$((RANDOM % (1 << abits)))
+ idat=$((RANDOM % ((1 << dbits) - 1) + 1))
+ sed -re "s/(ABITS = )0/\1$abits/g; s/(DBITS = )0/\1$dbits/g; s/(INIT_ADDR = )0/\1$iadr/g; s/(INIT_DATA = )0/\1$idat/g;" < test_bram.v > ${id}.v
+ sed -re "s/(ABITS = )0/\1$abits/g; s/(DBITS = )0/\1$dbits/g; s/(INIT_ADDR = )0/\1$iadr/g; s/(INIT_DATA = )0/\1$idat/g;" < test_bram_tb.v > ${id}_tb.v
+ ../../../yosys -ql ${id}_syn.log -p "synth_ice40" -o ${id}_syn.v ${id}.v
+ # iverilog -s bram_tb -o ${id}_tb ${id}_syn.v ${id}_tb.v /opt/lscc/iCEcube2.2014.08/verilog/sb_ice_syn.v
+ iverilog -s bram_tb -o ${id}_tb ${id}_syn.v ${id}_tb.v ../cells_sim.v
+ ./${id}_tb > ${id}_tb.txt
+ if grep -H ERROR ${id}_tb.txt; then false; fi
+done; done
+echo OK
+
diff --git a/techlibs/ice40/tests/test_bram.v b/techlibs/ice40/tests/test_bram.v
new file mode 100644
index 00000000..320735d0
--- /dev/null
+++ b/techlibs/ice40/tests/test_bram.v
@@ -0,0 +1,24 @@
+module bram #(
+ parameter ABITS = 8, DBITS = 8,
+ parameter INIT_ADDR = 0, INIT_DATA = 0
+) (
+ input clk,
+
+ input [ABITS-1:0] WR_ADDR,
+ input [DBITS-1:0] WR_DATA,
+ input WR_EN,
+
+ input [ABITS-1:0] RD_ADDR,
+ output reg [DBITS-1:0] RD_DATA
+);
+ reg [DBITS-1:0] memory [0:2**ABITS-1];
+
+ initial begin
+ memory[INIT_ADDR] <= INIT_DATA;
+ end
+
+ always @(posedge clk) begin
+ if (WR_EN) memory[WR_ADDR] <= WR_DATA;
+ RD_DATA <= memory[RD_ADDR];
+ end
+endmodule
diff --git a/techlibs/ice40/tests/test_bram_tb.v b/techlibs/ice40/tests/test_bram_tb.v
new file mode 100644
index 00000000..bdb8d456
--- /dev/null
+++ b/techlibs/ice40/tests/test_bram_tb.v
@@ -0,0 +1,110 @@
+module bram_tb #(
+ parameter ABITS = 8, DBITS = 8,
+ parameter INIT_ADDR = 0, INIT_DATA = 0
+);
+ reg clk;
+ reg [ABITS-1:0] WR_ADDR;
+ reg [DBITS-1:0] WR_DATA;
+ reg WR_EN;
+ reg [ABITS-1:0] RD_ADDR;
+ wire [DBITS-1:0] RD_DATA;
+
+ bram uut (
+ .clk (clk ),
+ .WR_ADDR(WR_ADDR),
+ .WR_DATA(WR_DATA),
+ .WR_EN (WR_EN ),
+ .RD_ADDR(RD_ADDR),
+ .RD_DATA(RD_DATA)
+ );
+
+ reg [63:0] xorshift64_state = 64'd88172645463325252 ^ (ABITS << 24) ^ (DBITS << 16);
+
+ task xorshift64_next;
+ begin
+ // see page 4 of Marsaglia, George (July 2003). "Xorshift RNGs". Journal of Statistical Software 8 (14).
+ xorshift64_state = xorshift64_state ^ (xorshift64_state << 13);
+ xorshift64_state = xorshift64_state ^ (xorshift64_state >> 7);
+ xorshift64_state = xorshift64_state ^ (xorshift64_state << 17);
+ end
+ endtask
+
+ reg [ABITS-1:0] randaddr1;
+ reg [ABITS-1:0] randaddr2;
+ reg [ABITS-1:0] randaddr3;
+
+ function [31:0] getaddr(input [3:0] n);
+ begin
+ case (n)
+ 0: getaddr = 0;
+ 1: getaddr = 2**ABITS-1;
+ 2: getaddr = 'b101 << (ABITS / 3);
+ 3: getaddr = 'b101 << (2*ABITS / 3);
+ 4: getaddr = 'b11011 << (ABITS / 4);
+ 5: getaddr = 'b11011 << (2*ABITS / 4);
+ 6: getaddr = 'b11011 << (3*ABITS / 4);
+ 7: getaddr = randaddr1;
+ 8: getaddr = randaddr2;
+ 9: getaddr = randaddr3;
+ default: begin
+ getaddr = 1 << (2*n-16);
+ if (!getaddr) getaddr = xorshift64_state;
+ end
+ endcase
+ end
+ endfunction
+
+ reg [DBITS-1:0] memory [0:2**ABITS-1];
+ reg [DBITS-1:0] expected_rd, expected_rd_masked;
+
+ event error;
+ integer i, j;
+
+ initial begin
+ // $dumpfile("testbench.vcd");
+ // $dumpvars(0, bram_tb);
+
+ memory[INIT_ADDR] <= INIT_DATA;
+
+ xorshift64_next;
+ xorshift64_next;
+ xorshift64_next;
+ xorshift64_next;
+
+ randaddr1 = xorshift64_state;
+ xorshift64_next;
+
+ randaddr2 = xorshift64_state;
+ xorshift64_next;
+
+ randaddr3 = xorshift64_state;
+ xorshift64_next;
+
+ clk <= 0;
+ for (i = 0; i < 512; i = i+1) begin
+ WR_DATA = xorshift64_state;
+ xorshift64_next;
+
+ WR_ADDR = getaddr(i < 256 ? i[7:4] : xorshift64_state[63:60]);
+ xorshift64_next;
+
+ RD_ADDR = i == 0 ? INIT_ADDR : getaddr(i < 256 ? i[3:0] : xorshift64_state[59:56]);
+ WR_EN = xorshift64_state[55] && ((WR_ADDR & 'hff) != (RD_ADDR & 'hff));
+ xorshift64_next;
+
+ #1; clk <= 1;
+ #1; clk <= 0;
+
+ expected_rd = memory[RD_ADDR];
+ if (WR_EN) memory[WR_ADDR] = WR_DATA;
+
+ for (j = 0; j < DBITS; j = j+1)
+ expected_rd_masked[j] = expected_rd[j] !== 1'bx ? expected_rd[j] : RD_DATA[j];
+
+ $display("#OUT# %3d | WA=%x WD=%x WE=%x | RA=%x RD=%x (%x) | %s",
+ i, WR_ADDR, WR_DATA, WR_EN, RD_ADDR, RD_DATA, expected_rd,
+ expected_rd_masked === RD_DATA ? "ok" : "ERROR");
+ if (expected_rd_masked !== RD_DATA) begin -> error; end
+ end
+ end
+endmodule
diff --git a/techlibs/ice40/tests/test_ffs.sh b/techlibs/ice40/tests/test_ffs.sh
new file mode 100644
index 00000000..ff79ec53
--- /dev/null
+++ b/techlibs/ice40/tests/test_ffs.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+set -ex
+for CLKPOL in 0 1; do
+for ENABLE_EN in 0 1; do
+for RESET_EN in 0 1; do
+for RESET_VAL in 0 1; do
+for RESET_SYN in 0 1; do
+ pf="test_ffs_${CLKPOL}${ENABLE_EN}${RESET_EN}${RESET_VAL}${RESET_SYN}"
+ sed -e "s/CLKPOL = 0/CLKPOL = ${CLKPOL}/;" -e "s/ENABLE_EN = 0/ENABLE_EN = ${ENABLE_EN}/;" \
+ -e "s/RESET_EN = 0/RESET_EN = ${RESET_EN}/;" -e "s/RESET_VAL = 0/RESET_VAL = ${RESET_VAL}/;" \
+ -e "s/RESET_SYN = 0/RESET_SYN = ${RESET_SYN}/;" test_ffs.v > ${pf}_gold.v
+ ../../../yosys -o ${pf}_gate.v -p "synth_ice40" ${pf}_gold.v
+ ../../../yosys -p "proc; opt; test_autotb ${pf}_tb.v" ${pf}_gold.v
+ iverilog -s testbench -o ${pf}_gold ${pf}_gold.v ${pf}_tb.v
+ iverilog -s testbench -o ${pf}_gate ${pf}_gate.v ${pf}_tb.v ../cells_sim.v
+ ./${pf}_gold > ${pf}_gold.txt
+ ./${pf}_gate > ${pf}_gate.txt
+ cmp ${pf}_gold.txt ${pf}_gate.txt
+done; done; done; done; done
+echo OK.
diff --git a/techlibs/ice40/tests/test_ffs.v b/techlibs/ice40/tests/test_ffs.v
new file mode 100644
index 00000000..1f6883f3
--- /dev/null
+++ b/techlibs/ice40/tests/test_ffs.v
@@ -0,0 +1,42 @@
+module test(D, C, E, R, Q);
+ parameter [0:0] CLKPOL = 0;
+ parameter [0:0] ENABLE_EN = 0;
+ parameter [0:0] RESET_EN = 0;
+ parameter [0:0] RESET_VAL = 0;
+ parameter [0:0] RESET_SYN = 0;
+
+ (* gentb_clock *)
+ input D, C, E, R;
+
+ output Q;
+
+ wire gated_reset = R & RESET_EN;
+ wire gated_enable = E | ~ENABLE_EN;
+ reg posedge_q, negedge_q, posedge_sq, negedge_sq;
+
+ always @(posedge C, posedge gated_reset)
+ if (gated_reset)
+ posedge_q <= RESET_VAL;
+ else if (gated_enable)
+ posedge_q <= D;
+
+ always @(negedge C, posedge gated_reset)
+ if (gated_reset)
+ negedge_q <= RESET_VAL;
+ else if (gated_enable)
+ negedge_q <= D;
+
+ always @(posedge C)
+ if (gated_reset)
+ posedge_sq <= RESET_VAL;
+ else if (gated_enable)
+ posedge_sq <= D;
+
+ always @(negedge C)
+ if (gated_reset)
+ negedge_sq <= RESET_VAL;
+ else if (gated_enable)
+ negedge_sq <= D;
+
+ assign Q = RESET_SYN ? (CLKPOL ? posedge_sq : negedge_sq) : (CLKPOL ? posedge_q : negedge_q);
+endmodule
diff --git a/techlibs/xilinx/.gitignore b/techlibs/xilinx/.gitignore
new file mode 100644
index 00000000..d127107d
--- /dev/null
+++ b/techlibs/xilinx/.gitignore
@@ -0,0 +1,2 @@
+brams_init.mk
+brams_init_*.vh
diff --git a/techlibs/xilinx/Makefile.inc b/techlibs/xilinx/Makefile.inc
new file mode 100644
index 00000000..5f09ffb0
--- /dev/null
+++ b/techlibs/xilinx/Makefile.inc
@@ -0,0 +1,37 @@
+
+OBJS += techlibs/xilinx/synth_xilinx.o
+
+GENFILES += techlibs/xilinx/brams_init_36.vh
+GENFILES += techlibs/xilinx/brams_init_32.vh
+GENFILES += techlibs/xilinx/brams_init_18.vh
+GENFILES += techlibs/xilinx/brams_init_16.vh
+
+EXTRA_OBJS += techlibs/xilinx/brams_init.mk
+.SECONDARY: techlibs/xilinx/brams_init.mk
+
+techlibs/xilinx/brams_init.mk: techlibs/xilinx/brams_init.py
+ $(Q) mkdir -p techlibs/xilinx
+ $(P) python3 $<
+ $(Q) touch $@
+
+techlibs/xilinx/brams_init_36.vh: techlibs/xilinx/brams_init.mk
+techlibs/xilinx/brams_init_32.vh: techlibs/xilinx/brams_init.mk
+techlibs/xilinx/brams_init_18.vh: techlibs/xilinx/brams_init.mk
+techlibs/xilinx/brams_init_16.vh: techlibs/xilinx/brams_init.mk
+
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_map.v))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_sim.v))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_xtra.v))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/brams.txt))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/brams_map.v))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/brams_bb.v))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/drams.txt))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/drams_map.v))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/drams_bb.v))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/arith_map.v))
+
+$(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_36.vh))
+$(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_32.vh))
+$(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_18.vh))
+$(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_16.vh))
+
diff --git a/techlibs/xilinx/arith_map.v b/techlibs/xilinx/arith_map.v
new file mode 100644
index 00000000..03719659
--- /dev/null
+++ b/techlibs/xilinx/arith_map.v
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ *
+ */
+
+(* techmap_celltype = "$lcu" *)
+module _80_xilinx_lcu (P, G, CI, CO);
+ parameter WIDTH = 2;
+
+ input [WIDTH-1:0] P, G;
+ input CI;
+
+ output [WIDTH-1:0] CO;
+
+ wire _TECHMAP_FAIL_ = WIDTH <= 2;
+
+ wire [WIDTH-1:0] C = {CO, CI};
+ wire [WIDTH-1:0] S = P & ~G;
+
+ genvar i;
+ generate for (i = 0; i < WIDTH; i = i + 1) begin:slice
+ MUXCY muxcy (
+ .CI(C[i]),
+ .DI(G[i]),
+ .S(S[i]),
+ .O(CO[i])
+ );
+ end endgenerate
+endmodule
+
+(* techmap_celltype = "$alu" *)
+module _80_xilinx_alu (A, B, CI, BI, X, Y, CO);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] X, Y;
+
+ input CI, BI;
+ output [Y_WIDTH-1:0] CO;
+
+ wire _TECHMAP_FAIL_ = Y_WIDTH <= 2;
+
+ wire [Y_WIDTH-1:0] A_buf, B_buf;
+ \$pos #(.A_SIGNED(A_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(Y_WIDTH)) A_conv (.A(A), .Y(A_buf));
+ \$pos #(.A_SIGNED(B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) B_conv (.A(B), .Y(B_buf));
+
+ wire [Y_WIDTH-1:0] AA = A_buf;
+ wire [Y_WIDTH-1:0] BB = BI ? ~B_buf : B_buf;
+
+ wire [Y_WIDTH-1:0] P = AA ^ BB;
+ wire [Y_WIDTH-1:0] G = AA & BB;
+ wire [Y_WIDTH-1:0] C = {CO, CI};
+ wire [Y_WIDTH-1:0] S = P & ~G;
+
+ genvar i;
+ generate for (i = 0; i < Y_WIDTH; i = i + 1) begin:slice
+ MUXCY muxcy (
+ .CI(C[i]),
+ .DI(G[i]),
+ .S(S[i]),
+ .O(CO[i])
+ );
+ XORCY xorcy (
+ .CI(C[i]),
+ .LI(S[i]),
+ .O(Y[i])
+ );
+ end endgenerate
+
+ assign X = P;
+endmodule
+
diff --git a/techlibs/xilinx/brams.txt b/techlibs/xilinx/brams.txt
new file mode 100644
index 00000000..f1161114
--- /dev/null
+++ b/techlibs/xilinx/brams.txt
@@ -0,0 +1,105 @@
+
+bram $__XILINX_RAMB36_SDP
+ init 1
+ abits 9
+ dbits 72
+ groups 2
+ ports 1 1
+ wrmode 0 1
+ enable 1 8
+ transp 0 0
+ clocks 2 3
+ clkpol 2 3
+endbram
+
+bram $__XILINX_RAMB18_SDP
+ init 1
+ abits 9
+ dbits 36
+ groups 2
+ ports 1 1
+ wrmode 0 1
+ enable 1 4
+ transp 0 0
+ clocks 2 3
+ clkpol 2 3
+endbram
+
+bram $__XILINX_RAMB36_TDP
+ init 1
+ abits 10 @a10d36
+ dbits 36 @a10d36
+ abits 11 @a11d18
+ dbits 18 @a11d18
+ abits 12 @a12d9
+ dbits 9 @a12d9
+ abits 13 @a13d4
+ dbits 4 @a13d4
+ abits 14 @a14d2
+ dbits 2 @a14d2
+ abits 15 @a15d1
+ dbits 1 @a15d1
+ groups 2
+ ports 1 1
+ wrmode 0 1
+ enable 1 4 @a10d36
+ enable 1 2 @a11d18
+ enable 1 1 @a12d9 @a13d4 @a14d2 @a15d1
+ transp 0 0
+ clocks 2 3
+ clkpol 2 3
+endbram
+
+bram $__XILINX_RAMB18_TDP
+ init 1
+ abits 10 @a10d18
+ dbits 18 @a10d18
+ abits 11 @a11d9
+ dbits 9 @a11d9
+ abits 12 @a12d4
+ dbits 4 @a12d4
+ abits 13 @a13d2
+ dbits 2 @a13d2
+ abits 14 @a14d1
+ dbits 1 @a14d1
+ groups 2
+ ports 1 1
+ wrmode 0 1
+ enable 1 2 @a10d18
+ enable 1 1 @a11d9 @a12d4 @a13d2 @a14d1
+ transp 0 0
+ clocks 2 3
+ clkpol 2 3
+endbram
+
+match $__XILINX_RAMB36_SDP
+ min bits 4096
+ min efficiency 5
+ shuffle_enable B
+ make_transp
+ or_next_if_better
+endmatch
+
+match $__XILINX_RAMB18_SDP
+ min bits 4096
+ min efficiency 5
+ shuffle_enable B
+ make_transp
+ or_next_if_better
+endmatch
+
+match $__XILINX_RAMB36_TDP
+ min bits 4096
+ min efficiency 5
+ shuffle_enable B
+ make_transp
+ or_next_if_better
+endmatch
+
+match $__XILINX_RAMB18_TDP
+ min bits 4096
+ min efficiency 5
+ shuffle_enable B
+ make_transp
+endmatch
+
diff --git a/techlibs/xilinx/brams_bb.v b/techlibs/xilinx/brams_bb.v
new file mode 100644
index 00000000..a682ba4a
--- /dev/null
+++ b/techlibs/xilinx/brams_bb.v
@@ -0,0 +1,319 @@
+module RAMB18E1 (
+ input CLKARDCLK,
+ input CLKBWRCLK,
+ input ENARDEN,
+ input ENBWREN,
+ input REGCEAREGCE,
+ input REGCEB,
+ input RSTRAMARSTRAM,
+ input RSTRAMB,
+ input RSTREGARSTREG,
+ input RSTREGB,
+
+ input [13:0] ADDRARDADDR,
+ input [13:0] ADDRBWRADDR,
+ input [15:0] DIADI,
+ input [15:0] DIBDI,
+ input [1:0] DIPADIP,
+ input [1:0] DIPBDIP,
+ input [1:0] WEA,
+ input [3:0] WEBWE,
+
+ output [15:0] DOADO,
+ output [15:0] DOBDO,
+ output [1:0] DOPADOP,
+ output [1:0] DOPBDOP
+);
+ parameter INITP_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_01 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_02 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_03 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_04 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_05 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_06 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_07 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+
+ parameter INIT_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_01 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_02 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_03 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_04 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_05 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_06 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_07 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_08 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_09 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_0A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_0B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_0C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_0D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_0E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_0F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_10 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_11 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_12 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_13 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_14 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_15 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_16 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_17 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_18 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_19 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_1A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_1B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_1C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_1D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_1E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_1F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_20 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_21 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_22 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_23 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_24 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_25 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_26 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_27 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_28 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_29 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_2A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_2B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_2C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_2D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_2E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_2F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_30 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_31 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_32 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_33 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_34 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_35 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_36 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_37 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_38 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_39 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_3A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_3B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_3C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_3D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_3E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_3F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+
+ parameter IS_CLKARDCLK_INVERTED = 1'b0;
+ parameter IS_CLKBWRCLK_INVERTED = 1'b0;
+ parameter IS_ENARDEN_INVERTED = 1'b0;
+ parameter IS_ENBWREN_INVERTED = 1'b0;
+ parameter IS_RSTRAMARSTRAM_INVERTED = 1'b0;
+ parameter IS_RSTRAMB_INVERTED = 1'b0;
+ parameter IS_RSTREGARSTREG_INVERTED = 1'b0;
+ parameter IS_RSTREGB_INVERTED = 1'b0;
+
+ parameter RAM_MODE = "TDP";
+ parameter integer DOA_REG = 0;
+ parameter integer DOB_REG = 0;
+
+ parameter integer READ_WIDTH_A = 0;
+ parameter integer READ_WIDTH_B = 0;
+ parameter integer WRITE_WIDTH_A = 0;
+ parameter integer WRITE_WIDTH_B = 0;
+
+ parameter WRITE_MODE_A = "WRITE_FIRST";
+ parameter WRITE_MODE_B = "WRITE_FIRST";
+
+ parameter SIM_DEVICE = "VIRTEX6";
+endmodule
+
+module RAMB36E1 (
+ input CLKARDCLK,
+ input CLKBWRCLK,
+ input ENARDEN,
+ input ENBWREN,
+ input REGCEAREGCE,
+ input REGCEB,
+ input RSTRAMARSTRAM,
+ input RSTRAMB,
+ input RSTREGARSTREG,
+ input RSTREGB,
+
+ input [15:0] ADDRARDADDR,
+ input [15:0] ADDRBWRADDR,
+ input [31:0] DIADI,
+ input [31:0] DIBDI,
+ input [3:0] DIPADIP,
+ input [3:0] DIPBDIP,
+ input [3:0] WEA,
+ input [7:0] WEBWE,
+
+ output [31:0] DOADO,
+ output [31:0] DOBDO,
+ output [3:0] DOPADOP,
+ output [3:0] DOPBDOP
+);
+ parameter INITP_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_01 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_02 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_03 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_04 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_05 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_06 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_07 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_08 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_09 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_0A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_0B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_0C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_0D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_0E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INITP_0F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+
+ parameter INIT_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_01 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_02 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_03 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_04 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_05 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_06 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_07 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_08 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_09 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_0A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_0B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_0C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_0D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_0E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_0F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_10 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_11 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_12 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_13 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_14 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_15 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_16 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_17 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_18 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_19 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_1A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_1B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_1C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_1D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_1E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_1F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_20 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_21 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_22 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_23 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_24 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_25 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_26 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_27 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_28 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_29 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_2A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_2B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_2C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_2D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_2E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_2F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_30 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_31 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_32 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_33 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_34 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_35 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_36 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_37 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_38 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_39 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_3A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_3B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_3C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_3D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_3E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_3F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_40 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_41 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_42 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_43 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_44 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_45 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_46 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_47 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_48 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_49 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_4A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_4B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_4C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_4D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_4E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_4F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_50 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_51 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_52 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_53 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_54 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_55 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_56 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_57 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_58 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_59 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_5A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_5B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_5C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_5D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_5E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_5F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_60 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_61 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_62 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_63 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_64 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_65 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_66 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_67 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_68 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_69 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_6A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_6B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_6C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_6D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_6E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_6F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_70 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_71 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_72 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_73 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_74 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_75 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_76 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_77 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_78 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_79 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_7A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_7B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_7C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_7D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_7E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_7F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+
+ parameter IS_CLKARDCLK_INVERTED = 1'b0;
+ parameter IS_CLKBWRCLK_INVERTED = 1'b0;
+ parameter IS_ENARDEN_INVERTED = 1'b0;
+ parameter IS_ENBWREN_INVERTED = 1'b0;
+ parameter IS_RSTRAMARSTRAM_INVERTED = 1'b0;
+ parameter IS_RSTRAMB_INVERTED = 1'b0;
+ parameter IS_RSTREGARSTREG_INVERTED = 1'b0;
+ parameter IS_RSTREGB_INVERTED = 1'b0;
+
+ parameter RAM_MODE = "TDP";
+ parameter integer DOA_REG = 0;
+ parameter integer DOB_REG = 0;
+
+ parameter integer READ_WIDTH_A = 0;
+ parameter integer READ_WIDTH_B = 0;
+ parameter integer WRITE_WIDTH_A = 0;
+ parameter integer WRITE_WIDTH_B = 0;
+
+ parameter WRITE_MODE_A = "WRITE_FIRST";
+ parameter WRITE_MODE_B = "WRITE_FIRST";
+
+ parameter SIM_DEVICE = "VIRTEX6";
+endmodule
diff --git a/techlibs/xilinx/brams_init.py b/techlibs/xilinx/brams_init.py
new file mode 100644
index 00000000..e787b1f7
--- /dev/null
+++ b/techlibs/xilinx/brams_init.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python3
+
+with open("techlibs/xilinx/brams_init_18.vh", "w") as f:
+ for i in range(8):
+ init_snippets = ["INIT[%3d*9+8]" % (k+256*i,) for k in range(255, -1, -1)]
+ for k in range(4, 256, 4):
+ init_snippets[k] = "\n " + init_snippets[k]
+ print(".INITP_%02X({%s})," % (i, ", ".join(init_snippets)), file=f)
+ for i in range(64):
+ init_snippets = ["INIT[%3d*9 +: 8]" % (k+32*i,) for k in range(31, -1, -1)]
+ for k in range(4, 32, 4):
+ init_snippets[k] = "\n " + init_snippets[k]
+ print(".INIT_%02X({%s})," % (i, ", ".join(init_snippets)), file=f)
+
+with open("techlibs/xilinx/brams_init_36.vh", "w") as f:
+ for i in range(16):
+ init_snippets = ["INIT[%3d*9+8]" % (k+256*i,) for k in range(255, -1, -1)]
+ for k in range(4, 256, 4):
+ init_snippets[k] = "\n " + init_snippets[k]
+ print(".INITP_%02X({%s})," % (i, ", ".join(init_snippets)), file=f)
+ for i in range(128):
+ init_snippets = ["INIT[%3d*9 +: 8]" % (k+32*i,) for k in range(31, -1, -1)]
+ for k in range(4, 32, 4):
+ init_snippets[k] = "\n " + init_snippets[k]
+ print(".INIT_%02X({%s})," % (i, ", ".join(init_snippets)), file=f)
+
+with open("techlibs/xilinx/brams_init_16.vh", "w") as f:
+ for i in range(64):
+ print(".INIT_%02X(INIT[%3d*256 +: 256])," % (i, i), file=f)
+
+with open("techlibs/xilinx/brams_init_32.vh", "w") as f:
+ for i in range(128):
+ print(".INIT_%02X(INIT[%3d*256 +: 256])," % (i, i), file=f)
+
diff --git a/techlibs/xilinx/brams_map.v b/techlibs/xilinx/brams_map.v
new file mode 100644
index 00000000..7ea49158
--- /dev/null
+++ b/techlibs/xilinx/brams_map.v
@@ -0,0 +1,359 @@
+module \$__XILINX_RAMB36_SDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
+ parameter CLKPOL2 = 1;
+ parameter CLKPOL3 = 1;
+ parameter [36863:0] INIT = 36864'bx;
+
+ input CLK2;
+ input CLK3;
+
+ input [8:0] A1ADDR;
+ output [71:0] A1DATA;
+ input A1EN;
+
+ input [8:0] B1ADDR;
+ input [71:0] B1DATA;
+ input [7:0] B1EN;
+
+ wire [15:0] A1ADDR_16 = {A1ADDR, 6'b0};
+ wire [15:0] B1ADDR_16 = {B1ADDR, 6'b0};
+
+ wire [7:0] DIP, DOP;
+ wire [63:0] DI, DO;
+
+ assign A1DATA = { DOP[7], DO[63:56], DOP[6], DO[55:48], DOP[5], DO[47:40], DOP[4], DO[39:32],
+ DOP[3], DO[31:24], DOP[2], DO[23:16], DOP[1], DO[15: 8], DOP[0], DO[ 7: 0] };
+
+ assign { DIP[7], DI[63:56], DIP[6], DI[55:48], DIP[5], DI[47:40], DIP[4], DI[39:32],
+ DIP[3], DI[31:24], DIP[2], DI[23:16], DIP[1], DI[15: 8], DIP[0], DI[ 7: 0] } = B1DATA;
+
+ RAMB36E1 #(
+ .RAM_MODE("SDP"),
+ .READ_WIDTH_A(72),
+ .WRITE_WIDTH_B(72),
+ .WRITE_MODE_A("READ_FIRST"),
+ .WRITE_MODE_B("READ_FIRST"),
+ .IS_CLKARDCLK_INVERTED(!CLKPOL2),
+ .IS_CLKBWRCLK_INVERTED(!CLKPOL3),
+ `include "brams_init_36.vh"
+ .SIM_DEVICE("7SERIES")
+ ) _TECHMAP_REPLACE_ (
+ .DOBDO(DO[63:32]),
+ .DOADO(DO[31:0]),
+ .DOPBDOP(DOP[7:4]),
+ .DOPADOP(DOP[3:0]),
+ .DIBDI(DI[63:32]),
+ .DIADI(DI[31:0]),
+ .DIPBDIP(DIP[7:4]),
+ .DIPADIP(DIP[3:0]),
+
+ .ADDRARDADDR(A1ADDR_16),
+ .CLKARDCLK(CLK2),
+ .ENARDEN(A1EN),
+ .REGCEAREGCE(|1),
+ .RSTRAMARSTRAM(|0),
+ .RSTREGARSTREG(|0),
+ .WEA(4'b0),
+
+ .ADDRBWRADDR(B1ADDR_16),
+ .CLKBWRCLK(CLK3),
+ .ENBWREN(|1),
+ .REGCEB(|0),
+ .RSTRAMB(|0),
+ .RSTREGB(|0),
+ .WEBWE(B1EN)
+ );
+endmodule
+
+// ------------------------------------------------------------------------
+
+module \$__XILINX_RAMB18_SDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
+ parameter CLKPOL2 = 1;
+ parameter CLKPOL3 = 1;
+ parameter [18431:0] INIT = 18432'bx;
+
+ input CLK2;
+ input CLK3;
+
+ input [8:0] A1ADDR;
+ output [35:0] A1DATA;
+ input A1EN;
+
+ input [8:0] B1ADDR;
+ input [35:0] B1DATA;
+ input [3:0] B1EN;
+
+ wire [13:0] A1ADDR_14 = {A1ADDR, 5'b0};
+ wire [13:0] B1ADDR_14 = {B1ADDR, 5'b0};
+
+ wire [3:0] DIP, DOP;
+ wire [31:0] DI, DO;
+
+ assign A1DATA = { DOP[3], DO[31:24], DOP[2], DO[23:16], DOP[1], DO[15: 8], DOP[0], DO[ 7: 0] };
+ assign { DIP[3], DI[31:24], DIP[2], DI[23:16], DIP[1], DI[15: 8], DIP[0], DI[ 7: 0] } = B1DATA;
+
+ RAMB18E1 #(
+ .RAM_MODE("SDP"),
+ .READ_WIDTH_A(36),
+ .WRITE_WIDTH_B(36),
+ .WRITE_MODE_A("READ_FIRST"),
+ .WRITE_MODE_B("READ_FIRST"),
+ .IS_CLKARDCLK_INVERTED(!CLKPOL2),
+ .IS_CLKBWRCLK_INVERTED(!CLKPOL3),
+ `include "brams_init_18.vh"
+ .SIM_DEVICE("7SERIES")
+ ) _TECHMAP_REPLACE_ (
+ .DOBDO(DO[31:16]),
+ .DOADO(DO[15:0]),
+ .DOPBDOP(DOP[3:2]),
+ .DOPADOP(DOP[1:0]),
+ .DIBDI(DI[31:16]),
+ .DIADI(DI[15:0]),
+ .DIPBDIP(DIP[3:2]),
+ .DIPADIP(DIP[1:0]),
+
+ .ADDRARDADDR(A1ADDR_14),
+ .CLKARDCLK(CLK2),
+ .ENARDEN(A1EN),
+ .REGCEAREGCE(|1),
+ .RSTRAMARSTRAM(|0),
+ .RSTREGARSTREG(|0),
+ .WEA(2'b0),
+
+ .ADDRBWRADDR(B1ADDR_14),
+ .CLKBWRCLK(CLK3),
+ .ENBWREN(|1),
+ .REGCEB(|0),
+ .RSTRAMB(|0),
+ .RSTREGB(|0),
+ .WEBWE(B1EN)
+ );
+endmodule
+
+// ------------------------------------------------------------------------
+
+module \$__XILINX_RAMB36_TDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
+ parameter CFG_ABITS = 10;
+ parameter CFG_DBITS = 36;
+ parameter CFG_ENABLE_B = 4;
+
+ parameter CLKPOL2 = 1;
+ parameter CLKPOL3 = 1;
+ parameter [36863:0] INIT = 36864'bx;
+
+ input CLK2;
+ input CLK3;
+
+ input [CFG_ABITS-1:0] A1ADDR;
+ output [CFG_DBITS-1:0] A1DATA;
+ input A1EN;
+
+ input [CFG_ABITS-1:0] B1ADDR;
+ input [CFG_DBITS-1:0] B1DATA;
+ input [CFG_ENABLE_B-1:0] B1EN;
+
+ wire [15:0] A1ADDR_16 = A1ADDR << (15 - CFG_ABITS);
+ wire [15:0] B1ADDR_16 = B1ADDR << (15 - CFG_ABITS);
+ wire [7:0] B1EN_8 = B1EN;
+
+ wire [3:0] DIP, DOP;
+ wire [31:0] DI, DO;
+
+ wire [31:0] DOBDO;
+ wire [3:0] DOPBDOP;
+
+ assign A1DATA = { DOP[3], DO[31:24], DOP[2], DO[23:16], DOP[1], DO[15: 8], DOP[0], DO[ 7: 0] };
+ assign { DIP[3], DI[31:24], DIP[2], DI[23:16], DIP[1], DI[15: 8], DIP[0], DI[ 7: 0] } = B1DATA;
+
+ generate if (CFG_DBITS > 8) begin
+ RAMB36E1 #(
+ .RAM_MODE("TDP"),
+ .READ_WIDTH_A(CFG_DBITS),
+ .READ_WIDTH_B(CFG_DBITS),
+ .WRITE_WIDTH_A(CFG_DBITS),
+ .WRITE_WIDTH_B(CFG_DBITS),
+ .WRITE_MODE_A("READ_FIRST"),
+ .WRITE_MODE_B("READ_FIRST"),
+ .IS_CLKARDCLK_INVERTED(!CLKPOL2),
+ .IS_CLKBWRCLK_INVERTED(!CLKPOL3),
+ `include "brams_init_36.vh"
+ .SIM_DEVICE("7SERIES")
+ ) _TECHMAP_REPLACE_ (
+ .DIADI(32'd0),
+ .DIPADIP(4'd0),
+ .DOADO(DO[31:0]),
+ .DOPADOP(DOP[3:0]),
+ .ADDRARDADDR(A1ADDR_16),
+ .CLKARDCLK(CLK2),
+ .ENARDEN(A1EN),
+ .REGCEAREGCE(|1),
+ .RSTRAMARSTRAM(|0),
+ .RSTREGARSTREG(|0),
+ .WEA(4'b0),
+
+ .DIBDI(DI),
+ .DIPBDIP(DIP),
+ .DOBDO(DOBDO),
+ .DOPBDOP(DOPBDOP),
+ .ADDRBWRADDR(B1ADDR_16),
+ .CLKBWRCLK(CLK3),
+ .ENBWREN(|1),
+ .REGCEB(|0),
+ .RSTRAMB(|0),
+ .RSTREGB(|0),
+ .WEBWE(B1EN_8)
+ );
+ end else begin
+ RAMB36E1 #(
+ .RAM_MODE("TDP"),
+ .READ_WIDTH_A(CFG_DBITS),
+ .READ_WIDTH_B(CFG_DBITS),
+ .WRITE_WIDTH_A(CFG_DBITS),
+ .WRITE_WIDTH_B(CFG_DBITS),
+ .WRITE_MODE_A("READ_FIRST"),
+ .WRITE_MODE_B("READ_FIRST"),
+ .IS_CLKARDCLK_INVERTED(!CLKPOL2),
+ .IS_CLKBWRCLK_INVERTED(!CLKPOL3),
+ `include "brams_init_32.vh"
+ .SIM_DEVICE("7SERIES")
+ ) _TECHMAP_REPLACE_ (
+ .DIADI(32'd0),
+ .DIPADIP(4'd0),
+ .DOADO(DO[31:0]),
+ .DOPADOP(DOP[3:0]),
+ .ADDRARDADDR(A1ADDR_16),
+ .CLKARDCLK(CLK2),
+ .ENARDEN(A1EN),
+ .REGCEAREGCE(|1),
+ .RSTRAMARSTRAM(|0),
+ .RSTREGARSTREG(|0),
+ .WEA(4'b0),
+
+ .DIBDI(DI),
+ .DIPBDIP(DIP),
+ .DOBDO(DOBDO),
+ .DOPBDOP(DOPBDOP),
+ .ADDRBWRADDR(B1ADDR_16),
+ .CLKBWRCLK(CLK3),
+ .ENBWREN(|1),
+ .REGCEB(|0),
+ .RSTRAMB(|0),
+ .RSTREGB(|0),
+ .WEBWE(B1EN_8)
+ );
+ end endgenerate
+endmodule
+
+// ------------------------------------------------------------------------
+
+module \$__XILINX_RAMB18_TDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
+ parameter CFG_ABITS = 10;
+ parameter CFG_DBITS = 18;
+ parameter CFG_ENABLE_B = 2;
+
+ parameter CLKPOL2 = 1;
+ parameter CLKPOL3 = 1;
+ parameter [18431:0] INIT = 18432'bx;
+
+ input CLK2;
+ input CLK3;
+
+ input [CFG_ABITS-1:0] A1ADDR;
+ output [CFG_DBITS-1:0] A1DATA;
+ input A1EN;
+
+ input [CFG_ABITS-1:0] B1ADDR;
+ input [CFG_DBITS-1:0] B1DATA;
+ input [CFG_ENABLE_B-1:0] B1EN;
+
+ wire [13:0] A1ADDR_14 = A1ADDR << (14 - CFG_ABITS);
+ wire [13:0] B1ADDR_14 = B1ADDR << (14 - CFG_ABITS);
+ wire [3:0] B1EN_4 = B1EN;
+
+ wire [1:0] DIP, DOP;
+ wire [15:0] DI, DO;
+
+ wire [15:0] DOBDO;
+ wire [1:0] DOPBDOP;
+
+ assign A1DATA = { DOP[1], DO[15: 8], DOP[0], DO[ 7: 0] };
+ assign { DIP[1], DI[15: 8], DIP[0], DI[ 7: 0] } = B1DATA;
+
+ generate if (CFG_DBITS > 8) begin
+ RAMB18E1 #(
+ .RAM_MODE("TDP"),
+ .READ_WIDTH_A(CFG_DBITS),
+ .READ_WIDTH_B(CFG_DBITS),
+ .WRITE_WIDTH_A(CFG_DBITS),
+ .WRITE_WIDTH_B(CFG_DBITS),
+ .WRITE_MODE_A("READ_FIRST"),
+ .WRITE_MODE_B("READ_FIRST"),
+ .IS_CLKARDCLK_INVERTED(!CLKPOL2),
+ .IS_CLKBWRCLK_INVERTED(!CLKPOL3),
+ `include "brams_init_18.vh"
+ .SIM_DEVICE("7SERIES")
+ ) _TECHMAP_REPLACE_ (
+ .DIADI(16'b0),
+ .DIPADIP(2'b0),
+ .DOADO(DO),
+ .DOPADOP(DOP),
+ .ADDRARDADDR(A1ADDR_14),
+ .CLKARDCLK(CLK2),
+ .ENARDEN(A1EN),
+ .REGCEAREGCE(|1),
+ .RSTRAMARSTRAM(|0),
+ .RSTREGARSTREG(|0),
+ .WEA(2'b0),
+
+ .DIBDI(DI),
+ .DIPBDIP(DIP),
+ .DOBDO(DOBDO),
+ .DOPBDOP(DOPBDOP),
+ .ADDRBWRADDR(B1ADDR_14),
+ .CLKBWRCLK(CLK3),
+ .ENBWREN(|1),
+ .REGCEB(|0),
+ .RSTRAMB(|0),
+ .RSTREGB(|0),
+ .WEBWE(B1EN_4)
+ );
+ end else begin
+ RAMB18E1 #(
+ .RAM_MODE("TDP"),
+ .READ_WIDTH_A(CFG_DBITS),
+ .READ_WIDTH_B(CFG_DBITS),
+ .WRITE_WIDTH_A(CFG_DBITS),
+ .WRITE_WIDTH_B(CFG_DBITS),
+ .WRITE_MODE_A("READ_FIRST"),
+ .WRITE_MODE_B("READ_FIRST"),
+ .IS_CLKARDCLK_INVERTED(!CLKPOL2),
+ .IS_CLKBWRCLK_INVERTED(!CLKPOL3),
+ `include "brams_init_16.vh"
+ .SIM_DEVICE("7SERIES")
+ ) _TECHMAP_REPLACE_ (
+ .DIADI(16'b0),
+ .DIPADIP(2'b0),
+ .DOADO(DO),
+ .DOPADOP(DOP),
+ .ADDRARDADDR(A1ADDR_14),
+ .CLKARDCLK(CLK2),
+ .ENARDEN(A1EN),
+ .REGCEAREGCE(|1),
+ .RSTRAMARSTRAM(|0),
+ .RSTREGARSTREG(|0),
+ .WEA(2'b0),
+
+ .DIBDI(DI),
+ .DIPBDIP(DIP),
+ .DOBDO(DOBDO),
+ .DOPBDOP(DOPBDOP),
+ .ADDRBWRADDR(B1ADDR_14),
+ .CLKBWRCLK(CLK3),
+ .ENBWREN(|1),
+ .REGCEB(|0),
+ .RSTRAMB(|0),
+ .RSTREGB(|0),
+ .WEBWE(B1EN_4)
+ );
+ end endgenerate
+endmodule
+
diff --git a/techlibs/xilinx/cells_map.v b/techlibs/xilinx/cells_map.v
new file mode 100644
index 00000000..8e5a83ce
--- /dev/null
+++ b/techlibs/xilinx/cells_map.v
@@ -0,0 +1,84 @@
+
+module \$_DFF_N_ (input D, C, output Q); FDRE #(.INIT(|0), .IS_C_INVERTED(|1), .IS_D_INVERTED(|0), .IS_R_INVERTED(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0)); endmodule
+module \$_DFF_P_ (input D, C, output Q); FDRE #(.INIT(|0), .IS_C_INVERTED(|0), .IS_D_INVERTED(|0), .IS_R_INVERTED(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0)); endmodule
+
+module \$_DFFE_NP_ (input D, C, E, output Q); FDRE #(.INIT(|0), .IS_C_INVERTED(|1), .IS_D_INVERTED(|0), .IS_R_INVERTED(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0)); endmodule
+module \$_DFFE_PP_ (input D, C, E, output Q); FDRE #(.INIT(|0), .IS_C_INVERTED(|0), .IS_D_INVERTED(|0), .IS_R_INVERTED(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0)); endmodule
+
+module \$_DFF_NN0_ (input D, C, R, output Q); FDCE #(.INIT(|0), .IS_C_INVERTED(|1), .IS_D_INVERTED(|0), .IS_CLR_INVERTED(|1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(R)); endmodule
+module \$_DFF_NP0_ (input D, C, R, output Q); FDCE #(.INIT(|0), .IS_C_INVERTED(|1), .IS_D_INVERTED(|0), .IS_CLR_INVERTED(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(R)); endmodule
+module \$_DFF_PN0_ (input D, C, R, output Q); FDCE #(.INIT(|0), .IS_C_INVERTED(|0), .IS_D_INVERTED(|0), .IS_CLR_INVERTED(|1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(R)); endmodule
+module \$_DFF_PP0_ (input D, C, R, output Q); FDCE #(.INIT(|0), .IS_C_INVERTED(|0), .IS_D_INVERTED(|0), .IS_CLR_INVERTED(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(R)); endmodule
+
+module \$_DFF_NN1_ (input D, C, R, output Q); FDPE #(.INIT(|0), .IS_C_INVERTED(|1), .IS_D_INVERTED(|0), .IS_PRE_INVERTED(|1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(R)); endmodule
+module \$_DFF_NP1_ (input D, C, R, output Q); FDPE #(.INIT(|0), .IS_C_INVERTED(|1), .IS_D_INVERTED(|0), .IS_PRE_INVERTED(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(R)); endmodule
+module \$_DFF_PN1_ (input D, C, R, output Q); FDPE #(.INIT(|0), .IS_C_INVERTED(|0), .IS_D_INVERTED(|0), .IS_PRE_INVERTED(|1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(R)); endmodule
+module \$_DFF_PP1_ (input D, C, R, output Q); FDPE #(.INIT(|0), .IS_C_INVERTED(|0), .IS_D_INVERTED(|0), .IS_PRE_INVERTED(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(R)); endmodule
+
+module \$lut (A, Y);
+ parameter WIDTH = 0;
+ parameter LUT = 0;
+
+ input [WIDTH-1:0] A;
+ output Y;
+
+ generate
+ if (WIDTH == 1) begin
+ LUT1 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(A[0]));
+ end else
+ if (WIDTH == 2) begin
+ LUT2 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(A[0]), .I1(A[1]));
+ end else
+ if (WIDTH == 3) begin
+ LUT3 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]));
+ end else
+ if (WIDTH == 4) begin
+ LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]),
+ .I3(A[3]));
+ end else
+ if (WIDTH == 5) begin
+ LUT5 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]),
+ .I3(A[3]), .I4(A[4]));
+ end else
+ if (WIDTH == 6) begin
+ LUT6 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]),
+ .I3(A[3]), .I4(A[4]), .I5(A[5]));
+ end else
+ if (WIDTH == 7) begin
+ wire T0, T1;
+ LUT6 #(.INIT(LUT[63:0])) fpga_lut_0 (.O(T0),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]),
+ .I3(A[3]), .I4(A[4]), .I5(A[5]));
+ LUT6 #(.INIT(LUT[127:64])) fpga_lut_1 (.O(T1),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]),
+ .I3(A[3]), .I4(A[4]), .I5(A[5]));
+ MUXF7 fpga_mux_0 (.O(Y), .I0(T0), .I1(T1), .S(A[6]));
+ end else
+ if (WIDTH == 8) begin
+ wire T0, T1, T2, T3, T4, T5;
+ LUT6 #(.INIT(LUT[63:0])) fpga_lut_0 (.O(T0),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]),
+ .I3(A[3]), .I4(A[4]), .I5(A[5]));
+ LUT6 #(.INIT(LUT[127:64])) fpga_lut_1 (.O(T1),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]),
+ .I3(A[3]), .I4(A[4]), .I5(A[5]));
+ LUT6 #(.INIT(LUT[191:128])) fpga_lut_2 (.O(T2),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]),
+ .I3(A[3]), .I4(A[4]), .I5(A[5]));
+ LUT6 #(.INIT(LUT[255:192])) fpga_lut_3 (.O(T3),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]),
+ .I3(A[3]), .I4(A[4]), .I5(A[5]));
+ MUXF7 fpga_mux_0 (.O(T4), .I0(T0), .I1(T1), .S(A[6]));
+ MUXF7 fpga_mux_1 (.O(T5), .I0(T2), .I1(T3), .S(A[6]));
+ MUXF8 fpga_mux_2 (.O(Y), .I0(T4), .I1(T5), .S(A[7]));
+ end else begin
+ wire _TECHMAP_FAIL_ = 1;
+ end
+ endgenerate
+endmodule
diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v
new file mode 100644
index 00000000..1f114a22
--- /dev/null
+++ b/techlibs/xilinx/cells_sim.v
@@ -0,0 +1,158 @@
+
+// See Xilinx UG953 and UG474 for a description of the cell types below.
+// http://www.xilinx.com/support/documentation/user_guides/ug474_7Series_CLB.pdf
+// http://www.xilinx.com/support/documentation/sw_manuals/xilinx2014_4/ug953-vivado-7series-libraries.pdf
+
+module VCC(output P);
+ assign P = 1;
+endmodule
+
+module GND(output G);
+ assign G = 0;
+endmodule
+
+module IBUF(output O, input I);
+ assign O = I;
+endmodule
+
+module OBUF(output O, input I);
+ assign O = I;
+endmodule
+
+module BUFG(output O, input I);
+ assign O = I;
+endmodule
+
+// module OBUFT(output O, input I, T);
+// assign O = T ? 1'bz : I;
+// endmodule
+
+// module IOBUF(inout IO, output O, input I, T);
+// assign O = IO, IO = T ? 1'bz : I;
+// endmodule
+
+module INV(output O, input I);
+ assign O = !I;
+endmodule
+
+module LUT1(output O, input I0);
+ parameter [1:0] INIT = 0;
+ assign O = I0 ? INIT[1] : INIT[0];
+endmodule
+
+module LUT2(output O, input I0, I1);
+ parameter [3:0] INIT = 0;
+ wire [ 1: 0] s1 = I1 ? INIT[ 3: 2] : INIT[ 1: 0];
+ assign O = I0 ? s1[1] : s1[0];
+endmodule
+
+module LUT3(output O, input I0, I1, I2);
+ parameter [7:0] INIT = 0;
+ wire [ 3: 0] s2 = I2 ? INIT[ 7: 4] : INIT[ 3: 0];
+ wire [ 1: 0] s1 = I1 ? s2[ 3: 2] : s2[ 1: 0];
+ assign O = I0 ? s1[1] : s1[0];
+endmodule
+
+module LUT4(output O, input I0, I1, I2, I3);
+ parameter [15:0] INIT = 0;
+ wire [ 7: 0] s3 = I3 ? INIT[15: 8] : INIT[ 7: 0];
+ wire [ 3: 0] s2 = I2 ? s3[ 7: 4] : s3[ 3: 0];
+ wire [ 1: 0] s1 = I1 ? s2[ 3: 2] : s2[ 1: 0];
+ assign O = I0 ? s1[1] : s1[0];
+endmodule
+
+module LUT5(output O, input I0, I1, I2, I3, I4);
+ parameter [31:0] INIT = 0;
+ wire [15: 0] s4 = I4 ? INIT[31:16] : INIT[15: 0];
+ wire [ 7: 0] s3 = I3 ? s4[15: 8] : s4[ 7: 0];
+ wire [ 3: 0] s2 = I2 ? s3[ 7: 4] : s3[ 3: 0];
+ wire [ 1: 0] s1 = I1 ? s2[ 3: 2] : s2[ 1: 0];
+ assign O = I0 ? s1[1] : s1[0];
+endmodule
+
+module LUT6(output O, input I0, I1, I2, I3, I4, I5);
+ parameter [63:0] INIT = 0;
+ wire [31: 0] s5 = I5 ? INIT[63:32] : INIT[31: 0];
+ wire [15: 0] s4 = I4 ? s5[31:16] : s5[15: 0];
+ wire [ 7: 0] s3 = I3 ? s4[15: 8] : s4[ 7: 0];
+ wire [ 3: 0] s2 = I2 ? s3[ 7: 4] : s3[ 3: 0];
+ wire [ 1: 0] s1 = I1 ? s2[ 3: 2] : s2[ 1: 0];
+ assign O = I0 ? s1[1] : s1[0];
+endmodule
+
+module MUXCY(output O, input CI, DI, S);
+ assign O = S ? CI : DI;
+endmodule
+
+module MUXF7(output O, input I0, I1, S);
+ assign O = S ? I1 : I0;
+endmodule
+
+module MUXF8(output O, input I0, I1, S);
+ assign O = S ? I1 : I0;
+endmodule
+
+module XORCY(output O, input CI, LI);
+ assign O = CI ^ LI;
+endmodule
+
+module CARRY4(output [3:0] CO, O, input CI, CYINIT, input [3:0] DI, S);
+ assign O = S ^ {CO[2:0], CI | CYINIT};
+ assign CO[0] = S[0] ? CI | CYINIT : DI[0];
+ assign CO[1] = S[1] ? CO[0] : DI[1];
+ assign CO[2] = S[2] ? CO[1] : DI[2];
+ assign CO[3] = S[3] ? CO[2] : DI[3];
+endmodule
+
+module FDRE (output reg Q, input C, CE, D, R);
+ parameter [0:0] INIT = 1'b0;
+ parameter [0:0] IS_C_INVERTED = 1'b0;
+ parameter [0:0] IS_D_INVERTED = 1'b0;
+ parameter [0:0] IS_R_INVERTED = 1'b0;
+ initial Q <= INIT;
+ generate case (|IS_C_INVERTED)
+ 1'b0: always @(posedge C) if (R == !IS_R_INVERTED) Q <= 1'b0; else if (CE) Q <= D ^ IS_D_INVERTED;
+ 1'b1: always @(negedge C) if (R == !IS_R_INVERTED) Q <= 1'b0; else if (CE) Q <= D ^ IS_D_INVERTED;
+ endcase endgenerate
+endmodule
+
+module FDSE (output reg Q, input C, CE, D, S);
+ parameter [0:0] INIT = 1'b0;
+ parameter [0:0] IS_C_INVERTED = 1'b0;
+ parameter [0:0] IS_D_INVERTED = 1'b0;
+ parameter [0:0] IS_S_INVERTED = 1'b0;
+ initial Q <= INIT;
+ generate case (|IS_C_INVERTED)
+ 1'b0: always @(posedge C) if (S == !IS_S_INVERTED) Q <= 1'b1; else if (CE) Q <= D ^ IS_D_INVERTED;
+ 1'b1: always @(negedge C) if (S == !IS_S_INVERTED) Q <= 1'b1; else if (CE) Q <= D ^ IS_D_INVERTED;
+ endcase endgenerate
+endmodule
+
+module FDCE (output reg Q, input C, CE, D, CLR);
+ parameter [0:0] INIT = 1'b0;
+ parameter [0:0] IS_C_INVERTED = 1'b0;
+ parameter [0:0] IS_D_INVERTED = 1'b0;
+ parameter [0:0] IS_CLR_INVERTED = 1'b0;
+ initial Q <= INIT;
+ generate case ({|IS_C_INVERTED, |IS_CLR_INVERTED})
+ 2'b00: always @(posedge C, posedge CLR) if ( CLR) Q <= 1'b0; else if (CE) Q <= D ^ IS_D_INVERTED;
+ 2'b01: always @(posedge C, negedge CLR) if (!CLR) Q <= 1'b0; else if (CE) Q <= D ^ IS_D_INVERTED;
+ 2'b10: always @(negedge C, posedge CLR) if ( CLR) Q <= 1'b0; else if (CE) Q <= D ^ IS_D_INVERTED;
+ 2'b11: always @(negedge C, negedge CLR) if (!CLR) Q <= 1'b0; else if (CE) Q <= D ^ IS_D_INVERTED;
+ endcase endgenerate
+endmodule
+
+module FDPE (output reg Q, input C, CE, D, PRE);
+ parameter [0:0] INIT = 1'b0;
+ parameter [0:0] IS_C_INVERTED = 1'b0;
+ parameter [0:0] IS_D_INVERTED = 1'b0;
+ parameter [0:0] IS_PRE_INVERTED = 1'b0;
+ initial Q <= INIT;
+ generate case ({|IS_C_INVERTED, |IS_PRE_INVERTED})
+ 2'b00: always @(posedge C, posedge PRE) if ( PRE) Q <= 1'b1; else if (CE) Q <= D ^ IS_D_INVERTED;
+ 2'b01: always @(posedge C, negedge PRE) if (!PRE) Q <= 1'b1; else if (CE) Q <= D ^ IS_D_INVERTED;
+ 2'b10: always @(negedge C, posedge PRE) if ( PRE) Q <= 1'b1; else if (CE) Q <= D ^ IS_D_INVERTED;
+ 2'b11: always @(negedge C, negedge PRE) if (!PRE) Q <= 1'b1; else if (CE) Q <= D ^ IS_D_INVERTED;
+ endcase endgenerate
+endmodule
+
diff --git a/techlibs/xilinx/cells_xtra.sh b/techlibs/xilinx/cells_xtra.sh
new file mode 100644
index 00000000..c7ad1604
--- /dev/null
+++ b/techlibs/xilinx/cells_xtra.sh
@@ -0,0 +1,145 @@
+#!/bin/bash
+
+set -e
+libdir="/opt/Xilinx/Vivado/2015.4/data/verilog/src"
+
+function xtract_cell_decl()
+{
+ for dir in $libdir/xeclib $libdir/retarget; do
+ [ -f $dir/$1.v ] || continue
+ egrep '^\s*((end)?module|parameter|input|output|(end)?function|(end)?task)' $dir/$1.v |
+ sed -re '/UNPLACED/ d; /^\s*function/,/endfunction/ d; /^\s*task/,/endtask/ d;
+ s,//.*,,; s/#?\(.*/(...);/; s/^(input|output|parameter)/ \1/;
+ s/\s+$//; s/,$/;/; /input|output|parameter/ s/[^;]$/&;/; s/\s+/ /g;
+ s/^ ((end)?module)/\1/; s/^ / /; /module.*_bb/,/endmodule/ d;'
+ echo; return
+ done
+ echo "Can't find $1."
+ exit 1
+}
+
+{
+ echo "// Created by cells_xtra.sh from Xilinx models"
+ echo
+
+ # Design elements types listed in Xilinx UG953
+ xtract_cell_decl BSCANE2
+ # xtract_cell_decl BUFG
+ xtract_cell_decl BUFGCE
+ xtract_cell_decl BUFGCE_1
+ xtract_cell_decl BUFGCTRL
+ xtract_cell_decl BUFGMUX
+ xtract_cell_decl BUFGMUX_1
+ xtract_cell_decl BUFGMUX_CTRL
+ xtract_cell_decl BUFH
+ xtract_cell_decl BUFHCE
+ xtract_cell_decl BUFIO
+ xtract_cell_decl BUFMR
+ xtract_cell_decl BUFMRCE
+ xtract_cell_decl BUFR
+ xtract_cell_decl CAPTUREE2
+ # xtract_cell_decl CARRY4
+ xtract_cell_decl CFGLUT5
+ xtract_cell_decl DCIRESET
+ xtract_cell_decl DNA_PORT
+ xtract_cell_decl DSP48E1
+ xtract_cell_decl EFUSE_USR
+ # xtract_cell_decl FDCE
+ # xtract_cell_decl FDPE
+ # xtract_cell_decl FDRE
+ # xtract_cell_decl FDSE
+ xtract_cell_decl FIFO18E1
+ xtract_cell_decl FIFO36E1
+ xtract_cell_decl FRAME_ECCE2
+ xtract_cell_decl GTHE2_CHANNEL
+ xtract_cell_decl GTHE2_COMMON
+ xtract_cell_decl GTPE2_CHANNEL
+ xtract_cell_decl GTPE2_COMMON
+ xtract_cell_decl GTXE2_CHANNEL
+ xtract_cell_decl GTXE2_COMMON
+ # xtract_cell_decl IBUF
+ xtract_cell_decl IBUF_IBUFDISABLE
+ xtract_cell_decl IBUF_INTERMDISABLE
+ xtract_cell_decl IBUFDS
+ xtract_cell_decl IBUFDS_DIFF_OUT
+ xtract_cell_decl IBUFDS_DIFF_OUT_IBUFDISABLE
+ xtract_cell_decl IBUFDS_DIFF_OUT_INTERMDISABLE
+ xtract_cell_decl IBUFDS_GTE2
+ xtract_cell_decl IBUFDS_IBUFDISABLE
+ xtract_cell_decl IBUFDS_INTERMDISABLE
+ xtract_cell_decl ICAPE2
+ xtract_cell_decl IDDR
+ xtract_cell_decl IDDR_2CLK
+ xtract_cell_decl IDELAYCTRL
+ xtract_cell_decl IDELAYE2
+ xtract_cell_decl IN_FIFO
+ xtract_cell_decl IOBUF
+ xtract_cell_decl IOBUF_DCIEN
+ xtract_cell_decl IOBUF_INTERMDISABLE
+ xtract_cell_decl IOBUFDS
+ xtract_cell_decl IOBUFDS_DCIEN
+ xtract_cell_decl IOBUFDS_DIFF_OUT
+ xtract_cell_decl IOBUFDS_DIFF_OUT_DCIEN
+ xtract_cell_decl IOBUFDS_DIFF_OUT_INTERMDISABLE
+ xtract_cell_decl ISERDESE2
+ xtract_cell_decl KEEPER
+ xtract_cell_decl LDCE
+ xtract_cell_decl LDPE
+ # xtract_cell_decl LUT1
+ # xtract_cell_decl LUT2
+ # xtract_cell_decl LUT3
+ # xtract_cell_decl LUT4
+ # xtract_cell_decl LUT5
+ # xtract_cell_decl LUT6
+ xtract_cell_decl LUT6_2
+ xtract_cell_decl MMCME2_ADV
+ xtract_cell_decl MMCME2_BASE
+ # xtract_cell_decl MUXF7
+ # xtract_cell_decl MUXF8
+ # xtract_cell_decl OBUF
+ xtract_cell_decl OBUFDS
+ xtract_cell_decl OBUFT
+ xtract_cell_decl OBUFTDS
+ xtract_cell_decl ODDR
+ xtract_cell_decl ODELAYE2
+ xtract_cell_decl OSERDESE2
+ xtract_cell_decl OUT_FIFO
+ xtract_cell_decl PHASER_IN
+ xtract_cell_decl PHASER_IN_PHY
+ xtract_cell_decl PHASER_OUT
+ xtract_cell_decl PHASER_OUT_PHY
+ xtract_cell_decl PHASER_REF
+ xtract_cell_decl PHY_CONTROL
+ xtract_cell_decl PLLE2_ADV
+ xtract_cell_decl PLLE2_BASE
+ xtract_cell_decl PULLDOWN
+ xtract_cell_decl PULLUP
+ # xtract_cell_decl RAM128X1D
+ xtract_cell_decl RAM128X1S
+ xtract_cell_decl RAM256X1S
+ xtract_cell_decl RAM32M
+ xtract_cell_decl RAM32X1D
+ xtract_cell_decl RAM32X1S
+ xtract_cell_decl RAM32X1S_1
+ xtract_cell_decl RAM32X2S
+ xtract_cell_decl RAM64M
+ # xtract_cell_decl RAM64X1D
+ xtract_cell_decl RAM64X1S
+ xtract_cell_decl RAM64X1S_1
+ xtract_cell_decl RAM64X2S
+ # xtract_cell_decl RAMB18E1
+ # xtract_cell_decl RAMB36E1
+ xtract_cell_decl ROM128X1
+ xtract_cell_decl ROM256X1
+ xtract_cell_decl ROM32X1
+ xtract_cell_decl ROM64X1
+ xtract_cell_decl SRL16E
+ xtract_cell_decl SRLC32E
+ xtract_cell_decl STARTUPE2
+ xtract_cell_decl USR_ACCESSE2
+ xtract_cell_decl XADC
+} > cells_xtra.new
+
+mv cells_xtra.new cells_xtra.v
+exit 0
+
diff --git a/techlibs/xilinx/cells_xtra.v b/techlibs/xilinx/cells_xtra.v
new file mode 100644
index 00000000..a2dd01ad
--- /dev/null
+++ b/techlibs/xilinx/cells_xtra.v
@@ -0,0 +1,3293 @@
+// Created by cells_xtra.sh from Xilinx models
+
+module BSCANE2 (...);
+ parameter DISABLE_JTAG = "FALSE";
+ parameter integer JTAG_CHAIN = 1;
+ output CAPTURE;
+ output DRCK;
+ output RESET;
+ output RUNTEST;
+ output SEL;
+ output SHIFT;
+ output TCK;
+ output TDI;
+ output TMS;
+ output UPDATE;
+ input TDO;
+endmodule
+
+module BUFGCE (...);
+ parameter CE_TYPE = "SYNC";
+ parameter [0:0] IS_CE_INVERTED = 1'b0;
+ parameter [0:0] IS_I_INVERTED = 1'b0;
+ output O;
+ input CE;
+ input I;
+endmodule
+
+module BUFGCE_1 (...);
+ output O;
+ input CE, I;
+endmodule
+
+module BUFGCTRL (...);
+ output O;
+ input CE0;
+ input CE1;
+ input I0;
+ input I1;
+ input IGNORE0;
+ input IGNORE1;
+ input S0;
+ input S1;
+ parameter integer INIT_OUT = 0;
+ parameter PRESELECT_I0 = "FALSE";
+ parameter PRESELECT_I1 = "FALSE";
+ parameter [0:0] IS_CE0_INVERTED = 1'b0;
+ parameter [0:0] IS_CE1_INVERTED = 1'b0;
+ parameter [0:0] IS_I0_INVERTED = 1'b0;
+ parameter [0:0] IS_I1_INVERTED = 1'b0;
+ parameter [0:0] IS_IGNORE0_INVERTED = 1'b0;
+ parameter [0:0] IS_IGNORE1_INVERTED = 1'b0;
+ parameter [0:0] IS_S0_INVERTED = 1'b0;
+ parameter [0:0] IS_S1_INVERTED = 1'b0;
+endmodule
+
+module BUFGMUX (...);
+ parameter CLK_SEL_TYPE = "SYNC";
+ output O;
+ input I0, I1, S;
+endmodule
+
+module BUFGMUX_1 (...);
+ parameter CLK_SEL_TYPE = "SYNC";
+ output O;
+ input I0, I1, S;
+endmodule
+
+module BUFGMUX_CTRL (...);
+ output O;
+ input I0;
+ input I1;
+ input S;
+endmodule
+
+module BUFH (...);
+ output O;
+ input I;
+endmodule
+
+module BUFHCE (...);
+ parameter CE_TYPE = "SYNC";
+ parameter integer INIT_OUT = 0;
+ parameter [0:0] IS_CE_INVERTED = 1'b0;
+ output O;
+ input CE;
+ input I;
+endmodule
+
+module BUFIO (...);
+ output O;
+ input I;
+endmodule
+
+module BUFMR (...);
+ output O;
+ input I;
+endmodule
+
+module BUFMRCE (...);
+ parameter CE_TYPE = "SYNC";
+ parameter integer INIT_OUT = 0;
+ parameter [0:0] IS_CE_INVERTED = 1'b0;
+ output O;
+ input CE;
+ input I;
+endmodule
+
+module BUFR (...);
+ output O;
+ input CE;
+ input CLR;
+ input I;
+ parameter BUFR_DIVIDE = "BYPASS";
+ parameter SIM_DEVICE = "7SERIES";
+endmodule
+
+module CAPTUREE2 (...);
+ parameter ONESHOT = "TRUE";
+ input CAP;
+ input CLK;
+endmodule
+
+module CFGLUT5 (...);
+ parameter [31:0] INIT = 32'h00000000;
+ parameter [0:0] IS_CLK_INVERTED = 1'b0;
+ output CDO;
+ output O5;
+ output O6;
+ input I4, I3, I2, I1, I0;
+ input CDI, CE, CLK;
+endmodule
+
+module DCIRESET (...);
+ output LOCKED;
+ input RST;
+endmodule
+
+module DNA_PORT (...);
+ parameter [56:0] SIM_DNA_VALUE = 57'h0;
+ output DOUT;
+ input CLK, DIN, READ, SHIFT;
+endmodule
+
+module DSP48E1 (...);
+ parameter integer ACASCREG = 1;
+ parameter integer ADREG = 1;
+ parameter integer ALUMODEREG = 1;
+ parameter integer AREG = 1;
+ parameter AUTORESET_PATDET = "NO_RESET";
+ parameter A_INPUT = "DIRECT";
+ parameter integer BCASCREG = 1;
+ parameter integer BREG = 1;
+ parameter B_INPUT = "DIRECT";
+ parameter integer CARRYINREG = 1;
+ parameter integer CARRYINSELREG = 1;
+ parameter integer CREG = 1;
+ parameter integer DREG = 1;
+ parameter integer INMODEREG = 1;
+ parameter integer MREG = 1;
+ parameter integer OPMODEREG = 1;
+ parameter integer PREG = 1;
+ parameter SEL_MASK = "MASK";
+ parameter SEL_PATTERN = "PATTERN";
+ parameter USE_DPORT = "FALSE";
+ parameter USE_MULT = "MULTIPLY";
+ parameter USE_PATTERN_DETECT = "NO_PATDET";
+ parameter USE_SIMD = "ONE48";
+ parameter [47:0] MASK = 48'h3FFFFFFFFFFF;
+ parameter [47:0] PATTERN = 48'h000000000000;
+ parameter [3:0] IS_ALUMODE_INVERTED = 4'b0;
+ parameter [0:0] IS_CARRYIN_INVERTED = 1'b0;
+ parameter [0:0] IS_CLK_INVERTED = 1'b0;
+ parameter [4:0] IS_INMODE_INVERTED = 5'b0;
+ parameter [6:0] IS_OPMODE_INVERTED = 7'b0;
+ output [29:0] ACOUT;
+ output [17:0] BCOUT;
+ output CARRYCASCOUT;
+ output [3:0] CARRYOUT;
+ output MULTSIGNOUT;
+ output OVERFLOW;
+ output [47:0] P;
+ output PATTERNBDETECT;
+ output PATTERNDETECT;
+ output [47:0] PCOUT;
+ output UNDERFLOW;
+ input [29:0] A;
+ input [29:0] ACIN;
+ input [3:0] ALUMODE;
+ input [17:0] B;
+ input [17:0] BCIN;
+ input [47:0] C;
+ input CARRYCASCIN;
+ input CARRYIN;
+ input [2:0] CARRYINSEL;
+ input CEA1;
+ input CEA2;
+ input CEAD;
+ input CEALUMODE;
+ input CEB1;
+ input CEB2;
+ input CEC;
+ input CECARRYIN;
+ input CECTRL;
+ input CED;
+ input CEINMODE;
+ input CEM;
+ input CEP;
+ input CLK;
+ input [24:0] D;
+ input [4:0] INMODE;
+ input MULTSIGNIN;
+ input [6:0] OPMODE;
+ input [47:0] PCIN;
+ input RSTA;
+ input RSTALLCARRYIN;
+ input RSTALUMODE;
+ input RSTB;
+ input RSTC;
+ input RSTCTRL;
+ input RSTD;
+ input RSTINMODE;
+ input RSTM;
+ input RSTP;
+endmodule
+
+module EFUSE_USR (...);
+ parameter [31:0] SIM_EFUSE_VALUE = 32'h00000000;
+ output [31:0] EFUSEUSR;
+endmodule
+
+module FIFO18E1 (...);
+ parameter ALMOST_EMPTY_OFFSET = 13'h0080;
+ parameter ALMOST_FULL_OFFSET = 13'h0080;
+ parameter integer DATA_WIDTH = 4;
+ parameter integer DO_REG = 1;
+ parameter EN_SYN = "FALSE";
+ parameter FIFO_MODE = "FIFO18";
+ parameter FIRST_WORD_FALL_THROUGH = "FALSE";
+ parameter INIT = 36'h0;
+ parameter SIM_DEVICE = "VIRTEX6";
+ parameter SRVAL = 36'h0;
+ parameter IS_RDCLK_INVERTED = 1'b0;
+ parameter IS_RDEN_INVERTED = 1'b0;
+ parameter IS_RSTREG_INVERTED = 1'b0;
+ parameter IS_RST_INVERTED = 1'b0;
+ parameter IS_WRCLK_INVERTED = 1'b0;
+ parameter IS_WREN_INVERTED = 1'b0;
+ output ALMOSTEMPTY;
+ output ALMOSTFULL;
+ output [31:0] DO;
+ output [3:0] DOP;
+ output EMPTY;
+ output FULL;
+ output [11:0] RDCOUNT;
+ output RDERR;
+ output [11:0] WRCOUNT;
+ output WRERR;
+ input [31:0] DI;
+ input [3:0] DIP;
+ input RDCLK;
+ input RDEN;
+ input REGCE;
+ input RST;
+ input RSTREG;
+ input WRCLK;
+ input WREN;
+endmodule
+
+module FIFO36E1 (...);
+ parameter ALMOST_EMPTY_OFFSET = 13'h0080;
+ parameter ALMOST_FULL_OFFSET = 13'h0080;
+ parameter integer DATA_WIDTH = 4;
+ parameter integer DO_REG = 1;
+ parameter EN_ECC_READ = "FALSE";
+ parameter EN_ECC_WRITE = "FALSE";
+ parameter EN_SYN = "FALSE";
+ parameter FIFO_MODE = "FIFO36";
+ parameter FIRST_WORD_FALL_THROUGH = "FALSE";
+ parameter INIT = 72'h0;
+ parameter SIM_DEVICE = "VIRTEX6";
+ parameter SRVAL = 72'h0;
+ parameter IS_RDCLK_INVERTED = 1'b0;
+ parameter IS_RDEN_INVERTED = 1'b0;
+ parameter IS_RSTREG_INVERTED = 1'b0;
+ parameter IS_RST_INVERTED = 1'b0;
+ parameter IS_WRCLK_INVERTED = 1'b0;
+ parameter IS_WREN_INVERTED = 1'b0;
+ output ALMOSTEMPTY;
+ output ALMOSTFULL;
+ output DBITERR;
+ output [63:0] DO;
+ output [7:0] DOP;
+ output [7:0] ECCPARITY;
+ output EMPTY;
+ output FULL;
+ output [12:0] RDCOUNT;
+ output RDERR;
+ output SBITERR;
+ output [12:0] WRCOUNT;
+ output WRERR;
+ input [63:0] DI;
+ input [7:0] DIP;
+ input INJECTDBITERR;
+ input INJECTSBITERR;
+ input RDCLK;
+ input RDEN;
+ input REGCE;
+ input RST;
+ input RSTREG;
+ input WRCLK;
+ input WREN;
+endmodule
+
+module FRAME_ECCE2 (...);
+ parameter FARSRC = "EFAR";
+ parameter FRAME_RBT_IN_FILENAME = "NONE";
+ output CRCERROR;
+ output ECCERROR;
+ output ECCERRORSINGLE;
+ output SYNDROMEVALID;
+ output [12:0] SYNDROME;
+ output [25:0] FAR;
+ output [4:0] SYNBIT;
+ output [6:0] SYNWORD;
+endmodule
+
+module GTHE2_CHANNEL (...);
+ parameter [0:0] ACJTAG_DEBUG_MODE = 1'b0;
+ parameter [0:0] ACJTAG_MODE = 1'b0;
+ parameter [0:0] ACJTAG_RESET = 1'b0;
+ parameter [19:0] ADAPT_CFG0 = 20'h00C10;
+ parameter ALIGN_COMMA_DOUBLE = "FALSE";
+ parameter [9:0] ALIGN_COMMA_ENABLE = 10'b0001111111;
+ parameter integer ALIGN_COMMA_WORD = 1;
+ parameter ALIGN_MCOMMA_DET = "TRUE";
+ parameter [9:0] ALIGN_MCOMMA_VALUE = 10'b1010000011;
+ parameter ALIGN_PCOMMA_DET = "TRUE";
+ parameter [9:0] ALIGN_PCOMMA_VALUE = 10'b0101111100;
+ parameter [0:0] A_RXOSCALRESET = 1'b0;
+ parameter CBCC_DATA_SOURCE_SEL = "DECODED";
+ parameter [41:0] CFOK_CFG = 42'h24800040E80;
+ parameter [5:0] CFOK_CFG2 = 6'b100000;
+ parameter [5:0] CFOK_CFG3 = 6'b100000;
+ parameter CHAN_BOND_KEEP_ALIGN = "FALSE";
+ parameter integer CHAN_BOND_MAX_SKEW = 7;
+ parameter [9:0] CHAN_BOND_SEQ_1_1 = 10'b0101111100;
+ parameter [9:0] CHAN_BOND_SEQ_1_2 = 10'b0000000000;
+ parameter [9:0] CHAN_BOND_SEQ_1_3 = 10'b0000000000;
+ parameter [9:0] CHAN_BOND_SEQ_1_4 = 10'b0000000000;
+ parameter [3:0] CHAN_BOND_SEQ_1_ENABLE = 4'b1111;
+ parameter [9:0] CHAN_BOND_SEQ_2_1 = 10'b0100000000;
+ parameter [9:0] CHAN_BOND_SEQ_2_2 = 10'b0100000000;
+ parameter [9:0] CHAN_BOND_SEQ_2_3 = 10'b0100000000;
+ parameter [9:0] CHAN_BOND_SEQ_2_4 = 10'b0100000000;
+ parameter [3:0] CHAN_BOND_SEQ_2_ENABLE = 4'b1111;
+ parameter CHAN_BOND_SEQ_2_USE = "FALSE";
+ parameter integer CHAN_BOND_SEQ_LEN = 1;
+ parameter CLK_CORRECT_USE = "TRUE";
+ parameter CLK_COR_KEEP_IDLE = "FALSE";
+ parameter integer CLK_COR_MAX_LAT = 20;
+ parameter integer CLK_COR_MIN_LAT = 18;
+ parameter CLK_COR_PRECEDENCE = "TRUE";
+ parameter integer CLK_COR_REPEAT_WAIT = 0;
+ parameter [9:0] CLK_COR_SEQ_1_1 = 10'b0100011100;
+ parameter [9:0] CLK_COR_SEQ_1_2 = 10'b0000000000;
+ parameter [9:0] CLK_COR_SEQ_1_3 = 10'b0000000000;
+ parameter [9:0] CLK_COR_SEQ_1_4 = 10'b0000000000;
+ parameter [3:0] CLK_COR_SEQ_1_ENABLE = 4'b1111;
+ parameter [9:0] CLK_COR_SEQ_2_1 = 10'b0100000000;
+ parameter [9:0] CLK_COR_SEQ_2_2 = 10'b0100000000;
+ parameter [9:0] CLK_COR_SEQ_2_3 = 10'b0100000000;
+ parameter [9:0] CLK_COR_SEQ_2_4 = 10'b0100000000;
+ parameter [3:0] CLK_COR_SEQ_2_ENABLE = 4'b1111;
+ parameter CLK_COR_SEQ_2_USE = "FALSE";
+ parameter integer CLK_COR_SEQ_LEN = 1;
+ parameter [28:0] CPLL_CFG = 29'h00BC07DC;
+ parameter integer CPLL_FBDIV = 4;
+ parameter integer CPLL_FBDIV_45 = 5;
+ parameter [23:0] CPLL_INIT_CFG = 24'h00001E;
+ parameter [15:0] CPLL_LOCK_CFG = 16'h01E8;
+ parameter integer CPLL_REFCLK_DIV = 1;
+ parameter DEC_MCOMMA_DETECT = "TRUE";
+ parameter DEC_PCOMMA_DETECT = "TRUE";
+ parameter DEC_VALID_COMMA_ONLY = "TRUE";
+ parameter [23:0] DMONITOR_CFG = 24'h000A00;
+ parameter [0:0] ES_CLK_PHASE_SEL = 1'b0;
+ parameter [5:0] ES_CONTROL = 6'b000000;
+ parameter ES_ERRDET_EN = "FALSE";
+ parameter ES_EYE_SCAN_EN = "TRUE";
+ parameter [11:0] ES_HORZ_OFFSET = 12'h000;
+ parameter [9:0] ES_PMA_CFG = 10'b0000000000;
+ parameter [4:0] ES_PRESCALE = 5'b00000;
+ parameter [79:0] ES_QUALIFIER = 80'h00000000000000000000;
+ parameter [79:0] ES_QUAL_MASK = 80'h00000000000000000000;
+ parameter [79:0] ES_SDATA_MASK = 80'h00000000000000000000;
+ parameter [8:0] ES_VERT_OFFSET = 9'b000000000;
+ parameter [3:0] FTS_DESKEW_SEQ_ENABLE = 4'b1111;
+ parameter [3:0] FTS_LANE_DESKEW_CFG = 4'b1111;
+ parameter FTS_LANE_DESKEW_EN = "FALSE";
+ parameter [2:0] GEARBOX_MODE = 3'b000;
+ parameter [0:0] IS_CLKRSVD0_INVERTED = 1'b0;
+ parameter [0:0] IS_CLKRSVD1_INVERTED = 1'b0;
+ parameter [0:0] IS_CPLLLOCKDETCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_DMONITORCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_DRPCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_GTGREFCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_RXUSRCLK2_INVERTED = 1'b0;
+ parameter [0:0] IS_RXUSRCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_SIGVALIDCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_TXPHDLYTSTCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_TXUSRCLK2_INVERTED = 1'b0;
+ parameter [0:0] IS_TXUSRCLK_INVERTED = 1'b0;
+ parameter [0:0] LOOPBACK_CFG = 1'b0;
+ parameter [1:0] OUTREFCLK_SEL_INV = 2'b11;
+ parameter PCS_PCIE_EN = "FALSE";
+ parameter [47:0] PCS_RSVD_ATTR = 48'h000000000000;
+ parameter [11:0] PD_TRANS_TIME_FROM_P2 = 12'h03C;
+ parameter [7:0] PD_TRANS_TIME_NONE_P2 = 8'h19;
+ parameter [7:0] PD_TRANS_TIME_TO_P2 = 8'h64;
+ parameter [31:0] PMA_RSV = 32'b00000000000000000000000010000000;
+ parameter [31:0] PMA_RSV2 = 32'b00011100000000000000000000001010;
+ parameter [1:0] PMA_RSV3 = 2'b00;
+ parameter [14:0] PMA_RSV4 = 15'b000000000001000;
+ parameter [3:0] PMA_RSV5 = 4'b0000;
+ parameter [0:0] RESET_POWERSAVE_DISABLE = 1'b0;
+ parameter [4:0] RXBUFRESET_TIME = 5'b00001;
+ parameter RXBUF_ADDR_MODE = "FULL";
+ parameter [3:0] RXBUF_EIDLE_HI_CNT = 4'b1000;
+ parameter [3:0] RXBUF_EIDLE_LO_CNT = 4'b0000;
+ parameter RXBUF_EN = "TRUE";
+ parameter RXBUF_RESET_ON_CB_CHANGE = "TRUE";
+ parameter RXBUF_RESET_ON_COMMAALIGN = "FALSE";
+ parameter RXBUF_RESET_ON_EIDLE = "FALSE";
+ parameter RXBUF_RESET_ON_RATE_CHANGE = "TRUE";
+ parameter integer RXBUF_THRESH_OVFLW = 61;
+ parameter RXBUF_THRESH_OVRD = "FALSE";
+ parameter integer RXBUF_THRESH_UNDFLW = 4;
+ parameter [4:0] RXCDRFREQRESET_TIME = 5'b00001;
+ parameter [4:0] RXCDRPHRESET_TIME = 5'b00001;
+ parameter [82:0] RXCDR_CFG = 83'h0002007FE2000C208001A;
+ parameter [0:0] RXCDR_FR_RESET_ON_EIDLE = 1'b0;
+ parameter [0:0] RXCDR_HOLD_DURING_EIDLE = 1'b0;
+ parameter [5:0] RXCDR_LOCK_CFG = 6'b001001;
+ parameter [0:0] RXCDR_PH_RESET_ON_EIDLE = 1'b0;
+ parameter [6:0] RXDFELPMRESET_TIME = 7'b0001111;
+ parameter [15:0] RXDLY_CFG = 16'h001F;
+ parameter [8:0] RXDLY_LCFG = 9'h030;
+ parameter [15:0] RXDLY_TAP_CFG = 16'h0000;
+ parameter RXGEARBOX_EN = "FALSE";
+ parameter [4:0] RXISCANRESET_TIME = 5'b00001;
+ parameter [13:0] RXLPM_HF_CFG = 14'b00001000000000;
+ parameter [17:0] RXLPM_LF_CFG = 18'b001001000000000000;
+ parameter [6:0] RXOOB_CFG = 7'b0000110;
+ parameter RXOOB_CLK_CFG = "PMA";
+ parameter [4:0] RXOSCALRESET_TIME = 5'b00011;
+ parameter [4:0] RXOSCALRESET_TIMEOUT = 5'b00000;
+ parameter integer RXOUT_DIV = 2;
+ parameter [4:0] RXPCSRESET_TIME = 5'b00001;
+ parameter [23:0] RXPHDLY_CFG = 24'h084020;
+ parameter [23:0] RXPH_CFG = 24'hC00002;
+ parameter [4:0] RXPH_MONITOR_SEL = 5'b00000;
+ parameter [1:0] RXPI_CFG0 = 2'b00;
+ parameter [1:0] RXPI_CFG1 = 2'b00;
+ parameter [1:0] RXPI_CFG2 = 2'b00;
+ parameter [1:0] RXPI_CFG3 = 2'b00;
+ parameter [0:0] RXPI_CFG4 = 1'b0;
+ parameter [0:0] RXPI_CFG5 = 1'b0;
+ parameter [2:0] RXPI_CFG6 = 3'b100;
+ parameter [4:0] RXPMARESET_TIME = 5'b00011;
+ parameter [0:0] RXPRBS_ERR_LOOPBACK = 1'b0;
+ parameter integer RXSLIDE_AUTO_WAIT = 7;
+ parameter RXSLIDE_MODE = "OFF";
+ parameter [0:0] RXSYNC_MULTILANE = 1'b0;
+ parameter [0:0] RXSYNC_OVRD = 1'b0;
+ parameter [0:0] RXSYNC_SKIP_DA = 1'b0;
+ parameter [23:0] RX_BIAS_CFG = 24'b000011000000000000010000;
+ parameter [5:0] RX_BUFFER_CFG = 6'b000000;
+ parameter integer RX_CLK25_DIV = 7;
+ parameter [0:0] RX_CLKMUX_PD = 1'b1;
+ parameter [1:0] RX_CM_SEL = 2'b11;
+ parameter [3:0] RX_CM_TRIM = 4'b0100;
+ parameter integer RX_DATA_WIDTH = 20;
+ parameter [5:0] RX_DDI_SEL = 6'b000000;
+ parameter [13:0] RX_DEBUG_CFG = 14'b00000000000000;
+ parameter RX_DEFER_RESET_BUF_EN = "TRUE";
+ parameter [3:0] RX_DFELPM_CFG0 = 4'b0110;
+ parameter [0:0] RX_DFELPM_CFG1 = 1'b0;
+ parameter [0:0] RX_DFELPM_KLKH_AGC_STUP_EN = 1'b1;
+ parameter [1:0] RX_DFE_AGC_CFG0 = 2'b00;
+ parameter [2:0] RX_DFE_AGC_CFG1 = 3'b010;
+ parameter [3:0] RX_DFE_AGC_CFG2 = 4'b0000;
+ parameter [0:0] RX_DFE_AGC_OVRDEN = 1'b1;
+ parameter [22:0] RX_DFE_GAIN_CFG = 23'h0020C0;
+ parameter [11:0] RX_DFE_H2_CFG = 12'b000000000000;
+ parameter [11:0] RX_DFE_H3_CFG = 12'b000001000000;
+ parameter [10:0] RX_DFE_H4_CFG = 11'b00011100000;
+ parameter [10:0] RX_DFE_H5_CFG = 11'b00011100000;
+ parameter [10:0] RX_DFE_H6_CFG = 11'b00000100000;
+ parameter [10:0] RX_DFE_H7_CFG = 11'b00000100000;
+ parameter [32:0] RX_DFE_KL_CFG = 33'b000000000000000000000001100010000;
+ parameter [1:0] RX_DFE_KL_LPM_KH_CFG0 = 2'b01;
+ parameter [2:0] RX_DFE_KL_LPM_KH_CFG1 = 3'b010;
+ parameter [3:0] RX_DFE_KL_LPM_KH_CFG2 = 4'b0010;
+ parameter [0:0] RX_DFE_KL_LPM_KH_OVRDEN = 1'b1;
+ parameter [1:0] RX_DFE_KL_LPM_KL_CFG0 = 2'b10;
+ parameter [2:0] RX_DFE_KL_LPM_KL_CFG1 = 3'b010;
+ parameter [3:0] RX_DFE_KL_LPM_KL_CFG2 = 4'b0010;
+ parameter [0:0] RX_DFE_KL_LPM_KL_OVRDEN = 1'b1;
+ parameter [15:0] RX_DFE_LPM_CFG = 16'h0080;
+ parameter [0:0] RX_DFE_LPM_HOLD_DURING_EIDLE = 1'b0;
+ parameter [53:0] RX_DFE_ST_CFG = 54'h00E100000C003F;
+ parameter [16:0] RX_DFE_UT_CFG = 17'b00011100000000000;
+ parameter [16:0] RX_DFE_VP_CFG = 17'b00011101010100011;
+ parameter RX_DISPERR_SEQ_MATCH = "TRUE";
+ parameter integer RX_INT_DATAWIDTH = 0;
+ parameter [12:0] RX_OS_CFG = 13'b0000010000000;
+ parameter integer RX_SIG_VALID_DLY = 10;
+ parameter RX_XCLK_SEL = "RXREC";
+ parameter integer SAS_MAX_COM = 64;
+ parameter integer SAS_MIN_COM = 36;
+ parameter [3:0] SATA_BURST_SEQ_LEN = 4'b1111;
+ parameter [2:0] SATA_BURST_VAL = 3'b100;
+ parameter SATA_CPLL_CFG = "VCO_3000MHZ";
+ parameter [2:0] SATA_EIDLE_VAL = 3'b100;
+ parameter integer SATA_MAX_BURST = 8;
+ parameter integer SATA_MAX_INIT = 21;
+ parameter integer SATA_MAX_WAKE = 7;
+ parameter integer SATA_MIN_BURST = 4;
+ parameter integer SATA_MIN_INIT = 12;
+ parameter integer SATA_MIN_WAKE = 4;
+ parameter SHOW_REALIGN_COMMA = "TRUE";
+ parameter [2:0] SIM_CPLLREFCLK_SEL = 3'b001;
+ parameter SIM_RECEIVER_DETECT_PASS = "TRUE";
+ parameter SIM_RESET_SPEEDUP = "TRUE";
+ parameter SIM_TX_EIDLE_DRIVE_LEVEL = "X";
+ parameter SIM_VERSION = "1.1";
+ parameter [14:0] TERM_RCAL_CFG = 15'b100001000010000;
+ parameter [2:0] TERM_RCAL_OVRD = 3'b000;
+ parameter [7:0] TRANS_TIME_RATE = 8'h0E;
+ parameter [31:0] TST_RSV = 32'h00000000;
+ parameter TXBUF_EN = "TRUE";
+ parameter TXBUF_RESET_ON_RATE_CHANGE = "FALSE";
+ parameter [15:0] TXDLY_CFG = 16'h001F;
+ parameter [8:0] TXDLY_LCFG = 9'h030;
+ parameter [15:0] TXDLY_TAP_CFG = 16'h0000;
+ parameter TXGEARBOX_EN = "FALSE";
+ parameter [0:0] TXOOB_CFG = 1'b0;
+ parameter integer TXOUT_DIV = 2;
+ parameter [4:0] TXPCSRESET_TIME = 5'b00001;
+ parameter [23:0] TXPHDLY_CFG = 24'h084020;
+ parameter [15:0] TXPH_CFG = 16'h0780;
+ parameter [4:0] TXPH_MONITOR_SEL = 5'b00000;
+ parameter [1:0] TXPI_CFG0 = 2'b00;
+ parameter [1:0] TXPI_CFG1 = 2'b00;
+ parameter [1:0] TXPI_CFG2 = 2'b00;
+ parameter [0:0] TXPI_CFG3 = 1'b0;
+ parameter [0:0] TXPI_CFG4 = 1'b0;
+ parameter [2:0] TXPI_CFG5 = 3'b100;
+ parameter [0:0] TXPI_GREY_SEL = 1'b0;
+ parameter [0:0] TXPI_INVSTROBE_SEL = 1'b0;
+ parameter TXPI_PPMCLK_SEL = "TXUSRCLK2";
+ parameter [7:0] TXPI_PPM_CFG = 8'b00000000;
+ parameter [2:0] TXPI_SYNFREQ_PPM = 3'b000;
+ parameter [4:0] TXPMARESET_TIME = 5'b00001;
+ parameter [0:0] TXSYNC_MULTILANE = 1'b0;
+ parameter [0:0] TXSYNC_OVRD = 1'b0;
+ parameter [0:0] TXSYNC_SKIP_DA = 1'b0;
+ parameter integer TX_CLK25_DIV = 7;
+ parameter [0:0] TX_CLKMUX_PD = 1'b1;
+ parameter integer TX_DATA_WIDTH = 20;
+ parameter [5:0] TX_DEEMPH0 = 6'b000000;
+ parameter [5:0] TX_DEEMPH1 = 6'b000000;
+ parameter TX_DRIVE_MODE = "DIRECT";
+ parameter [2:0] TX_EIDLE_ASSERT_DELAY = 3'b110;
+ parameter [2:0] TX_EIDLE_DEASSERT_DELAY = 3'b100;
+ parameter integer TX_INT_DATAWIDTH = 0;
+ parameter TX_LOOPBACK_DRIVE_HIZ = "FALSE";
+ parameter [0:0] TX_MAINCURSOR_SEL = 1'b0;
+ parameter [6:0] TX_MARGIN_FULL_0 = 7'b1001110;
+ parameter [6:0] TX_MARGIN_FULL_1 = 7'b1001001;
+ parameter [6:0] TX_MARGIN_FULL_2 = 7'b1000101;
+ parameter [6:0] TX_MARGIN_FULL_3 = 7'b1000010;
+ parameter [6:0] TX_MARGIN_FULL_4 = 7'b1000000;
+ parameter [6:0] TX_MARGIN_LOW_0 = 7'b1000110;
+ parameter [6:0] TX_MARGIN_LOW_1 = 7'b1000100;
+ parameter [6:0] TX_MARGIN_LOW_2 = 7'b1000010;
+ parameter [6:0] TX_MARGIN_LOW_3 = 7'b1000000;
+ parameter [6:0] TX_MARGIN_LOW_4 = 7'b1000000;
+ parameter [0:0] TX_QPI_STATUS_EN = 1'b0;
+ parameter [13:0] TX_RXDETECT_CFG = 14'h1832;
+ parameter [16:0] TX_RXDETECT_PRECHARGE_TIME = 17'h00000;
+ parameter [2:0] TX_RXDETECT_REF = 3'b100;
+ parameter TX_XCLK_SEL = "TXUSR";
+ parameter [0:0] UCODEER_CLR = 1'b0;
+ parameter [0:0] USE_PCS_CLK_PHASE_SEL = 1'b0;
+ output CPLLFBCLKLOST;
+ output CPLLLOCK;
+ output CPLLREFCLKLOST;
+ output DRPRDY;
+ output EYESCANDATAERROR;
+ output GTHTXN;
+ output GTHTXP;
+ output GTREFCLKMONITOR;
+ output PHYSTATUS;
+ output RSOSINTDONE;
+ output RXBYTEISALIGNED;
+ output RXBYTEREALIGN;
+ output RXCDRLOCK;
+ output RXCHANBONDSEQ;
+ output RXCHANISALIGNED;
+ output RXCHANREALIGN;
+ output RXCOMINITDET;
+ output RXCOMMADET;
+ output RXCOMSASDET;
+ output RXCOMWAKEDET;
+ output RXDFESLIDETAPSTARTED;
+ output RXDFESLIDETAPSTROBEDONE;
+ output RXDFESLIDETAPSTROBESTARTED;
+ output RXDFESTADAPTDONE;
+ output RXDLYSRESETDONE;
+ output RXELECIDLE;
+ output RXOSINTSTARTED;
+ output RXOSINTSTROBEDONE;
+ output RXOSINTSTROBESTARTED;
+ output RXOUTCLK;
+ output RXOUTCLKFABRIC;
+ output RXOUTCLKPCS;
+ output RXPHALIGNDONE;
+ output RXPMARESETDONE;
+ output RXPRBSERR;
+ output RXQPISENN;
+ output RXQPISENP;
+ output RXRATEDONE;
+ output RXRESETDONE;
+ output RXSYNCDONE;
+ output RXSYNCOUT;
+ output RXVALID;
+ output TXCOMFINISH;
+ output TXDLYSRESETDONE;
+ output TXGEARBOXREADY;
+ output TXOUTCLK;
+ output TXOUTCLKFABRIC;
+ output TXOUTCLKPCS;
+ output TXPHALIGNDONE;
+ output TXPHINITDONE;
+ output TXPMARESETDONE;
+ output TXQPISENN;
+ output TXQPISENP;
+ output TXRATEDONE;
+ output TXRESETDONE;
+ output TXSYNCDONE;
+ output TXSYNCOUT;
+ output [14:0] DMONITOROUT;
+ output [15:0] DRPDO;
+ output [15:0] PCSRSVDOUT;
+ output [1:0] RXCLKCORCNT;
+ output [1:0] RXDATAVALID;
+ output [1:0] RXHEADERVALID;
+ output [1:0] RXSTARTOFSEQ;
+ output [1:0] TXBUFSTATUS;
+ output [2:0] RXBUFSTATUS;
+ output [2:0] RXSTATUS;
+ output [4:0] RXCHBONDO;
+ output [4:0] RXPHMONITOR;
+ output [4:0] RXPHSLIPMONITOR;
+ output [5:0] RXHEADER;
+ output [63:0] RXDATA;
+ output [6:0] RXMONITOROUT;
+ output [7:0] RXCHARISCOMMA;
+ output [7:0] RXCHARISK;
+ output [7:0] RXDISPERR;
+ output [7:0] RXNOTINTABLE;
+ input CFGRESET;
+ input CLKRSVD0;
+ input CLKRSVD1;
+ input CPLLLOCKDETCLK;
+ input CPLLLOCKEN;
+ input CPLLPD;
+ input CPLLRESET;
+ input DMONFIFORESET;
+ input DMONITORCLK;
+ input DRPCLK;
+ input DRPEN;
+ input DRPWE;
+ input EYESCANMODE;
+ input EYESCANRESET;
+ input EYESCANTRIGGER;
+ input GTGREFCLK;
+ input GTHRXN;
+ input GTHRXP;
+ input GTNORTHREFCLK0;
+ input GTNORTHREFCLK1;
+ input GTREFCLK0;
+ input GTREFCLK1;
+ input GTRESETSEL;
+ input GTRXRESET;
+ input GTSOUTHREFCLK0;
+ input GTSOUTHREFCLK1;
+ input GTTXRESET;
+ input QPLLCLK;
+ input QPLLREFCLK;
+ input RESETOVRD;
+ input RX8B10BEN;
+ input RXBUFRESET;
+ input RXCDRFREQRESET;
+ input RXCDRHOLD;
+ input RXCDROVRDEN;
+ input RXCDRRESET;
+ input RXCDRRESETRSV;
+ input RXCHBONDEN;
+ input RXCHBONDMASTER;
+ input RXCHBONDSLAVE;
+ input RXCOMMADETEN;
+ input RXDDIEN;
+ input RXDFEAGCHOLD;
+ input RXDFEAGCOVRDEN;
+ input RXDFECM1EN;
+ input RXDFELFHOLD;
+ input RXDFELFOVRDEN;
+ input RXDFELPMRESET;
+ input RXDFESLIDETAPADAPTEN;
+ input RXDFESLIDETAPHOLD;
+ input RXDFESLIDETAPINITOVRDEN;
+ input RXDFESLIDETAPONLYADAPTEN;
+ input RXDFESLIDETAPOVRDEN;
+ input RXDFESLIDETAPSTROBE;
+ input RXDFETAP2HOLD;
+ input RXDFETAP2OVRDEN;
+ input RXDFETAP3HOLD;
+ input RXDFETAP3OVRDEN;
+ input RXDFETAP4HOLD;
+ input RXDFETAP4OVRDEN;
+ input RXDFETAP5HOLD;
+ input RXDFETAP5OVRDEN;
+ input RXDFETAP6HOLD;
+ input RXDFETAP6OVRDEN;
+ input RXDFETAP7HOLD;
+ input RXDFETAP7OVRDEN;
+ input RXDFEUTHOLD;
+ input RXDFEUTOVRDEN;
+ input RXDFEVPHOLD;
+ input RXDFEVPOVRDEN;
+ input RXDFEVSEN;
+ input RXDFEXYDEN;
+ input RXDLYBYPASS;
+ input RXDLYEN;
+ input RXDLYOVRDEN;
+ input RXDLYSRESET;
+ input RXGEARBOXSLIP;
+ input RXLPMEN;
+ input RXLPMHFHOLD;
+ input RXLPMHFOVRDEN;
+ input RXLPMLFHOLD;
+ input RXLPMLFKLOVRDEN;
+ input RXMCOMMAALIGNEN;
+ input RXOOBRESET;
+ input RXOSCALRESET;
+ input RXOSHOLD;
+ input RXOSINTEN;
+ input RXOSINTHOLD;
+ input RXOSINTNTRLEN;
+ input RXOSINTOVRDEN;
+ input RXOSINTSTROBE;
+ input RXOSINTTESTOVRDEN;
+ input RXOSOVRDEN;
+ input RXPCOMMAALIGNEN;
+ input RXPCSRESET;
+ input RXPHALIGN;
+ input RXPHALIGNEN;
+ input RXPHDLYPD;
+ input RXPHDLYRESET;
+ input RXPHOVRDEN;
+ input RXPMARESET;
+ input RXPOLARITY;
+ input RXPRBSCNTRESET;
+ input RXQPIEN;
+ input RXRATEMODE;
+ input RXSLIDE;
+ input RXSYNCALLIN;
+ input RXSYNCIN;
+ input RXSYNCMODE;
+ input RXUSERRDY;
+ input RXUSRCLK2;
+ input RXUSRCLK;
+ input SETERRSTATUS;
+ input SIGVALIDCLK;
+ input TX8B10BEN;
+ input TXCOMINIT;
+ input TXCOMSAS;
+ input TXCOMWAKE;
+ input TXDEEMPH;
+ input TXDETECTRX;
+ input TXDIFFPD;
+ input TXDLYBYPASS;
+ input TXDLYEN;
+ input TXDLYHOLD;
+ input TXDLYOVRDEN;
+ input TXDLYSRESET;
+ input TXDLYUPDOWN;
+ input TXELECIDLE;
+ input TXINHIBIT;
+ input TXPCSRESET;
+ input TXPDELECIDLEMODE;
+ input TXPHALIGN;
+ input TXPHALIGNEN;
+ input TXPHDLYPD;
+ input TXPHDLYRESET;
+ input TXPHDLYTSTCLK;
+ input TXPHINIT;
+ input TXPHOVRDEN;
+ input TXPIPPMEN;
+ input TXPIPPMOVRDEN;
+ input TXPIPPMPD;
+ input TXPIPPMSEL;
+ input TXPISOPD;
+ input TXPMARESET;
+ input TXPOLARITY;
+ input TXPOSTCURSORINV;
+ input TXPRBSFORCEERR;
+ input TXPRECURSORINV;
+ input TXQPIBIASEN;
+ input TXQPISTRONGPDOWN;
+ input TXQPIWEAKPUP;
+ input TXRATEMODE;
+ input TXSTARTSEQ;
+ input TXSWING;
+ input TXSYNCALLIN;
+ input TXSYNCIN;
+ input TXSYNCMODE;
+ input TXUSERRDY;
+ input TXUSRCLK2;
+ input TXUSRCLK;
+ input [13:0] RXADAPTSELTEST;
+ input [15:0] DRPDI;
+ input [15:0] GTRSVD;
+ input [15:0] PCSRSVDIN;
+ input [19:0] TSTIN;
+ input [1:0] RXELECIDLEMODE;
+ input [1:0] RXMONITORSEL;
+ input [1:0] RXPD;
+ input [1:0] RXSYSCLKSEL;
+ input [1:0] TXPD;
+ input [1:0] TXSYSCLKSEL;
+ input [2:0] CPLLREFCLKSEL;
+ input [2:0] LOOPBACK;
+ input [2:0] RXCHBONDLEVEL;
+ input [2:0] RXOUTCLKSEL;
+ input [2:0] RXPRBSSEL;
+ input [2:0] RXRATE;
+ input [2:0] TXBUFDIFFCTRL;
+ input [2:0] TXHEADER;
+ input [2:0] TXMARGIN;
+ input [2:0] TXOUTCLKSEL;
+ input [2:0] TXPRBSSEL;
+ input [2:0] TXRATE;
+ input [3:0] RXOSINTCFG;
+ input [3:0] RXOSINTID0;
+ input [3:0] TXDIFFCTRL;
+ input [4:0] PCSRSVDIN2;
+ input [4:0] PMARSVDIN;
+ input [4:0] RXCHBONDI;
+ input [4:0] RXDFEAGCTRL;
+ input [4:0] RXDFESLIDETAP;
+ input [4:0] TXPIPPMSTEPSIZE;
+ input [4:0] TXPOSTCURSOR;
+ input [4:0] TXPRECURSOR;
+ input [5:0] RXDFESLIDETAPID;
+ input [63:0] TXDATA;
+ input [6:0] TXMAINCURSOR;
+ input [6:0] TXSEQUENCE;
+ input [7:0] TX8B10BBYPASS;
+ input [7:0] TXCHARDISPMODE;
+ input [7:0] TXCHARDISPVAL;
+ input [7:0] TXCHARISK;
+ input [8:0] DRPADDR;
+endmodule
+
+module GTHE2_COMMON (...);
+ parameter [63:0] BIAS_CFG = 64'h0000040000001000;
+ parameter [31:0] COMMON_CFG = 32'h0000001C;
+ parameter [0:0] IS_DRPCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_GTGREFCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_QPLLLOCKDETCLK_INVERTED = 1'b0;
+ parameter [26:0] QPLL_CFG = 27'h0480181;
+ parameter [3:0] QPLL_CLKOUT_CFG = 4'b0000;
+ parameter [5:0] QPLL_COARSE_FREQ_OVRD = 6'b010000;
+ parameter [0:0] QPLL_COARSE_FREQ_OVRD_EN = 1'b0;
+ parameter [9:0] QPLL_CP = 10'b0000011111;
+ parameter [0:0] QPLL_CP_MONITOR_EN = 1'b0;
+ parameter [0:0] QPLL_DMONITOR_SEL = 1'b0;
+ parameter [9:0] QPLL_FBDIV = 10'b0000000000;
+ parameter [0:0] QPLL_FBDIV_MONITOR_EN = 1'b0;
+ parameter [0:0] QPLL_FBDIV_RATIO = 1'b0;
+ parameter [23:0] QPLL_INIT_CFG = 24'h000006;
+ parameter [15:0] QPLL_LOCK_CFG = 16'h01E8;
+ parameter [3:0] QPLL_LPF = 4'b1111;
+ parameter integer QPLL_REFCLK_DIV = 2;
+ parameter [0:0] QPLL_RP_COMP = 1'b0;
+ parameter [1:0] QPLL_VTRL_RESET = 2'b00;
+ parameter [1:0] RCAL_CFG = 2'b00;
+ parameter [15:0] RSVD_ATTR0 = 16'h0000;
+ parameter [15:0] RSVD_ATTR1 = 16'h0000;
+ parameter [2:0] SIM_QPLLREFCLK_SEL = 3'b001;
+ parameter SIM_RESET_SPEEDUP = "TRUE";
+ parameter SIM_VERSION = "1.1";
+ output DRPRDY;
+ output QPLLFBCLKLOST;
+ output QPLLLOCK;
+ output QPLLOUTCLK;
+ output QPLLOUTREFCLK;
+ output QPLLREFCLKLOST;
+ output REFCLKOUTMONITOR;
+ output [15:0] DRPDO;
+ output [15:0] PMARSVDOUT;
+ output [7:0] QPLLDMONITOR;
+ input BGBYPASSB;
+ input BGMONITORENB;
+ input BGPDB;
+ input BGRCALOVRDENB;
+ input DRPCLK;
+ input DRPEN;
+ input DRPWE;
+ input GTGREFCLK;
+ input GTNORTHREFCLK0;
+ input GTNORTHREFCLK1;
+ input GTREFCLK0;
+ input GTREFCLK1;
+ input GTSOUTHREFCLK0;
+ input GTSOUTHREFCLK1;
+ input QPLLLOCKDETCLK;
+ input QPLLLOCKEN;
+ input QPLLOUTRESET;
+ input QPLLPD;
+ input QPLLRESET;
+ input RCALENB;
+ input [15:0] DRPDI;
+ input [15:0] QPLLRSVD1;
+ input [2:0] QPLLREFCLKSEL;
+ input [4:0] BGRCALOVRD;
+ input [4:0] QPLLRSVD2;
+ input [7:0] DRPADDR;
+ input [7:0] PMARSVD;
+endmodule
+
+module GTPE2_CHANNEL (...);
+ parameter [0:0] ACJTAG_DEBUG_MODE = 1'b0;
+ parameter [0:0] ACJTAG_MODE = 1'b0;
+ parameter [0:0] ACJTAG_RESET = 1'b0;
+ parameter [19:0] ADAPT_CFG0 = 20'b00000000000000000000;
+ parameter ALIGN_COMMA_DOUBLE = "FALSE";
+ parameter [9:0] ALIGN_COMMA_ENABLE = 10'b0001111111;
+ parameter integer ALIGN_COMMA_WORD = 1;
+ parameter ALIGN_MCOMMA_DET = "TRUE";
+ parameter [9:0] ALIGN_MCOMMA_VALUE = 10'b1010000011;
+ parameter ALIGN_PCOMMA_DET = "TRUE";
+ parameter [9:0] ALIGN_PCOMMA_VALUE = 10'b0101111100;
+ parameter CBCC_DATA_SOURCE_SEL = "DECODED";
+ parameter [42:0] CFOK_CFG = 43'b1001001000000000000000001000000111010000000;
+ parameter [6:0] CFOK_CFG2 = 7'b0100000;
+ parameter [6:0] CFOK_CFG3 = 7'b0100000;
+ parameter [0:0] CFOK_CFG4 = 1'b0;
+ parameter [1:0] CFOK_CFG5 = 2'b00;
+ parameter [3:0] CFOK_CFG6 = 4'b0000;
+ parameter CHAN_BOND_KEEP_ALIGN = "FALSE";
+ parameter integer CHAN_BOND_MAX_SKEW = 7;
+ parameter [9:0] CHAN_BOND_SEQ_1_1 = 10'b0101111100;
+ parameter [9:0] CHAN_BOND_SEQ_1_2 = 10'b0000000000;
+ parameter [9:0] CHAN_BOND_SEQ_1_3 = 10'b0000000000;
+ parameter [9:0] CHAN_BOND_SEQ_1_4 = 10'b0000000000;
+ parameter [3:0] CHAN_BOND_SEQ_1_ENABLE = 4'b1111;
+ parameter [9:0] CHAN_BOND_SEQ_2_1 = 10'b0100000000;
+ parameter [9:0] CHAN_BOND_SEQ_2_2 = 10'b0100000000;
+ parameter [9:0] CHAN_BOND_SEQ_2_3 = 10'b0100000000;
+ parameter [9:0] CHAN_BOND_SEQ_2_4 = 10'b0100000000;
+ parameter [3:0] CHAN_BOND_SEQ_2_ENABLE = 4'b1111;
+ parameter CHAN_BOND_SEQ_2_USE = "FALSE";
+ parameter integer CHAN_BOND_SEQ_LEN = 1;
+ parameter [0:0] CLK_COMMON_SWING = 1'b0;
+ parameter CLK_CORRECT_USE = "TRUE";
+ parameter CLK_COR_KEEP_IDLE = "FALSE";
+ parameter integer CLK_COR_MAX_LAT = 20;
+ parameter integer CLK_COR_MIN_LAT = 18;
+ parameter CLK_COR_PRECEDENCE = "TRUE";
+ parameter integer CLK_COR_REPEAT_WAIT = 0;
+ parameter [9:0] CLK_COR_SEQ_1_1 = 10'b0100011100;
+ parameter [9:0] CLK_COR_SEQ_1_2 = 10'b0000000000;
+ parameter [9:0] CLK_COR_SEQ_1_3 = 10'b0000000000;
+ parameter [9:0] CLK_COR_SEQ_1_4 = 10'b0000000000;
+ parameter [3:0] CLK_COR_SEQ_1_ENABLE = 4'b1111;
+ parameter [9:0] CLK_COR_SEQ_2_1 = 10'b0100000000;
+ parameter [9:0] CLK_COR_SEQ_2_2 = 10'b0100000000;
+ parameter [9:0] CLK_COR_SEQ_2_3 = 10'b0100000000;
+ parameter [9:0] CLK_COR_SEQ_2_4 = 10'b0100000000;
+ parameter [3:0] CLK_COR_SEQ_2_ENABLE = 4'b1111;
+ parameter CLK_COR_SEQ_2_USE = "FALSE";
+ parameter integer CLK_COR_SEQ_LEN = 1;
+ parameter DEC_MCOMMA_DETECT = "TRUE";
+ parameter DEC_PCOMMA_DETECT = "TRUE";
+ parameter DEC_VALID_COMMA_ONLY = "TRUE";
+ parameter [23:0] DMONITOR_CFG = 24'h000A00;
+ parameter [0:0] ES_CLK_PHASE_SEL = 1'b0;
+ parameter [5:0] ES_CONTROL = 6'b000000;
+ parameter ES_ERRDET_EN = "FALSE";
+ parameter ES_EYE_SCAN_EN = "FALSE";
+ parameter [11:0] ES_HORZ_OFFSET = 12'h010;
+ parameter [9:0] ES_PMA_CFG = 10'b0000000000;
+ parameter [4:0] ES_PRESCALE = 5'b00000;
+ parameter [79:0] ES_QUALIFIER = 80'h00000000000000000000;
+ parameter [79:0] ES_QUAL_MASK = 80'h00000000000000000000;
+ parameter [79:0] ES_SDATA_MASK = 80'h00000000000000000000;
+ parameter [8:0] ES_VERT_OFFSET = 9'b000000000;
+ parameter [3:0] FTS_DESKEW_SEQ_ENABLE = 4'b1111;
+ parameter [3:0] FTS_LANE_DESKEW_CFG = 4'b1111;
+ parameter FTS_LANE_DESKEW_EN = "FALSE";
+ parameter [2:0] GEARBOX_MODE = 3'b000;
+ parameter [0:0] IS_CLKRSVD0_INVERTED = 1'b0;
+ parameter [0:0] IS_CLKRSVD1_INVERTED = 1'b0;
+ parameter [0:0] IS_DMONITORCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_DRPCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_RXUSRCLK2_INVERTED = 1'b0;
+ parameter [0:0] IS_RXUSRCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_SIGVALIDCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_TXPHDLYTSTCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_TXUSRCLK2_INVERTED = 1'b0;
+ parameter [0:0] IS_TXUSRCLK_INVERTED = 1'b0;
+ parameter [0:0] LOOPBACK_CFG = 1'b0;
+ parameter [1:0] OUTREFCLK_SEL_INV = 2'b11;
+ parameter PCS_PCIE_EN = "FALSE";
+ parameter [47:0] PCS_RSVD_ATTR = 48'h000000000000;
+ parameter [11:0] PD_TRANS_TIME_FROM_P2 = 12'h03C;
+ parameter [7:0] PD_TRANS_TIME_NONE_P2 = 8'h19;
+ parameter [7:0] PD_TRANS_TIME_TO_P2 = 8'h64;
+ parameter [0:0] PMA_LOOPBACK_CFG = 1'b0;
+ parameter [31:0] PMA_RSV = 32'h00000333;
+ parameter [31:0] PMA_RSV2 = 32'h00002050;
+ parameter [1:0] PMA_RSV3 = 2'b00;
+ parameter [3:0] PMA_RSV4 = 4'b0000;
+ parameter [0:0] PMA_RSV5 = 1'b0;
+ parameter [0:0] PMA_RSV6 = 1'b0;
+ parameter [0:0] PMA_RSV7 = 1'b0;
+ parameter [4:0] RXBUFRESET_TIME = 5'b00001;
+ parameter RXBUF_ADDR_MODE = "FULL";
+ parameter [3:0] RXBUF_EIDLE_HI_CNT = 4'b1000;
+ parameter [3:0] RXBUF_EIDLE_LO_CNT = 4'b0000;
+ parameter RXBUF_EN = "TRUE";
+ parameter RXBUF_RESET_ON_CB_CHANGE = "TRUE";
+ parameter RXBUF_RESET_ON_COMMAALIGN = "FALSE";
+ parameter RXBUF_RESET_ON_EIDLE = "FALSE";
+ parameter RXBUF_RESET_ON_RATE_CHANGE = "TRUE";
+ parameter integer RXBUF_THRESH_OVFLW = 61;
+ parameter RXBUF_THRESH_OVRD = "FALSE";
+ parameter integer RXBUF_THRESH_UNDFLW = 4;
+ parameter [4:0] RXCDRFREQRESET_TIME = 5'b00001;
+ parameter [4:0] RXCDRPHRESET_TIME = 5'b00001;
+ parameter [82:0] RXCDR_CFG = 83'h0000107FE406001041010;
+ parameter [0:0] RXCDR_FR_RESET_ON_EIDLE = 1'b0;
+ parameter [0:0] RXCDR_HOLD_DURING_EIDLE = 1'b0;
+ parameter [5:0] RXCDR_LOCK_CFG = 6'b001001;
+ parameter [0:0] RXCDR_PH_RESET_ON_EIDLE = 1'b0;
+ parameter [15:0] RXDLY_CFG = 16'h0010;
+ parameter [8:0] RXDLY_LCFG = 9'h020;
+ parameter [15:0] RXDLY_TAP_CFG = 16'h0000;
+ parameter RXGEARBOX_EN = "FALSE";
+ parameter [4:0] RXISCANRESET_TIME = 5'b00001;
+ parameter [6:0] RXLPMRESET_TIME = 7'b0001111;
+ parameter [0:0] RXLPM_BIAS_STARTUP_DISABLE = 1'b0;
+ parameter [3:0] RXLPM_CFG = 4'b0110;
+ parameter [0:0] RXLPM_CFG1 = 1'b0;
+ parameter [0:0] RXLPM_CM_CFG = 1'b0;
+ parameter [8:0] RXLPM_GC_CFG = 9'b111100010;
+ parameter [2:0] RXLPM_GC_CFG2 = 3'b001;
+ parameter [13:0] RXLPM_HF_CFG = 14'b00001111110000;
+ parameter [4:0] RXLPM_HF_CFG2 = 5'b01010;
+ parameter [3:0] RXLPM_HF_CFG3 = 4'b0000;
+ parameter [0:0] RXLPM_HOLD_DURING_EIDLE = 1'b0;
+ parameter [0:0] RXLPM_INCM_CFG = 1'b0;
+ parameter [0:0] RXLPM_IPCM_CFG = 1'b0;
+ parameter [17:0] RXLPM_LF_CFG = 18'b000000001111110000;
+ parameter [4:0] RXLPM_LF_CFG2 = 5'b01010;
+ parameter [2:0] RXLPM_OSINT_CFG = 3'b100;
+ parameter [6:0] RXOOB_CFG = 7'b0000110;
+ parameter RXOOB_CLK_CFG = "PMA";
+ parameter [4:0] RXOSCALRESET_TIME = 5'b00011;
+ parameter [4:0] RXOSCALRESET_TIMEOUT = 5'b00000;
+ parameter integer RXOUT_DIV = 2;
+ parameter [4:0] RXPCSRESET_TIME = 5'b00001;
+ parameter [23:0] RXPHDLY_CFG = 24'h084000;
+ parameter [23:0] RXPH_CFG = 24'hC00002;
+ parameter [4:0] RXPH_MONITOR_SEL = 5'b00000;
+ parameter [2:0] RXPI_CFG0 = 3'b000;
+ parameter [0:0] RXPI_CFG1 = 1'b0;
+ parameter [0:0] RXPI_CFG2 = 1'b0;
+ parameter [4:0] RXPMARESET_TIME = 5'b00011;
+ parameter [0:0] RXPRBS_ERR_LOOPBACK = 1'b0;
+ parameter integer RXSLIDE_AUTO_WAIT = 7;
+ parameter RXSLIDE_MODE = "OFF";
+ parameter [0:0] RXSYNC_MULTILANE = 1'b0;
+ parameter [0:0] RXSYNC_OVRD = 1'b0;
+ parameter [0:0] RXSYNC_SKIP_DA = 1'b0;
+ parameter [15:0] RX_BIAS_CFG = 16'b0000111100110011;
+ parameter [5:0] RX_BUFFER_CFG = 6'b000000;
+ parameter integer RX_CLK25_DIV = 7;
+ parameter [0:0] RX_CLKMUX_EN = 1'b1;
+ parameter [1:0] RX_CM_SEL = 2'b11;
+ parameter [3:0] RX_CM_TRIM = 4'b0100;
+ parameter integer RX_DATA_WIDTH = 20;
+ parameter [5:0] RX_DDI_SEL = 6'b000000;
+ parameter [13:0] RX_DEBUG_CFG = 14'b00000000000000;
+ parameter RX_DEFER_RESET_BUF_EN = "TRUE";
+ parameter RX_DISPERR_SEQ_MATCH = "TRUE";
+ parameter [12:0] RX_OS_CFG = 13'b0001111110000;
+ parameter integer RX_SIG_VALID_DLY = 10;
+ parameter RX_XCLK_SEL = "RXREC";
+ parameter integer SAS_MAX_COM = 64;
+ parameter integer SAS_MIN_COM = 36;
+ parameter [3:0] SATA_BURST_SEQ_LEN = 4'b1111;
+ parameter [2:0] SATA_BURST_VAL = 3'b100;
+ parameter [2:0] SATA_EIDLE_VAL = 3'b100;
+ parameter integer SATA_MAX_BURST = 8;
+ parameter integer SATA_MAX_INIT = 21;
+ parameter integer SATA_MAX_WAKE = 7;
+ parameter integer SATA_MIN_BURST = 4;
+ parameter integer SATA_MIN_INIT = 12;
+ parameter integer SATA_MIN_WAKE = 4;
+ parameter SATA_PLL_CFG = "VCO_3000MHZ";
+ parameter SHOW_REALIGN_COMMA = "TRUE";
+ parameter SIM_RECEIVER_DETECT_PASS = "TRUE";
+ parameter SIM_RESET_SPEEDUP = "TRUE";
+ parameter SIM_TX_EIDLE_DRIVE_LEVEL = "X";
+ parameter SIM_VERSION = "1.0";
+ parameter [14:0] TERM_RCAL_CFG = 15'b100001000010000;
+ parameter [2:0] TERM_RCAL_OVRD = 3'b000;
+ parameter [7:0] TRANS_TIME_RATE = 8'h0E;
+ parameter [31:0] TST_RSV = 32'h00000000;
+ parameter TXBUF_EN = "TRUE";
+ parameter TXBUF_RESET_ON_RATE_CHANGE = "FALSE";
+ parameter [15:0] TXDLY_CFG = 16'h0010;
+ parameter [8:0] TXDLY_LCFG = 9'h020;
+ parameter [15:0] TXDLY_TAP_CFG = 16'h0000;
+ parameter TXGEARBOX_EN = "FALSE";
+ parameter [0:0] TXOOB_CFG = 1'b0;
+ parameter integer TXOUT_DIV = 2;
+ parameter [4:0] TXPCSRESET_TIME = 5'b00001;
+ parameter [23:0] TXPHDLY_CFG = 24'h084000;
+ parameter [15:0] TXPH_CFG = 16'h0400;
+ parameter [4:0] TXPH_MONITOR_SEL = 5'b00000;
+ parameter [1:0] TXPI_CFG0 = 2'b00;
+ parameter [1:0] TXPI_CFG1 = 2'b00;
+ parameter [1:0] TXPI_CFG2 = 2'b00;
+ parameter [0:0] TXPI_CFG3 = 1'b0;
+ parameter [0:0] TXPI_CFG4 = 1'b0;
+ parameter [2:0] TXPI_CFG5 = 3'b000;
+ parameter [0:0] TXPI_GREY_SEL = 1'b0;
+ parameter [0:0] TXPI_INVSTROBE_SEL = 1'b0;
+ parameter TXPI_PPMCLK_SEL = "TXUSRCLK2";
+ parameter [7:0] TXPI_PPM_CFG = 8'b00000000;
+ parameter [2:0] TXPI_SYNFREQ_PPM = 3'b000;
+ parameter [4:0] TXPMARESET_TIME = 5'b00001;
+ parameter [0:0] TXSYNC_MULTILANE = 1'b0;
+ parameter [0:0] TXSYNC_OVRD = 1'b0;
+ parameter [0:0] TXSYNC_SKIP_DA = 1'b0;
+ parameter integer TX_CLK25_DIV = 7;
+ parameter [0:0] TX_CLKMUX_EN = 1'b1;
+ parameter integer TX_DATA_WIDTH = 20;
+ parameter [5:0] TX_DEEMPH0 = 6'b000000;
+ parameter [5:0] TX_DEEMPH1 = 6'b000000;
+ parameter TX_DRIVE_MODE = "DIRECT";
+ parameter [2:0] TX_EIDLE_ASSERT_DELAY = 3'b110;
+ parameter [2:0] TX_EIDLE_DEASSERT_DELAY = 3'b100;
+ parameter TX_LOOPBACK_DRIVE_HIZ = "FALSE";
+ parameter [0:0] TX_MAINCURSOR_SEL = 1'b0;
+ parameter [6:0] TX_MARGIN_FULL_0 = 7'b1001110;
+ parameter [6:0] TX_MARGIN_FULL_1 = 7'b1001001;
+ parameter [6:0] TX_MARGIN_FULL_2 = 7'b1000101;
+ parameter [6:0] TX_MARGIN_FULL_3 = 7'b1000010;
+ parameter [6:0] TX_MARGIN_FULL_4 = 7'b1000000;
+ parameter [6:0] TX_MARGIN_LOW_0 = 7'b1000110;
+ parameter [6:0] TX_MARGIN_LOW_1 = 7'b1000100;
+ parameter [6:0] TX_MARGIN_LOW_2 = 7'b1000010;
+ parameter [6:0] TX_MARGIN_LOW_3 = 7'b1000000;
+ parameter [6:0] TX_MARGIN_LOW_4 = 7'b1000000;
+ parameter [0:0] TX_PREDRIVER_MODE = 1'b0;
+ parameter [13:0] TX_RXDETECT_CFG = 14'h1832;
+ parameter [2:0] TX_RXDETECT_REF = 3'b100;
+ parameter TX_XCLK_SEL = "TXUSR";
+ parameter [0:0] UCODEER_CLR = 1'b0;
+ parameter [0:0] USE_PCS_CLK_PHASE_SEL = 1'b0;
+ output DRPRDY;
+ output EYESCANDATAERROR;
+ output GTPTXN;
+ output GTPTXP;
+ output PHYSTATUS;
+ output PMARSVDOUT0;
+ output PMARSVDOUT1;
+ output RXBYTEISALIGNED;
+ output RXBYTEREALIGN;
+ output RXCDRLOCK;
+ output RXCHANBONDSEQ;
+ output RXCHANISALIGNED;
+ output RXCHANREALIGN;
+ output RXCOMINITDET;
+ output RXCOMMADET;
+ output RXCOMSASDET;
+ output RXCOMWAKEDET;
+ output RXDLYSRESETDONE;
+ output RXELECIDLE;
+ output RXHEADERVALID;
+ output RXOSINTDONE;
+ output RXOSINTSTARTED;
+ output RXOSINTSTROBEDONE;
+ output RXOSINTSTROBESTARTED;
+ output RXOUTCLK;
+ output RXOUTCLKFABRIC;
+ output RXOUTCLKPCS;
+ output RXPHALIGNDONE;
+ output RXPMARESETDONE;
+ output RXPRBSERR;
+ output RXRATEDONE;
+ output RXRESETDONE;
+ output RXSYNCDONE;
+ output RXSYNCOUT;
+ output RXVALID;
+ output TXCOMFINISH;
+ output TXDLYSRESETDONE;
+ output TXGEARBOXREADY;
+ output TXOUTCLK;
+ output TXOUTCLKFABRIC;
+ output TXOUTCLKPCS;
+ output TXPHALIGNDONE;
+ output TXPHINITDONE;
+ output TXPMARESETDONE;
+ output TXRATEDONE;
+ output TXRESETDONE;
+ output TXSYNCDONE;
+ output TXSYNCOUT;
+ output [14:0] DMONITOROUT;
+ output [15:0] DRPDO;
+ output [15:0] PCSRSVDOUT;
+ output [1:0] RXCLKCORCNT;
+ output [1:0] RXDATAVALID;
+ output [1:0] RXSTARTOFSEQ;
+ output [1:0] TXBUFSTATUS;
+ output [2:0] RXBUFSTATUS;
+ output [2:0] RXHEADER;
+ output [2:0] RXSTATUS;
+ output [31:0] RXDATA;
+ output [3:0] RXCHARISCOMMA;
+ output [3:0] RXCHARISK;
+ output [3:0] RXCHBONDO;
+ output [3:0] RXDISPERR;
+ output [3:0] RXNOTINTABLE;
+ output [4:0] RXPHMONITOR;
+ output [4:0] RXPHSLIPMONITOR;
+ input CFGRESET;
+ input CLKRSVD0;
+ input CLKRSVD1;
+ input DMONFIFORESET;
+ input DMONITORCLK;
+ input DRPCLK;
+ input DRPEN;
+ input DRPWE;
+ input EYESCANMODE;
+ input EYESCANRESET;
+ input EYESCANTRIGGER;
+ input GTPRXN;
+ input GTPRXP;
+ input GTRESETSEL;
+ input GTRXRESET;
+ input GTTXRESET;
+ input PLL0CLK;
+ input PLL0REFCLK;
+ input PLL1CLK;
+ input PLL1REFCLK;
+ input PMARSVDIN0;
+ input PMARSVDIN1;
+ input PMARSVDIN2;
+ input PMARSVDIN3;
+ input PMARSVDIN4;
+ input RESETOVRD;
+ input RX8B10BEN;
+ input RXBUFRESET;
+ input RXCDRFREQRESET;
+ input RXCDRHOLD;
+ input RXCDROVRDEN;
+ input RXCDRRESET;
+ input RXCDRRESETRSV;
+ input RXCHBONDEN;
+ input RXCHBONDMASTER;
+ input RXCHBONDSLAVE;
+ input RXCOMMADETEN;
+ input RXDDIEN;
+ input RXDFEXYDEN;
+ input RXDLYBYPASS;
+ input RXDLYEN;
+ input RXDLYOVRDEN;
+ input RXDLYSRESET;
+ input RXGEARBOXSLIP;
+ input RXLPMHFHOLD;
+ input RXLPMHFOVRDEN;
+ input RXLPMLFHOLD;
+ input RXLPMLFOVRDEN;
+ input RXLPMOSINTNTRLEN;
+ input RXLPMRESET;
+ input RXMCOMMAALIGNEN;
+ input RXOOBRESET;
+ input RXOSCALRESET;
+ input RXOSHOLD;
+ input RXOSINTEN;
+ input RXOSINTHOLD;
+ input RXOSINTNTRLEN;
+ input RXOSINTOVRDEN;
+ input RXOSINTPD;
+ input RXOSINTSTROBE;
+ input RXOSINTTESTOVRDEN;
+ input RXOSOVRDEN;
+ input RXPCOMMAALIGNEN;
+ input RXPCSRESET;
+ input RXPHALIGN;
+ input RXPHALIGNEN;
+ input RXPHDLYPD;
+ input RXPHDLYRESET;
+ input RXPHOVRDEN;
+ input RXPMARESET;
+ input RXPOLARITY;
+ input RXPRBSCNTRESET;
+ input RXRATEMODE;
+ input RXSLIDE;
+ input RXSYNCALLIN;
+ input RXSYNCIN;
+ input RXSYNCMODE;
+ input RXUSERRDY;
+ input RXUSRCLK2;
+ input RXUSRCLK;
+ input SETERRSTATUS;
+ input SIGVALIDCLK;
+ input TX8B10BEN;
+ input TXCOMINIT;
+ input TXCOMSAS;
+ input TXCOMWAKE;
+ input TXDEEMPH;
+ input TXDETECTRX;
+ input TXDIFFPD;
+ input TXDLYBYPASS;
+ input TXDLYEN;
+ input TXDLYHOLD;
+ input TXDLYOVRDEN;
+ input TXDLYSRESET;
+ input TXDLYUPDOWN;
+ input TXELECIDLE;
+ input TXINHIBIT;
+ input TXPCSRESET;
+ input TXPDELECIDLEMODE;
+ input TXPHALIGN;
+ input TXPHALIGNEN;
+ input TXPHDLYPD;
+ input TXPHDLYRESET;
+ input TXPHDLYTSTCLK;
+ input TXPHINIT;
+ input TXPHOVRDEN;
+ input TXPIPPMEN;
+ input TXPIPPMOVRDEN;
+ input TXPIPPMPD;
+ input TXPIPPMSEL;
+ input TXPISOPD;
+ input TXPMARESET;
+ input TXPOLARITY;
+ input TXPOSTCURSORINV;
+ input TXPRBSFORCEERR;
+ input TXPRECURSORINV;
+ input TXRATEMODE;
+ input TXSTARTSEQ;
+ input TXSWING;
+ input TXSYNCALLIN;
+ input TXSYNCIN;
+ input TXSYNCMODE;
+ input TXUSERRDY;
+ input TXUSRCLK2;
+ input TXUSRCLK;
+ input [13:0] RXADAPTSELTEST;
+ input [15:0] DRPDI;
+ input [15:0] GTRSVD;
+ input [15:0] PCSRSVDIN;
+ input [19:0] TSTIN;
+ input [1:0] RXELECIDLEMODE;
+ input [1:0] RXPD;
+ input [1:0] RXSYSCLKSEL;
+ input [1:0] TXPD;
+ input [1:0] TXSYSCLKSEL;
+ input [2:0] LOOPBACK;
+ input [2:0] RXCHBONDLEVEL;
+ input [2:0] RXOUTCLKSEL;
+ input [2:0] RXPRBSSEL;
+ input [2:0] RXRATE;
+ input [2:0] TXBUFDIFFCTRL;
+ input [2:0] TXHEADER;
+ input [2:0] TXMARGIN;
+ input [2:0] TXOUTCLKSEL;
+ input [2:0] TXPRBSSEL;
+ input [2:0] TXRATE;
+ input [31:0] TXDATA;
+ input [3:0] RXCHBONDI;
+ input [3:0] RXOSINTCFG;
+ input [3:0] RXOSINTID0;
+ input [3:0] TX8B10BBYPASS;
+ input [3:0] TXCHARDISPMODE;
+ input [3:0] TXCHARDISPVAL;
+ input [3:0] TXCHARISK;
+ input [3:0] TXDIFFCTRL;
+ input [4:0] TXPIPPMSTEPSIZE;
+ input [4:0] TXPOSTCURSOR;
+ input [4:0] TXPRECURSOR;
+ input [6:0] TXMAINCURSOR;
+ input [6:0] TXSEQUENCE;
+ input [8:0] DRPADDR;
+endmodule
+
+module GTPE2_COMMON (...);
+ parameter [63:0] BIAS_CFG = 64'h0000000000000000;
+ parameter [31:0] COMMON_CFG = 32'h00000000;
+ parameter [0:0] IS_DRPCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_GTGREFCLK0_INVERTED = 1'b0;
+ parameter [0:0] IS_GTGREFCLK1_INVERTED = 1'b0;
+ parameter [0:0] IS_PLL0LOCKDETCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_PLL1LOCKDETCLK_INVERTED = 1'b0;
+ parameter [26:0] PLL0_CFG = 27'h01F03DC;
+ parameter [0:0] PLL0_DMON_CFG = 1'b0;
+ parameter integer PLL0_FBDIV = 4;
+ parameter integer PLL0_FBDIV_45 = 5;
+ parameter [23:0] PLL0_INIT_CFG = 24'h00001E;
+ parameter [8:0] PLL0_LOCK_CFG = 9'h1E8;
+ parameter integer PLL0_REFCLK_DIV = 1;
+ parameter [26:0] PLL1_CFG = 27'h01F03DC;
+ parameter [0:0] PLL1_DMON_CFG = 1'b0;
+ parameter integer PLL1_FBDIV = 4;
+ parameter integer PLL1_FBDIV_45 = 5;
+ parameter [23:0] PLL1_INIT_CFG = 24'h00001E;
+ parameter [8:0] PLL1_LOCK_CFG = 9'h1E8;
+ parameter integer PLL1_REFCLK_DIV = 1;
+ parameter [7:0] PLL_CLKOUT_CFG = 8'b00000000;
+ parameter [15:0] RSVD_ATTR0 = 16'h0000;
+ parameter [15:0] RSVD_ATTR1 = 16'h0000;
+ parameter [2:0] SIM_PLL0REFCLK_SEL = 3'b001;
+ parameter [2:0] SIM_PLL1REFCLK_SEL = 3'b001;
+ parameter SIM_RESET_SPEEDUP = "TRUE";
+ parameter SIM_VERSION = "1.0";
+ output DRPRDY;
+ output PLL0FBCLKLOST;
+ output PLL0LOCK;
+ output PLL0OUTCLK;
+ output PLL0OUTREFCLK;
+ output PLL0REFCLKLOST;
+ output PLL1FBCLKLOST;
+ output PLL1LOCK;
+ output PLL1OUTCLK;
+ output PLL1OUTREFCLK;
+ output PLL1REFCLKLOST;
+ output REFCLKOUTMONITOR0;
+ output REFCLKOUTMONITOR1;
+ output [15:0] DRPDO;
+ output [15:0] PMARSVDOUT;
+ output [7:0] DMONITOROUT;
+ input BGBYPASSB;
+ input BGMONITORENB;
+ input BGPDB;
+ input BGRCALOVRDENB;
+ input DRPCLK;
+ input DRPEN;
+ input DRPWE;
+ input GTEASTREFCLK0;
+ input GTEASTREFCLK1;
+ input GTGREFCLK0;
+ input GTGREFCLK1;
+ input GTREFCLK0;
+ input GTREFCLK1;
+ input GTWESTREFCLK0;
+ input GTWESTREFCLK1;
+ input PLL0LOCKDETCLK;
+ input PLL0LOCKEN;
+ input PLL0PD;
+ input PLL0RESET;
+ input PLL1LOCKDETCLK;
+ input PLL1LOCKEN;
+ input PLL1PD;
+ input PLL1RESET;
+ input RCALENB;
+ input [15:0] DRPDI;
+ input [15:0] PLLRSVD1;
+ input [2:0] PLL0REFCLKSEL;
+ input [2:0] PLL1REFCLKSEL;
+ input [4:0] BGRCALOVRD;
+ input [4:0] PLLRSVD2;
+ input [7:0] DRPADDR;
+ input [7:0] PMARSVD;
+endmodule
+
+module GTXE2_CHANNEL (...);
+ parameter ALIGN_COMMA_DOUBLE = "FALSE";
+ parameter [9:0] ALIGN_COMMA_ENABLE = 10'b0001111111;
+ parameter integer ALIGN_COMMA_WORD = 1;
+ parameter ALIGN_MCOMMA_DET = "TRUE";
+ parameter [9:0] ALIGN_MCOMMA_VALUE = 10'b1010000011;
+ parameter ALIGN_PCOMMA_DET = "TRUE";
+ parameter [9:0] ALIGN_PCOMMA_VALUE = 10'b0101111100;
+ parameter CBCC_DATA_SOURCE_SEL = "DECODED";
+ parameter CHAN_BOND_KEEP_ALIGN = "FALSE";
+ parameter integer CHAN_BOND_MAX_SKEW = 7;
+ parameter [9:0] CHAN_BOND_SEQ_1_1 = 10'b0101111100;
+ parameter [9:0] CHAN_BOND_SEQ_1_2 = 10'b0000000000;
+ parameter [9:0] CHAN_BOND_SEQ_1_3 = 10'b0000000000;
+ parameter [9:0] CHAN_BOND_SEQ_1_4 = 10'b0000000000;
+ parameter [3:0] CHAN_BOND_SEQ_1_ENABLE = 4'b1111;
+ parameter [9:0] CHAN_BOND_SEQ_2_1 = 10'b0100000000;
+ parameter [9:0] CHAN_BOND_SEQ_2_2 = 10'b0100000000;
+ parameter [9:0] CHAN_BOND_SEQ_2_3 = 10'b0100000000;
+ parameter [9:0] CHAN_BOND_SEQ_2_4 = 10'b0100000000;
+ parameter [3:0] CHAN_BOND_SEQ_2_ENABLE = 4'b1111;
+ parameter CHAN_BOND_SEQ_2_USE = "FALSE";
+ parameter integer CHAN_BOND_SEQ_LEN = 1;
+ parameter CLK_CORRECT_USE = "TRUE";
+ parameter CLK_COR_KEEP_IDLE = "FALSE";
+ parameter integer CLK_COR_MAX_LAT = 20;
+ parameter integer CLK_COR_MIN_LAT = 18;
+ parameter CLK_COR_PRECEDENCE = "TRUE";
+ parameter integer CLK_COR_REPEAT_WAIT = 0;
+ parameter [9:0] CLK_COR_SEQ_1_1 = 10'b0100011100;
+ parameter [9:0] CLK_COR_SEQ_1_2 = 10'b0000000000;
+ parameter [9:0] CLK_COR_SEQ_1_3 = 10'b0000000000;
+ parameter [9:0] CLK_COR_SEQ_1_4 = 10'b0000000000;
+ parameter [3:0] CLK_COR_SEQ_1_ENABLE = 4'b1111;
+ parameter [9:0] CLK_COR_SEQ_2_1 = 10'b0100000000;
+ parameter [9:0] CLK_COR_SEQ_2_2 = 10'b0100000000;
+ parameter [9:0] CLK_COR_SEQ_2_3 = 10'b0100000000;
+ parameter [9:0] CLK_COR_SEQ_2_4 = 10'b0100000000;
+ parameter [3:0] CLK_COR_SEQ_2_ENABLE = 4'b1111;
+ parameter CLK_COR_SEQ_2_USE = "FALSE";
+ parameter integer CLK_COR_SEQ_LEN = 1;
+ parameter [23:0] CPLL_CFG = 24'hB007D8;
+ parameter integer CPLL_FBDIV = 4;
+ parameter integer CPLL_FBDIV_45 = 5;
+ parameter [23:0] CPLL_INIT_CFG = 24'h00001E;
+ parameter [15:0] CPLL_LOCK_CFG = 16'h01E8;
+ parameter integer CPLL_REFCLK_DIV = 1;
+ parameter DEC_MCOMMA_DETECT = "TRUE";
+ parameter DEC_PCOMMA_DETECT = "TRUE";
+ parameter DEC_VALID_COMMA_ONLY = "TRUE";
+ parameter [23:0] DMONITOR_CFG = 24'h000A00;
+ parameter [5:0] ES_CONTROL = 6'b000000;
+ parameter ES_ERRDET_EN = "FALSE";
+ parameter ES_EYE_SCAN_EN = "FALSE";
+ parameter [11:0] ES_HORZ_OFFSET = 12'h000;
+ parameter [9:0] ES_PMA_CFG = 10'b0000000000;
+ parameter [4:0] ES_PRESCALE = 5'b00000;
+ parameter [79:0] ES_QUALIFIER = 80'h00000000000000000000;
+ parameter [79:0] ES_QUAL_MASK = 80'h00000000000000000000;
+ parameter [79:0] ES_SDATA_MASK = 80'h00000000000000000000;
+ parameter [8:0] ES_VERT_OFFSET = 9'b000000000;
+ parameter [3:0] FTS_DESKEW_SEQ_ENABLE = 4'b1111;
+ parameter [3:0] FTS_LANE_DESKEW_CFG = 4'b1111;
+ parameter FTS_LANE_DESKEW_EN = "FALSE";
+ parameter [2:0] GEARBOX_MODE = 3'b000;
+ parameter [0:0] IS_CPLLLOCKDETCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_DRPCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_GTGREFCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_RXUSRCLK2_INVERTED = 1'b0;
+ parameter [0:0] IS_RXUSRCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_TXPHDLYTSTCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_TXUSRCLK2_INVERTED = 1'b0;
+ parameter [0:0] IS_TXUSRCLK_INVERTED = 1'b0;
+ parameter [1:0] OUTREFCLK_SEL_INV = 2'b11;
+ parameter PCS_PCIE_EN = "FALSE";
+ parameter [47:0] PCS_RSVD_ATTR = 48'h000000000000;
+ parameter [11:0] PD_TRANS_TIME_FROM_P2 = 12'h03C;
+ parameter [7:0] PD_TRANS_TIME_NONE_P2 = 8'h19;
+ parameter [7:0] PD_TRANS_TIME_TO_P2 = 8'h64;
+ parameter [31:0] PMA_RSV = 32'h00000000;
+ parameter [15:0] PMA_RSV2 = 16'h2050;
+ parameter [1:0] PMA_RSV3 = 2'b00;
+ parameter [31:0] PMA_RSV4 = 32'h00000000;
+ parameter [4:0] RXBUFRESET_TIME = 5'b00001;
+ parameter RXBUF_ADDR_MODE = "FULL";
+ parameter [3:0] RXBUF_EIDLE_HI_CNT = 4'b1000;
+ parameter [3:0] RXBUF_EIDLE_LO_CNT = 4'b0000;
+ parameter RXBUF_EN = "TRUE";
+ parameter RXBUF_RESET_ON_CB_CHANGE = "TRUE";
+ parameter RXBUF_RESET_ON_COMMAALIGN = "FALSE";
+ parameter RXBUF_RESET_ON_EIDLE = "FALSE";
+ parameter RXBUF_RESET_ON_RATE_CHANGE = "TRUE";
+ parameter integer RXBUF_THRESH_OVFLW = 61;
+ parameter RXBUF_THRESH_OVRD = "FALSE";
+ parameter integer RXBUF_THRESH_UNDFLW = 4;
+ parameter [4:0] RXCDRFREQRESET_TIME = 5'b00001;
+ parameter [4:0] RXCDRPHRESET_TIME = 5'b00001;
+ parameter [71:0] RXCDR_CFG = 72'h0B000023FF20400020;
+ parameter [0:0] RXCDR_FR_RESET_ON_EIDLE = 1'b0;
+ parameter [0:0] RXCDR_HOLD_DURING_EIDLE = 1'b0;
+ parameter [5:0] RXCDR_LOCK_CFG = 6'b010101;
+ parameter [0:0] RXCDR_PH_RESET_ON_EIDLE = 1'b0;
+ parameter [6:0] RXDFELPMRESET_TIME = 7'b0001111;
+ parameter [15:0] RXDLY_CFG = 16'h001F;
+ parameter [8:0] RXDLY_LCFG = 9'h030;
+ parameter [15:0] RXDLY_TAP_CFG = 16'h0000;
+ parameter RXGEARBOX_EN = "FALSE";
+ parameter [4:0] RXISCANRESET_TIME = 5'b00001;
+ parameter [13:0] RXLPM_HF_CFG = 14'b00000011110000;
+ parameter [13:0] RXLPM_LF_CFG = 14'b00000011110000;
+ parameter [6:0] RXOOB_CFG = 7'b0000110;
+ parameter integer RXOUT_DIV = 2;
+ parameter [4:0] RXPCSRESET_TIME = 5'b00001;
+ parameter [23:0] RXPHDLY_CFG = 24'h084020;
+ parameter [23:0] RXPH_CFG = 24'h000000;
+ parameter [4:0] RXPH_MONITOR_SEL = 5'b00000;
+ parameter [4:0] RXPMARESET_TIME = 5'b00011;
+ parameter [0:0] RXPRBS_ERR_LOOPBACK = 1'b0;
+ parameter integer RXSLIDE_AUTO_WAIT = 7;
+ parameter RXSLIDE_MODE = "OFF";
+ parameter [11:0] RX_BIAS_CFG = 12'b000000000000;
+ parameter [5:0] RX_BUFFER_CFG = 6'b000000;
+ parameter integer RX_CLK25_DIV = 7;
+ parameter [0:0] RX_CLKMUX_PD = 1'b1;
+ parameter [1:0] RX_CM_SEL = 2'b11;
+ parameter [2:0] RX_CM_TRIM = 3'b100;
+ parameter integer RX_DATA_WIDTH = 20;
+ parameter [5:0] RX_DDI_SEL = 6'b000000;
+ parameter [11:0] RX_DEBUG_CFG = 12'b000000000000;
+ parameter RX_DEFER_RESET_BUF_EN = "TRUE";
+ parameter [22:0] RX_DFE_GAIN_CFG = 23'h180E0F;
+ parameter [11:0] RX_DFE_H2_CFG = 12'b000111100000;
+ parameter [11:0] RX_DFE_H3_CFG = 12'b000111100000;
+ parameter [10:0] RX_DFE_H4_CFG = 11'b00011110000;
+ parameter [10:0] RX_DFE_H5_CFG = 11'b00011110000;
+ parameter [12:0] RX_DFE_KL_CFG = 13'b0001111110000;
+ parameter [31:0] RX_DFE_KL_CFG2 = 32'h3008E56A;
+ parameter [15:0] RX_DFE_LPM_CFG = 16'h0904;
+ parameter [0:0] RX_DFE_LPM_HOLD_DURING_EIDLE = 1'b0;
+ parameter [16:0] RX_DFE_UT_CFG = 17'b00111111000000000;
+ parameter [16:0] RX_DFE_VP_CFG = 17'b00011111100000000;
+ parameter [12:0] RX_DFE_XYD_CFG = 13'b0000000010000;
+ parameter RX_DISPERR_SEQ_MATCH = "TRUE";
+ parameter integer RX_INT_DATAWIDTH = 0;
+ parameter [12:0] RX_OS_CFG = 13'b0001111110000;
+ parameter integer RX_SIG_VALID_DLY = 10;
+ parameter RX_XCLK_SEL = "RXREC";
+ parameter integer SAS_MAX_COM = 64;
+ parameter integer SAS_MIN_COM = 36;
+ parameter [3:0] SATA_BURST_SEQ_LEN = 4'b1111;
+ parameter [2:0] SATA_BURST_VAL = 3'b100;
+ parameter SATA_CPLL_CFG = "VCO_3000MHZ";
+ parameter [2:0] SATA_EIDLE_VAL = 3'b100;
+ parameter integer SATA_MAX_BURST = 8;
+ parameter integer SATA_MAX_INIT = 21;
+ parameter integer SATA_MAX_WAKE = 7;
+ parameter integer SATA_MIN_BURST = 4;
+ parameter integer SATA_MIN_INIT = 12;
+ parameter integer SATA_MIN_WAKE = 4;
+ parameter SHOW_REALIGN_COMMA = "TRUE";
+ parameter [2:0] SIM_CPLLREFCLK_SEL = 3'b001;
+ parameter SIM_RECEIVER_DETECT_PASS = "TRUE";
+ parameter SIM_RESET_SPEEDUP = "TRUE";
+ parameter SIM_TX_EIDLE_DRIVE_LEVEL = "X";
+ parameter SIM_VERSION = "4.0";
+ parameter [4:0] TERM_RCAL_CFG = 5'b10000;
+ parameter [0:0] TERM_RCAL_OVRD = 1'b0;
+ parameter [7:0] TRANS_TIME_RATE = 8'h0E;
+ parameter [31:0] TST_RSV = 32'h00000000;
+ parameter TXBUF_EN = "TRUE";
+ parameter TXBUF_RESET_ON_RATE_CHANGE = "FALSE";
+ parameter [15:0] TXDLY_CFG = 16'h001F;
+ parameter [8:0] TXDLY_LCFG = 9'h030;
+ parameter [15:0] TXDLY_TAP_CFG = 16'h0000;
+ parameter TXGEARBOX_EN = "FALSE";
+ parameter integer TXOUT_DIV = 2;
+ parameter [4:0] TXPCSRESET_TIME = 5'b00001;
+ parameter [23:0] TXPHDLY_CFG = 24'h084020;
+ parameter [15:0] TXPH_CFG = 16'h0780;
+ parameter [4:0] TXPH_MONITOR_SEL = 5'b00000;
+ parameter [4:0] TXPMARESET_TIME = 5'b00001;
+ parameter integer TX_CLK25_DIV = 7;
+ parameter [0:0] TX_CLKMUX_PD = 1'b1;
+ parameter integer TX_DATA_WIDTH = 20;
+ parameter [4:0] TX_DEEMPH0 = 5'b00000;
+ parameter [4:0] TX_DEEMPH1 = 5'b00000;
+ parameter TX_DRIVE_MODE = "DIRECT";
+ parameter [2:0] TX_EIDLE_ASSERT_DELAY = 3'b110;
+ parameter [2:0] TX_EIDLE_DEASSERT_DELAY = 3'b100;
+ parameter integer TX_INT_DATAWIDTH = 0;
+ parameter TX_LOOPBACK_DRIVE_HIZ = "FALSE";
+ parameter [0:0] TX_MAINCURSOR_SEL = 1'b0;
+ parameter [6:0] TX_MARGIN_FULL_0 = 7'b1001110;
+ parameter [6:0] TX_MARGIN_FULL_1 = 7'b1001001;
+ parameter [6:0] TX_MARGIN_FULL_2 = 7'b1000101;
+ parameter [6:0] TX_MARGIN_FULL_3 = 7'b1000010;
+ parameter [6:0] TX_MARGIN_FULL_4 = 7'b1000000;
+ parameter [6:0] TX_MARGIN_LOW_0 = 7'b1000110;
+ parameter [6:0] TX_MARGIN_LOW_1 = 7'b1000100;
+ parameter [6:0] TX_MARGIN_LOW_2 = 7'b1000010;
+ parameter [6:0] TX_MARGIN_LOW_3 = 7'b1000000;
+ parameter [6:0] TX_MARGIN_LOW_4 = 7'b1000000;
+ parameter [0:0] TX_PREDRIVER_MODE = 1'b0;
+ parameter [0:0] TX_QPI_STATUS_EN = 1'b0;
+ parameter [13:0] TX_RXDETECT_CFG = 14'h1832;
+ parameter [2:0] TX_RXDETECT_REF = 3'b100;
+ parameter TX_XCLK_SEL = "TXUSR";
+ parameter [0:0] UCODEER_CLR = 1'b0;
+ output CPLLFBCLKLOST;
+ output CPLLLOCK;
+ output CPLLREFCLKLOST;
+ output DRPRDY;
+ output EYESCANDATAERROR;
+ output GTREFCLKMONITOR;
+ output GTXTXN;
+ output GTXTXP;
+ output PHYSTATUS;
+ output RXBYTEISALIGNED;
+ output RXBYTEREALIGN;
+ output RXCDRLOCK;
+ output RXCHANBONDSEQ;
+ output RXCHANISALIGNED;
+ output RXCHANREALIGN;
+ output RXCOMINITDET;
+ output RXCOMMADET;
+ output RXCOMSASDET;
+ output RXCOMWAKEDET;
+ output RXDATAVALID;
+ output RXDLYSRESETDONE;
+ output RXELECIDLE;
+ output RXHEADERVALID;
+ output RXOUTCLK;
+ output RXOUTCLKFABRIC;
+ output RXOUTCLKPCS;
+ output RXPHALIGNDONE;
+ output RXPRBSERR;
+ output RXQPISENN;
+ output RXQPISENP;
+ output RXRATEDONE;
+ output RXRESETDONE;
+ output RXSTARTOFSEQ;
+ output RXVALID;
+ output TXCOMFINISH;
+ output TXDLYSRESETDONE;
+ output TXGEARBOXREADY;
+ output TXOUTCLK;
+ output TXOUTCLKFABRIC;
+ output TXOUTCLKPCS;
+ output TXPHALIGNDONE;
+ output TXPHINITDONE;
+ output TXQPISENN;
+ output TXQPISENP;
+ output TXRATEDONE;
+ output TXRESETDONE;
+ output [15:0] DRPDO;
+ output [15:0] PCSRSVDOUT;
+ output [1:0] RXCLKCORCNT;
+ output [1:0] TXBUFSTATUS;
+ output [2:0] RXBUFSTATUS;
+ output [2:0] RXHEADER;
+ output [2:0] RXSTATUS;
+ output [4:0] RXCHBONDO;
+ output [4:0] RXPHMONITOR;
+ output [4:0] RXPHSLIPMONITOR;
+ output [63:0] RXDATA;
+ output [6:0] RXMONITOROUT;
+ output [7:0] DMONITOROUT;
+ output [7:0] RXCHARISCOMMA;
+ output [7:0] RXCHARISK;
+ output [7:0] RXDISPERR;
+ output [7:0] RXNOTINTABLE;
+ output [9:0] TSTOUT;
+ input CFGRESET;
+ input CPLLLOCKDETCLK;
+ input CPLLLOCKEN;
+ input CPLLPD;
+ input CPLLRESET;
+ input DRPCLK;
+ input DRPEN;
+ input DRPWE;
+ input EYESCANMODE;
+ input EYESCANRESET;
+ input EYESCANTRIGGER;
+ input GTGREFCLK;
+ input GTNORTHREFCLK0;
+ input GTNORTHREFCLK1;
+ input GTREFCLK0;
+ input GTREFCLK1;
+ input GTRESETSEL;
+ input GTRXRESET;
+ input GTSOUTHREFCLK0;
+ input GTSOUTHREFCLK1;
+ input GTTXRESET;
+ input GTXRXN;
+ input GTXRXP;
+ input QPLLCLK;
+ input QPLLREFCLK;
+ input RESETOVRD;
+ input RX8B10BEN;
+ input RXBUFRESET;
+ input RXCDRFREQRESET;
+ input RXCDRHOLD;
+ input RXCDROVRDEN;
+ input RXCDRRESET;
+ input RXCDRRESETRSV;
+ input RXCHBONDEN;
+ input RXCHBONDMASTER;
+ input RXCHBONDSLAVE;
+ input RXCOMMADETEN;
+ input RXDDIEN;
+ input RXDFEAGCHOLD;
+ input RXDFEAGCOVRDEN;
+ input RXDFECM1EN;
+ input RXDFELFHOLD;
+ input RXDFELFOVRDEN;
+ input RXDFELPMRESET;
+ input RXDFETAP2HOLD;
+ input RXDFETAP2OVRDEN;
+ input RXDFETAP3HOLD;
+ input RXDFETAP3OVRDEN;
+ input RXDFETAP4HOLD;
+ input RXDFETAP4OVRDEN;
+ input RXDFETAP5HOLD;
+ input RXDFETAP5OVRDEN;
+ input RXDFEUTHOLD;
+ input RXDFEUTOVRDEN;
+ input RXDFEVPHOLD;
+ input RXDFEVPOVRDEN;
+ input RXDFEVSEN;
+ input RXDFEXYDEN;
+ input RXDFEXYDHOLD;
+ input RXDFEXYDOVRDEN;
+ input RXDLYBYPASS;
+ input RXDLYEN;
+ input RXDLYOVRDEN;
+ input RXDLYSRESET;
+ input RXGEARBOXSLIP;
+ input RXLPMEN;
+ input RXLPMHFHOLD;
+ input RXLPMHFOVRDEN;
+ input RXLPMLFHOLD;
+ input RXLPMLFKLOVRDEN;
+ input RXMCOMMAALIGNEN;
+ input RXOOBRESET;
+ input RXOSHOLD;
+ input RXOSOVRDEN;
+ input RXPCOMMAALIGNEN;
+ input RXPCSRESET;
+ input RXPHALIGN;
+ input RXPHALIGNEN;
+ input RXPHDLYPD;
+ input RXPHDLYRESET;
+ input RXPHOVRDEN;
+ input RXPMARESET;
+ input RXPOLARITY;
+ input RXPRBSCNTRESET;
+ input RXQPIEN;
+ input RXSLIDE;
+ input RXUSERRDY;
+ input RXUSRCLK2;
+ input RXUSRCLK;
+ input SETERRSTATUS;
+ input TX8B10BEN;
+ input TXCOMINIT;
+ input TXCOMSAS;
+ input TXCOMWAKE;
+ input TXDEEMPH;
+ input TXDETECTRX;
+ input TXDIFFPD;
+ input TXDLYBYPASS;
+ input TXDLYEN;
+ input TXDLYHOLD;
+ input TXDLYOVRDEN;
+ input TXDLYSRESET;
+ input TXDLYUPDOWN;
+ input TXELECIDLE;
+ input TXINHIBIT;
+ input TXPCSRESET;
+ input TXPDELECIDLEMODE;
+ input TXPHALIGN;
+ input TXPHALIGNEN;
+ input TXPHDLYPD;
+ input TXPHDLYRESET;
+ input TXPHDLYTSTCLK;
+ input TXPHINIT;
+ input TXPHOVRDEN;
+ input TXPISOPD;
+ input TXPMARESET;
+ input TXPOLARITY;
+ input TXPOSTCURSORINV;
+ input TXPRBSFORCEERR;
+ input TXPRECURSORINV;
+ input TXQPIBIASEN;
+ input TXQPISTRONGPDOWN;
+ input TXQPIWEAKPUP;
+ input TXSTARTSEQ;
+ input TXSWING;
+ input TXUSERRDY;
+ input TXUSRCLK2;
+ input TXUSRCLK;
+ input [15:0] DRPDI;
+ input [15:0] GTRSVD;
+ input [15:0] PCSRSVDIN;
+ input [19:0] TSTIN;
+ input [1:0] RXELECIDLEMODE;
+ input [1:0] RXMONITORSEL;
+ input [1:0] RXPD;
+ input [1:0] RXSYSCLKSEL;
+ input [1:0] TXPD;
+ input [1:0] TXSYSCLKSEL;
+ input [2:0] CPLLREFCLKSEL;
+ input [2:0] LOOPBACK;
+ input [2:0] RXCHBONDLEVEL;
+ input [2:0] RXOUTCLKSEL;
+ input [2:0] RXPRBSSEL;
+ input [2:0] RXRATE;
+ input [2:0] TXBUFDIFFCTRL;
+ input [2:0] TXHEADER;
+ input [2:0] TXMARGIN;
+ input [2:0] TXOUTCLKSEL;
+ input [2:0] TXPRBSSEL;
+ input [2:0] TXRATE;
+ input [3:0] CLKRSVD;
+ input [3:0] TXDIFFCTRL;
+ input [4:0] PCSRSVDIN2;
+ input [4:0] PMARSVDIN2;
+ input [4:0] PMARSVDIN;
+ input [4:0] RXCHBONDI;
+ input [4:0] TXPOSTCURSOR;
+ input [4:0] TXPRECURSOR;
+ input [63:0] TXDATA;
+ input [6:0] TXMAINCURSOR;
+ input [6:0] TXSEQUENCE;
+ input [7:0] TX8B10BBYPASS;
+ input [7:0] TXCHARDISPMODE;
+ input [7:0] TXCHARDISPVAL;
+ input [7:0] TXCHARISK;
+ input [8:0] DRPADDR;
+endmodule
+
+module GTXE2_COMMON (...);
+ parameter [63:0] BIAS_CFG = 64'h0000040000001000;
+ parameter [31:0] COMMON_CFG = 32'h00000000;
+ parameter [0:0] IS_DRPCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_GTGREFCLK_INVERTED = 1'b0;
+ parameter [0:0] IS_QPLLLOCKDETCLK_INVERTED = 1'b0;
+ parameter [26:0] QPLL_CFG = 27'h0680181;
+ parameter [3:0] QPLL_CLKOUT_CFG = 4'b0000;
+ parameter [5:0] QPLL_COARSE_FREQ_OVRD = 6'b010000;
+ parameter [0:0] QPLL_COARSE_FREQ_OVRD_EN = 1'b0;
+ parameter [9:0] QPLL_CP = 10'b0000011111;
+ parameter [0:0] QPLL_CP_MONITOR_EN = 1'b0;
+ parameter [0:0] QPLL_DMONITOR_SEL = 1'b0;
+ parameter [9:0] QPLL_FBDIV = 10'b0000000000;
+ parameter [0:0] QPLL_FBDIV_MONITOR_EN = 1'b0;
+ parameter [0:0] QPLL_FBDIV_RATIO = 1'b0;
+ parameter [23:0] QPLL_INIT_CFG = 24'h000006;
+ parameter [15:0] QPLL_LOCK_CFG = 16'h21E8;
+ parameter [3:0] QPLL_LPF = 4'b1111;
+ parameter integer QPLL_REFCLK_DIV = 2;
+ parameter [2:0] SIM_QPLLREFCLK_SEL = 3'b001;
+ parameter SIM_RESET_SPEEDUP = "TRUE";
+ parameter SIM_VERSION = "4.0";
+ output DRPRDY;
+ output QPLLFBCLKLOST;
+ output QPLLLOCK;
+ output QPLLOUTCLK;
+ output QPLLOUTREFCLK;
+ output QPLLREFCLKLOST;
+ output REFCLKOUTMONITOR;
+ output [15:0] DRPDO;
+ output [7:0] QPLLDMONITOR;
+ input BGBYPASSB;
+ input BGMONITORENB;
+ input BGPDB;
+ input DRPCLK;
+ input DRPEN;
+ input DRPWE;
+ input GTGREFCLK;
+ input GTNORTHREFCLK0;
+ input GTNORTHREFCLK1;
+ input GTREFCLK0;
+ input GTREFCLK1;
+ input GTSOUTHREFCLK0;
+ input GTSOUTHREFCLK1;
+ input QPLLLOCKDETCLK;
+ input QPLLLOCKEN;
+ input QPLLOUTRESET;
+ input QPLLPD;
+ input QPLLRESET;
+ input RCALENB;
+ input [15:0] DRPDI;
+ input [15:0] QPLLRSVD1;
+ input [2:0] QPLLREFCLKSEL;
+ input [4:0] BGRCALOVRD;
+ input [4:0] QPLLRSVD2;
+ input [7:0] DRPADDR;
+ input [7:0] PMARSVD;
+endmodule
+
+module IBUF_IBUFDISABLE (...);
+ parameter IBUF_LOW_PWR = "TRUE";
+ parameter IOSTANDARD = "DEFAULT";
+ parameter SIM_DEVICE = "7SERIES";
+ parameter USE_IBUFDISABLE = "TRUE";
+ output O;
+ input I;
+ input IBUFDISABLE;
+endmodule
+
+module IBUF_INTERMDISABLE (...);
+ parameter IBUF_LOW_PWR = "TRUE";
+ parameter IOSTANDARD = "DEFAULT";
+ parameter SIM_DEVICE = "7SERIES";
+ parameter USE_IBUFDISABLE = "TRUE";
+ output O;
+ input I;
+ input IBUFDISABLE;
+ input INTERMDISABLE;
+endmodule
+
+module IBUFDS (...);
+ parameter CAPACITANCE = "DONT_CARE";
+ parameter DIFF_TERM = "FALSE";
+ parameter DQS_BIAS = "FALSE";
+ parameter IBUF_DELAY_VALUE = "0";
+ parameter IBUF_LOW_PWR = "TRUE";
+ parameter IFD_DELAY_VALUE = "AUTO";
+ parameter IOSTANDARD = "DEFAULT";
+ output O;
+ input I, IB;
+endmodule
+
+module IBUFDS_DIFF_OUT (...);
+ parameter DIFF_TERM = "FALSE";
+ parameter DQS_BIAS = "FALSE";
+ parameter IBUF_LOW_PWR = "TRUE";
+ parameter IOSTANDARD = "DEFAULT";
+ output O, OB;
+ input I, IB;
+endmodule
+
+module IBUFDS_DIFF_OUT_IBUFDISABLE (...);
+ parameter DIFF_TERM = "FALSE";
+ parameter DQS_BIAS = "FALSE";
+ parameter IBUF_LOW_PWR = "TRUE";
+ parameter IOSTANDARD = "DEFAULT";
+ parameter SIM_DEVICE = "7SERIES";
+ parameter USE_IBUFDISABLE = "TRUE";
+ output O;
+ output OB;
+ input I;
+ input IB;
+ input IBUFDISABLE;
+endmodule
+
+module IBUFDS_DIFF_OUT_INTERMDISABLE (...);
+ parameter DIFF_TERM = "FALSE";
+ parameter DQS_BIAS = "FALSE";
+ parameter IBUF_LOW_PWR = "TRUE";
+ parameter IOSTANDARD = "DEFAULT";
+ parameter SIM_DEVICE = "7SERIES";
+ parameter USE_IBUFDISABLE = "TRUE";
+ output O;
+ output OB;
+ input I;
+ input IB;
+ input IBUFDISABLE;
+ input INTERMDISABLE;
+endmodule
+
+module IBUFDS_GTE2 (...);
+ parameter CLKCM_CFG = "TRUE";
+ parameter CLKRCV_TRST = "TRUE";
+ parameter CLKSWING_CFG = "TRUE";
+ output O;
+ output ODIV2;
+ input CEB;
+ input I;
+ input IB;
+endmodule
+
+module IBUFDS_IBUFDISABLE (...);
+ parameter DIFF_TERM = "FALSE";
+ parameter DQS_BIAS = "FALSE";
+ parameter IBUF_LOW_PWR = "TRUE";
+ parameter IOSTANDARD = "DEFAULT";
+ parameter SIM_DEVICE = "7SERIES";
+ parameter USE_IBUFDISABLE = "TRUE";
+ output O;
+ input I;
+ input IB;
+ input IBUFDISABLE;
+endmodule
+
+module IBUFDS_INTERMDISABLE (...);
+ parameter DIFF_TERM = "FALSE";
+ parameter DQS_BIAS = "FALSE";
+ parameter IBUF_LOW_PWR = "TRUE";
+ parameter IOSTANDARD = "DEFAULT";
+ parameter SIM_DEVICE = "7SERIES";
+ parameter USE_IBUFDISABLE = "TRUE";
+ output O;
+ input I;
+ input IB;
+ input IBUFDISABLE;
+ input INTERMDISABLE;
+endmodule
+
+module ICAPE2 (...);
+ parameter [31:0] DEVICE_ID = 32'h04244093;
+ parameter ICAP_WIDTH = "X32";
+ parameter SIM_CFG_FILE_NAME = "NONE";
+ output [31:0] O;
+ input CLK;
+ input CSIB;
+ input RDWRB;
+ input [31:0] I;
+endmodule
+
+module IDDR (...);
+ parameter DDR_CLK_EDGE = "OPPOSITE_EDGE";
+ parameter INIT_Q1 = 1'b0;
+ parameter INIT_Q2 = 1'b0;
+ parameter [0:0] IS_C_INVERTED = 1'b0;
+ parameter [0:0] IS_D_INVERTED = 1'b0;
+ parameter SRTYPE = "SYNC";
+ parameter MSGON = "TRUE";
+ parameter XON = "TRUE";
+ output Q1;
+ output Q2;
+ input C;
+ input CE;
+ input D;
+ input R;
+ input S;
+endmodule
+
+module IDDR_2CLK (...);
+ parameter DDR_CLK_EDGE = "OPPOSITE_EDGE";
+ parameter INIT_Q1 = 1'b0;
+ parameter INIT_Q2 = 1'b0;
+ parameter [0:0] IS_CB_INVERTED = 1'b0;
+ parameter [0:0] IS_C_INVERTED = 1'b0;
+ parameter [0:0] IS_D_INVERTED = 1'b0;
+ parameter SRTYPE = "SYNC";
+ output Q1;
+ output Q2;
+ input C;
+ input CB;
+ input CE;
+ input D;
+ input R;
+ input S;
+endmodule
+
+module IDELAYCTRL (...);
+ parameter SIM_DEVICE = "7SERIES";
+ output RDY;
+ input REFCLK;
+ input RST;
+endmodule
+
+module IDELAYE2 (...);
+ parameter CINVCTRL_SEL = "FALSE";
+ parameter DELAY_SRC = "IDATAIN";
+ parameter HIGH_PERFORMANCE_MODE = "FALSE";
+ parameter IDELAY_TYPE = "FIXED";
+ parameter integer IDELAY_VALUE = 0;
+ parameter [0:0] IS_C_INVERTED = 1'b0;
+ parameter [0:0] IS_DATAIN_INVERTED = 1'b0;
+ parameter [0:0] IS_IDATAIN_INVERTED = 1'b0;
+ parameter PIPE_SEL = "FALSE";
+ parameter real REFCLK_FREQUENCY = 200.0;
+ parameter SIGNAL_PATTERN = "DATA";
+ parameter integer SIM_DELAY_D = 0;
+ output [4:0] CNTVALUEOUT;
+ output DATAOUT;
+ input C;
+ input CE;
+ input CINVCTRL;
+ input [4:0] CNTVALUEIN;
+ input DATAIN;
+ input IDATAIN;
+ input INC;
+ input LD;
+ input LDPIPEEN;
+ input REGRST;
+endmodule
+
+module IN_FIFO (...);
+ parameter integer ALMOST_EMPTY_VALUE = 1;
+ parameter integer ALMOST_FULL_VALUE = 1;
+ parameter ARRAY_MODE = "ARRAY_MODE_4_X_8";
+ parameter SYNCHRONOUS_MODE = "FALSE";
+ output ALMOSTEMPTY;
+ output ALMOSTFULL;
+ output EMPTY;
+ output FULL;
+ output [7:0] Q0;
+ output [7:0] Q1;
+ output [7:0] Q2;
+ output [7:0] Q3;
+ output [7:0] Q4;
+ output [7:0] Q5;
+ output [7:0] Q6;
+ output [7:0] Q7;
+ output [7:0] Q8;
+ output [7:0] Q9;
+ input RDCLK;
+ input RDEN;
+ input RESET;
+ input WRCLK;
+ input WREN;
+ input [3:0] D0;
+ input [3:0] D1;
+ input [3:0] D2;
+ input [3:0] D3;
+ input [3:0] D4;
+ input [3:0] D7;
+ input [3:0] D8;
+ input [3:0] D9;
+ input [7:0] D5;
+ input [7:0] D6;
+endmodule
+
+module IOBUF (...);
+ parameter integer DRIVE = 12;
+ parameter IBUF_LOW_PWR = "TRUE";
+ parameter IOSTANDARD = "DEFAULT";
+ parameter SLEW = "SLOW";
+ output O;
+ input I, T;
+endmodule
+
+module IOBUF_DCIEN (...);
+ parameter integer DRIVE = 12;
+ parameter IBUF_LOW_PWR = "TRUE";
+ parameter IOSTANDARD = "DEFAULT";
+ parameter SIM_DEVICE = "7SERIES";
+ parameter SLEW = "SLOW";
+ parameter USE_IBUFDISABLE = "TRUE";
+ output O;
+ input DCITERMDISABLE;
+ input I;
+ input IBUFDISABLE;
+ input T;
+endmodule
+
+module IOBUF_INTERMDISABLE (...);
+ parameter integer DRIVE = 12;
+ parameter IBUF_LOW_PWR = "TRUE";
+ parameter IOSTANDARD = "DEFAULT";
+ parameter SIM_DEVICE = "7SERIES";
+ parameter SLEW = "SLOW";
+ parameter USE_IBUFDISABLE = "TRUE";
+ output O;
+ input I;
+ input IBUFDISABLE;
+ input INTERMDISABLE;
+ input T;
+endmodule
+
+module IOBUFDS (...);
+ parameter DIFF_TERM = "FALSE";
+ parameter DQS_BIAS = "FALSE";
+ parameter IBUF_LOW_PWR = "TRUE";
+ parameter IOSTANDARD = "DEFAULT";
+ parameter SLEW = "SLOW";
+ output O;
+ input I, T;
+endmodule
+
+module IOBUFDS_DCIEN (...);
+ parameter DIFF_TERM = "FALSE";
+ parameter DQS_BIAS = "FALSE";
+ parameter IBUF_LOW_PWR = "TRUE";
+ parameter IOSTANDARD = "DEFAULT";
+ parameter SIM_DEVICE = "7SERIES";
+ parameter SLEW = "SLOW";
+ parameter USE_IBUFDISABLE = "TRUE";
+ output O;
+ input DCITERMDISABLE;
+ input I;
+ input IBUFDISABLE;
+ input T;
+endmodule
+
+module IOBUFDS_DIFF_OUT (...);
+ parameter DIFF_TERM = "FALSE";
+ parameter DQS_BIAS = "FALSE";
+ parameter IBUF_LOW_PWR = "TRUE";
+ parameter IOSTANDARD = "DEFAULT";
+ output O;
+ output OB;
+ input I;
+ input TM;
+ input TS;
+endmodule
+
+module IOBUFDS_DIFF_OUT_DCIEN (...);
+ parameter DIFF_TERM = "FALSE";
+ parameter DQS_BIAS = "FALSE";
+ parameter IBUF_LOW_PWR = "TRUE";
+ parameter IOSTANDARD = "DEFAULT";
+ parameter SIM_DEVICE = "7SERIES";
+ parameter USE_IBUFDISABLE = "TRUE";
+ output O;
+ output OB;
+ input DCITERMDISABLE;
+ input I;
+ input IBUFDISABLE;
+ input TM;
+ input TS;
+endmodule
+
+module IOBUFDS_DIFF_OUT_INTERMDISABLE (...);
+ parameter DIFF_TERM = "FALSE";
+ parameter DQS_BIAS = "FALSE";
+ parameter IBUF_LOW_PWR = "TRUE";
+ parameter IOSTANDARD = "DEFAULT";
+ parameter SIM_DEVICE = "7SERIES";
+ parameter USE_IBUFDISABLE = "TRUE";
+ output O;
+ output OB;
+ input I;
+ input IBUFDISABLE;
+ input INTERMDISABLE;
+ input TM;
+ input TS;
+endmodule
+
+module ISERDESE2 (...);
+ parameter DATA_RATE = "DDR";
+ parameter integer DATA_WIDTH = 4;
+ parameter DYN_CLKDIV_INV_EN = "FALSE";
+ parameter DYN_CLK_INV_EN = "FALSE";
+ parameter [0:0] INIT_Q1 = 1'b0;
+ parameter [0:0] INIT_Q2 = 1'b0;
+ parameter [0:0] INIT_Q3 = 1'b0;
+ parameter [0:0] INIT_Q4 = 1'b0;
+ parameter INTERFACE_TYPE = "MEMORY";
+ parameter IOBDELAY = "NONE";
+ parameter [0:0] IS_CLKB_INVERTED = 1'b0;
+ parameter [0:0] IS_CLKDIVP_INVERTED = 1'b0;
+ parameter [0:0] IS_CLKDIV_INVERTED = 1'b0;
+ parameter [0:0] IS_CLK_INVERTED = 1'b0;
+ parameter [0:0] IS_D_INVERTED = 1'b0;
+ parameter [0:0] IS_OCLKB_INVERTED = 1'b0;
+ parameter [0:0] IS_OCLK_INVERTED = 1'b0;
+ parameter integer NUM_CE = 2;
+ parameter OFB_USED = "FALSE";
+ parameter SERDES_MODE = "MASTER";
+ parameter [0:0] SRVAL_Q1 = 1'b0;
+ parameter [0:0] SRVAL_Q2 = 1'b0;
+ parameter [0:0] SRVAL_Q3 = 1'b0;
+ parameter [0:0] SRVAL_Q4 = 1'b0;
+ output O;
+ output Q1;
+ output Q2;
+ output Q3;
+ output Q4;
+ output Q5;
+ output Q6;
+ output Q7;
+ output Q8;
+ output SHIFTOUT1;
+ output SHIFTOUT2;
+ input BITSLIP;
+ input CE1;
+ input CE2;
+ input CLK;
+ input CLKB;
+ input CLKDIV;
+ input CLKDIVP;
+ input D;
+ input DDLY;
+ input DYNCLKDIVSEL;
+ input DYNCLKSEL;
+ input OCLK;
+ input OCLKB;
+ input OFB;
+ input RST;
+ input SHIFTIN1;
+ input SHIFTIN2;
+endmodule
+
+module KEEPER (...);
+endmodule
+
+module LDCE (...);
+ parameter [0:0] INIT = 1'b0;
+ parameter [0:0] IS_CLR_INVERTED = 1'b0;
+ parameter [0:0] IS_G_INVERTED = 1'b0;
+ parameter MSGON = "TRUE";
+ parameter XON = "TRUE";
+ output Q;
+ input CLR, D, G, GE;
+endmodule
+
+module LDPE (...);
+ parameter [0:0] INIT = 1'b1;
+ parameter [0:0] IS_G_INVERTED = 1'b0;
+ parameter [0:0] IS_PRE_INVERTED = 1'b0;
+ parameter MSGON = "TRUE";
+ parameter XON = "TRUE";
+ output Q;
+ input D, G, GE, PRE;
+endmodule
+
+module LUT6_2 (...);
+ parameter [63:0] INIT = 64'h0000000000000000;
+ input I0, I1, I2, I3, I4, I5;
+ output O5, O6;
+endmodule
+
+module MMCME2_ADV (...);
+ parameter BANDWIDTH = "OPTIMIZED";
+ parameter real CLKFBOUT_MULT_F = 5.000;
+ parameter real CLKFBOUT_PHASE = 0.000;
+ parameter CLKFBOUT_USE_FINE_PS = "FALSE";
+ parameter real CLKIN1_PERIOD = 0.000;
+ parameter real CLKIN2_PERIOD = 0.000;
+ parameter real CLKIN_FREQ_MAX = 1066.000;
+ parameter real CLKIN_FREQ_MIN = 10.000;
+ parameter real CLKOUT0_DIVIDE_F = 1.000;
+ parameter real CLKOUT0_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT0_PHASE = 0.000;
+ parameter CLKOUT0_USE_FINE_PS = "FALSE";
+ parameter integer CLKOUT1_DIVIDE = 1;
+ parameter real CLKOUT1_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT1_PHASE = 0.000;
+ parameter CLKOUT1_USE_FINE_PS = "FALSE";
+ parameter integer CLKOUT2_DIVIDE = 1;
+ parameter real CLKOUT2_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT2_PHASE = 0.000;
+ parameter CLKOUT2_USE_FINE_PS = "FALSE";
+ parameter integer CLKOUT3_DIVIDE = 1;
+ parameter real CLKOUT3_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT3_PHASE = 0.000;
+ parameter CLKOUT3_USE_FINE_PS = "FALSE";
+ parameter CLKOUT4_CASCADE = "FALSE";
+ parameter integer CLKOUT4_DIVIDE = 1;
+ parameter real CLKOUT4_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT4_PHASE = 0.000;
+ parameter CLKOUT4_USE_FINE_PS = "FALSE";
+ parameter integer CLKOUT5_DIVIDE = 1;
+ parameter real CLKOUT5_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT5_PHASE = 0.000;
+ parameter CLKOUT5_USE_FINE_PS = "FALSE";
+ parameter integer CLKOUT6_DIVIDE = 1;
+ parameter real CLKOUT6_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT6_PHASE = 0.000;
+ parameter CLKOUT6_USE_FINE_PS = "FALSE";
+ parameter real CLKPFD_FREQ_MAX = 550.000;
+ parameter real CLKPFD_FREQ_MIN = 10.000;
+ parameter COMPENSATION = "ZHOLD";
+ parameter integer DIVCLK_DIVIDE = 1;
+ parameter [0:0] IS_CLKINSEL_INVERTED = 1'b0;
+ parameter [0:0] IS_PSEN_INVERTED = 1'b0;
+ parameter [0:0] IS_PSINCDEC_INVERTED = 1'b0;
+ parameter [0:0] IS_PWRDWN_INVERTED = 1'b0;
+ parameter [0:0] IS_RST_INVERTED = 1'b0;
+ parameter real REF_JITTER1 = 0.010;
+ parameter real REF_JITTER2 = 0.010;
+ parameter SS_EN = "FALSE";
+ parameter SS_MODE = "CENTER_HIGH";
+ parameter integer SS_MOD_PERIOD = 10000;
+ parameter STARTUP_WAIT = "FALSE";
+ parameter real VCOCLK_FREQ_MAX = 1600.000;
+ parameter real VCOCLK_FREQ_MIN = 600.000;
+ parameter STARTUP_WAIT = "FALSE";
+ output CLKFBOUT;
+ output CLKFBOUTB;
+ output CLKFBSTOPPED;
+ output CLKINSTOPPED;
+ output CLKOUT0;
+ output CLKOUT0B;
+ output CLKOUT1;
+ output CLKOUT1B;
+ output CLKOUT2;
+ output CLKOUT2B;
+ output CLKOUT3;
+ output CLKOUT3B;
+ output CLKOUT4;
+ output CLKOUT5;
+ output CLKOUT6;
+ output [15:0] DO;
+ output DRDY;
+ output LOCKED;
+ output PSDONE;
+ input CLKFBIN;
+ input CLKIN1;
+ input CLKIN2;
+ input CLKINSEL;
+ input [6:0] DADDR;
+ input DCLK;
+ input DEN;
+ input [15:0] DI;
+ input DWE;
+ input PSCLK;
+ input PSEN;
+ input PSINCDEC;
+ input PWRDWN;
+ input RST;
+endmodule
+
+module MMCME2_BASE (...);
+ parameter BANDWIDTH = "OPTIMIZED";
+ parameter real CLKFBOUT_MULT_F = 5.000;
+ parameter real CLKFBOUT_PHASE = 0.000;
+ parameter real CLKIN1_PERIOD = 0.000;
+ parameter real CLKOUT0_DIVIDE_F = 1.000;
+ parameter real CLKOUT0_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT0_PHASE = 0.000;
+ parameter integer CLKOUT1_DIVIDE = 1;
+ parameter real CLKOUT1_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT1_PHASE = 0.000;
+ parameter integer CLKOUT2_DIVIDE = 1;
+ parameter real CLKOUT2_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT2_PHASE = 0.000;
+ parameter integer CLKOUT3_DIVIDE = 1;
+ parameter real CLKOUT3_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT3_PHASE = 0.000;
+ parameter CLKOUT4_CASCADE = "FALSE";
+ parameter integer CLKOUT4_DIVIDE = 1;
+ parameter real CLKOUT4_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT4_PHASE = 0.000;
+ parameter integer CLKOUT5_DIVIDE = 1;
+ parameter real CLKOUT5_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT5_PHASE = 0.000;
+ parameter integer CLKOUT6_DIVIDE = 1;
+ parameter real CLKOUT6_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT6_PHASE = 0.000;
+ parameter integer DIVCLK_DIVIDE = 1;
+ parameter real REF_JITTER1 = 0.010;
+ parameter STARTUP_WAIT = "FALSE";
+ output CLKFBOUT;
+ output CLKFBOUTB;
+ output CLKOUT0;
+ output CLKOUT0B;
+ output CLKOUT1;
+ output CLKOUT1B;
+ output CLKOUT2;
+ output CLKOUT2B;
+ output CLKOUT3;
+ output CLKOUT3B;
+ output CLKOUT4;
+ output CLKOUT5;
+ output CLKOUT6;
+ output LOCKED;
+ input CLKFBIN;
+ input CLKIN1;
+ input PWRDWN;
+ input RST;
+endmodule
+
+module OBUFDS (...);
+ parameter CAPACITANCE = "DONT_CARE";
+ parameter IOSTANDARD = "DEFAULT";
+ parameter SLEW = "SLOW";
+ output O, OB;
+ input I;
+endmodule
+
+module OBUFT (...);
+ parameter CAPACITANCE = "DONT_CARE";
+ parameter integer DRIVE = 12;
+ parameter IOSTANDARD = "DEFAULT";
+ parameter SLEW = "SLOW";
+ output O;
+ input I, T;
+endmodule
+
+module OBUFTDS (...);
+ parameter CAPACITANCE = "DONT_CARE";
+ parameter IOSTANDARD = "DEFAULT";
+ parameter SLEW = "SLOW";
+ output O, OB;
+ input I, T;
+endmodule
+
+module ODDR (...);
+ output Q;
+ input C;
+ input CE;
+ input D1;
+ input D2;
+ input R;
+ input S;
+ parameter DDR_CLK_EDGE = "OPPOSITE_EDGE";
+ parameter INIT = 1'b0;
+ parameter [0:0] IS_C_INVERTED = 1'b0;
+ parameter [0:0] IS_D1_INVERTED = 1'b0;
+ parameter [0:0] IS_D2_INVERTED = 1'b0;
+ parameter SRTYPE = "SYNC";
+ parameter MSGON = "TRUE";
+ parameter XON = "TRUE";
+endmodule
+
+module ODELAYE2 (...);
+ parameter CINVCTRL_SEL = "FALSE";
+ parameter DELAY_SRC = "ODATAIN";
+ parameter HIGH_PERFORMANCE_MODE = "FALSE";
+ parameter [0:0] IS_C_INVERTED = 1'b0;
+ parameter [0:0] IS_ODATAIN_INVERTED = 1'b0;
+ parameter ODELAY_TYPE = "FIXED";
+ parameter integer ODELAY_VALUE = 0;
+ parameter PIPE_SEL = "FALSE";
+ parameter real REFCLK_FREQUENCY = 200.0;
+ parameter SIGNAL_PATTERN = "DATA";
+ parameter integer SIM_DELAY_D = 0;
+ output [4:0] CNTVALUEOUT;
+ output DATAOUT;
+ input C;
+ input CE;
+ input CINVCTRL;
+ input CLKIN;
+ input [4:0] CNTVALUEIN;
+ input INC;
+ input LD;
+ input LDPIPEEN;
+ input ODATAIN;
+ input REGRST;
+endmodule
+
+module OSERDESE2 (...);
+ parameter DATA_RATE_OQ = "DDR";
+ parameter DATA_RATE_TQ = "DDR";
+ parameter integer DATA_WIDTH = 4;
+ parameter [0:0] INIT_OQ = 1'b0;
+ parameter [0:0] INIT_TQ = 1'b0;
+ parameter [0:0] IS_CLKDIV_INVERTED = 1'b0;
+ parameter [0:0] IS_CLK_INVERTED = 1'b0;
+ parameter [0:0] IS_D1_INVERTED = 1'b0;
+ parameter [0:0] IS_D2_INVERTED = 1'b0;
+ parameter [0:0] IS_D3_INVERTED = 1'b0;
+ parameter [0:0] IS_D4_INVERTED = 1'b0;
+ parameter [0:0] IS_D5_INVERTED = 1'b0;
+ parameter [0:0] IS_D6_INVERTED = 1'b0;
+ parameter [0:0] IS_D7_INVERTED = 1'b0;
+ parameter [0:0] IS_D8_INVERTED = 1'b0;
+ parameter [0:0] IS_T1_INVERTED = 1'b0;
+ parameter [0:0] IS_T2_INVERTED = 1'b0;
+ parameter [0:0] IS_T3_INVERTED = 1'b0;
+ parameter [0:0] IS_T4_INVERTED = 1'b0;
+ parameter SERDES_MODE = "MASTER";
+ parameter [0:0] SRVAL_OQ = 1'b0;
+ parameter [0:0] SRVAL_TQ = 1'b0;
+ parameter TBYTE_CTL = "FALSE";
+ parameter TBYTE_SRC = "FALSE";
+ parameter integer TRISTATE_WIDTH = 4;
+ output OFB;
+ output OQ;
+ output SHIFTOUT1;
+ output SHIFTOUT2;
+ output TBYTEOUT;
+ output TFB;
+ output TQ;
+ input CLK;
+ input CLKDIV;
+ input D1;
+ input D2;
+ input D3;
+ input D4;
+ input D5;
+ input D6;
+ input D7;
+ input D8;
+ input OCE;
+ input RST;
+ input SHIFTIN1;
+ input SHIFTIN2;
+ input T1;
+ input T2;
+ input T3;
+ input T4;
+ input TBYTEIN;
+ input TCE;
+endmodule
+
+module OUT_FIFO (...);
+ parameter integer ALMOST_EMPTY_VALUE = 1;
+ parameter integer ALMOST_FULL_VALUE = 1;
+ parameter ARRAY_MODE = "ARRAY_MODE_8_X_4";
+ parameter OUTPUT_DISABLE = "FALSE";
+ parameter SYNCHRONOUS_MODE = "FALSE";
+ output ALMOSTEMPTY;
+ output ALMOSTFULL;
+ output EMPTY;
+ output FULL;
+ output [3:0] Q0;
+ output [3:0] Q1;
+ output [3:0] Q2;
+ output [3:0] Q3;
+ output [3:0] Q4;
+ output [3:0] Q7;
+ output [3:0] Q8;
+ output [3:0] Q9;
+ output [7:0] Q5;
+ output [7:0] Q6;
+ input RDCLK;
+ input RDEN;
+ input RESET;
+ input WRCLK;
+ input WREN;
+ input [7:0] D0;
+ input [7:0] D1;
+ input [7:0] D2;
+ input [7:0] D3;
+ input [7:0] D4;
+ input [7:0] D5;
+ input [7:0] D6;
+ input [7:0] D7;
+ input [7:0] D8;
+ input [7:0] D9;
+endmodule
+
+module PHASER_IN (...);
+ parameter integer CLKOUT_DIV = 4;
+ parameter DQS_BIAS_MODE = "FALSE";
+ parameter EN_ISERDES_RST = "FALSE";
+ parameter integer FINE_DELAY = 0;
+ parameter FREQ_REF_DIV = "NONE";
+ parameter [0:0] IS_RST_INVERTED = 1'b0;
+ parameter real MEMREFCLK_PERIOD = 0.000;
+ parameter OUTPUT_CLK_SRC = "PHASE_REF";
+ parameter real PHASEREFCLK_PERIOD = 0.000;
+ parameter real REFCLK_PERIOD = 0.000;
+ parameter integer SEL_CLK_OFFSET = 5;
+ parameter SYNC_IN_DIV_RST = "FALSE";
+ output FINEOVERFLOW;
+ output ICLK;
+ output ICLKDIV;
+ output ISERDESRST;
+ output RCLK;
+ output [5:0] COUNTERREADVAL;
+ input COUNTERLOADEN;
+ input COUNTERREADEN;
+ input DIVIDERST;
+ input EDGEADV;
+ input FINEENABLE;
+ input FINEINC;
+ input FREQREFCLK;
+ input MEMREFCLK;
+ input PHASEREFCLK;
+ input RST;
+ input SYNCIN;
+ input SYSCLK;
+ input [1:0] RANKSEL;
+ input [5:0] COUNTERLOADVAL;
+endmodule
+
+module PHASER_IN_PHY (...);
+ parameter BURST_MODE = "FALSE";
+ parameter integer CLKOUT_DIV = 4;
+ parameter [0:0] DQS_AUTO_RECAL = 1'b1;
+ parameter DQS_BIAS_MODE = "FALSE";
+ parameter [2:0] DQS_FIND_PATTERN = 3'b001;
+ parameter integer FINE_DELAY = 0;
+ parameter FREQ_REF_DIV = "NONE";
+ parameter [0:0] IS_RST_INVERTED = 1'b0;
+ parameter real MEMREFCLK_PERIOD = 0.000;
+ parameter OUTPUT_CLK_SRC = "PHASE_REF";
+ parameter real PHASEREFCLK_PERIOD = 0.000;
+ parameter real REFCLK_PERIOD = 0.000;
+ parameter integer SEL_CLK_OFFSET = 5;
+ parameter SYNC_IN_DIV_RST = "FALSE";
+ parameter WR_CYCLES = "FALSE";
+ output DQSFOUND;
+ output DQSOUTOFRANGE;
+ output FINEOVERFLOW;
+ output ICLK;
+ output ICLKDIV;
+ output ISERDESRST;
+ output PHASELOCKED;
+ output RCLK;
+ output WRENABLE;
+ output [5:0] COUNTERREADVAL;
+ input BURSTPENDINGPHY;
+ input COUNTERLOADEN;
+ input COUNTERREADEN;
+ input FINEENABLE;
+ input FINEINC;
+ input FREQREFCLK;
+ input MEMREFCLK;
+ input PHASEREFCLK;
+ input RST;
+ input RSTDQSFIND;
+ input SYNCIN;
+ input SYSCLK;
+ input [1:0] ENCALIBPHY;
+ input [1:0] RANKSELPHY;
+ input [5:0] COUNTERLOADVAL;
+endmodule
+
+module PHASER_OUT (...);
+ parameter integer CLKOUT_DIV = 4;
+ parameter COARSE_BYPASS = "FALSE";
+ parameter integer COARSE_DELAY = 0;
+ parameter EN_OSERDES_RST = "FALSE";
+ parameter integer FINE_DELAY = 0;
+ parameter [0:0] IS_RST_INVERTED = 1'b0;
+ parameter real MEMREFCLK_PERIOD = 0.000;
+ parameter OCLKDELAY_INV = "FALSE";
+ parameter integer OCLK_DELAY = 0;
+ parameter OUTPUT_CLK_SRC = "PHASE_REF";
+ parameter real PHASEREFCLK_PERIOD = 0.000;
+ parameter [2:0] PO = 3'b000;
+ parameter real REFCLK_PERIOD = 0.000;
+ parameter SYNC_IN_DIV_RST = "FALSE";
+ output COARSEOVERFLOW;
+ output FINEOVERFLOW;
+ output OCLK;
+ output OCLKDELAYED;
+ output OCLKDIV;
+ output OSERDESRST;
+ output [8:0] COUNTERREADVAL;
+ input COARSEENABLE;
+ input COARSEINC;
+ input COUNTERLOADEN;
+ input COUNTERREADEN;
+ input DIVIDERST;
+ input EDGEADV;
+ input FINEENABLE;
+ input FINEINC;
+ input FREQREFCLK;
+ input MEMREFCLK;
+ input PHASEREFCLK;
+ input RST;
+ input SELFINEOCLKDELAY;
+ input SYNCIN;
+ input SYSCLK;
+ input [8:0] COUNTERLOADVAL;
+endmodule
+
+module PHASER_OUT_PHY (...);
+ parameter integer CLKOUT_DIV = 4;
+ parameter COARSE_BYPASS = "FALSE";
+ parameter integer COARSE_DELAY = 0;
+ parameter DATA_CTL_N = "FALSE";
+ parameter DATA_RD_CYCLES = "FALSE";
+ parameter integer FINE_DELAY = 0;
+ parameter [0:0] IS_RST_INVERTED = 1'b0;
+ parameter real MEMREFCLK_PERIOD = 0.000;
+ parameter OCLKDELAY_INV = "FALSE";
+ parameter integer OCLK_DELAY = 0;
+ parameter OUTPUT_CLK_SRC = "PHASE_REF";
+ parameter real PHASEREFCLK_PERIOD = 0.000;
+ parameter [2:0] PO = 3'b000;
+ parameter real REFCLK_PERIOD = 0.000;
+ parameter SYNC_IN_DIV_RST = "FALSE";
+ output COARSEOVERFLOW;
+ output FINEOVERFLOW;
+ output OCLK;
+ output OCLKDELAYED;
+ output OCLKDIV;
+ output OSERDESRST;
+ output RDENABLE;
+ output [1:0] CTSBUS;
+ output [1:0] DQSBUS;
+ output [1:0] DTSBUS;
+ output [8:0] COUNTERREADVAL;
+ input BURSTPENDINGPHY;
+ input COARSEENABLE;
+ input COARSEINC;
+ input COUNTERLOADEN;
+ input COUNTERREADEN;
+ input FINEENABLE;
+ input FINEINC;
+ input FREQREFCLK;
+ input MEMREFCLK;
+ input PHASEREFCLK;
+ input RST;
+ input SELFINEOCLKDELAY;
+ input SYNCIN;
+ input SYSCLK;
+ input [1:0] ENCALIBPHY;
+ input [8:0] COUNTERLOADVAL;
+endmodule
+
+module PHASER_REF (...);
+ parameter [0:0] IS_RST_INVERTED = 1'b0;
+ parameter [0:0] IS_PWRDWN_INVERTED = 1'b0;
+ output LOCKED;
+ input CLKIN;
+ input PWRDWN;
+ input RST;
+endmodule
+
+module PHY_CONTROL (...);
+ parameter integer AO_TOGGLE = 0;
+ parameter [3:0] AO_WRLVL_EN = 4'b0000;
+ parameter BURST_MODE = "FALSE";
+ parameter integer CLK_RATIO = 1;
+ parameter integer CMD_OFFSET = 0;
+ parameter integer CO_DURATION = 0;
+ parameter DATA_CTL_A_N = "FALSE";
+ parameter DATA_CTL_B_N = "FALSE";
+ parameter DATA_CTL_C_N = "FALSE";
+ parameter DATA_CTL_D_N = "FALSE";
+ parameter DISABLE_SEQ_MATCH = "TRUE";
+ parameter integer DI_DURATION = 0;
+ parameter integer DO_DURATION = 0;
+ parameter integer EVENTS_DELAY = 63;
+ parameter integer FOUR_WINDOW_CLOCKS = 63;
+ parameter MULTI_REGION = "FALSE";
+ parameter PHY_COUNT_ENABLE = "FALSE";
+ parameter integer RD_CMD_OFFSET_0 = 0;
+ parameter integer RD_CMD_OFFSET_1 = 00;
+ parameter integer RD_CMD_OFFSET_2 = 0;
+ parameter integer RD_CMD_OFFSET_3 = 0;
+ parameter integer RD_DURATION_0 = 0;
+ parameter integer RD_DURATION_1 = 0;
+ parameter integer RD_DURATION_2 = 0;
+ parameter integer RD_DURATION_3 = 0;
+ parameter SYNC_MODE = "FALSE";
+ parameter integer WR_CMD_OFFSET_0 = 0;
+ parameter integer WR_CMD_OFFSET_1 = 0;
+ parameter integer WR_CMD_OFFSET_2 = 0;
+ parameter integer WR_CMD_OFFSET_3 = 0;
+ parameter integer WR_DURATION_0 = 0;
+ parameter integer WR_DURATION_1 = 0;
+ parameter integer WR_DURATION_2 = 0;
+ parameter integer WR_DURATION_3 = 0;
+ output PHYCTLALMOSTFULL;
+ output PHYCTLEMPTY;
+ output PHYCTLFULL;
+ output PHYCTLREADY;
+ output [1:0] INRANKA;
+ output [1:0] INRANKB;
+ output [1:0] INRANKC;
+ output [1:0] INRANKD;
+ output [1:0] PCENABLECALIB;
+ output [3:0] AUXOUTPUT;
+ output [3:0] INBURSTPENDING;
+ output [3:0] OUTBURSTPENDING;
+ input MEMREFCLK;
+ input PHYCLK;
+ input PHYCTLMSTREMPTY;
+ input PHYCTLWRENABLE;
+ input PLLLOCK;
+ input READCALIBENABLE;
+ input REFDLLLOCK;
+ input RESET;
+ input SYNCIN;
+ input WRITECALIBENABLE;
+ input [31:0] PHYCTLWD;
+endmodule
+
+module PLLE2_ADV (...);
+ parameter BANDWIDTH = "OPTIMIZED";
+ parameter COMPENSATION = "ZHOLD";
+ parameter STARTUP_WAIT = "FALSE";
+ parameter integer CLKOUT0_DIVIDE = 1;
+ parameter integer CLKOUT1_DIVIDE = 1;
+ parameter integer CLKOUT2_DIVIDE = 1;
+ parameter integer CLKOUT3_DIVIDE = 1;
+ parameter integer CLKOUT4_DIVIDE = 1;
+ parameter integer CLKOUT5_DIVIDE = 1;
+ parameter integer DIVCLK_DIVIDE = 1;
+ parameter integer CLKFBOUT_MULT = 5;
+ parameter real CLKFBOUT_PHASE = 0.000;
+ parameter real CLKIN1_PERIOD = 0.000;
+ parameter real CLKIN2_PERIOD = 0.000;
+ parameter real CLKOUT0_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT0_PHASE = 0.000;
+ parameter real CLKOUT1_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT1_PHASE = 0.000;
+ parameter real CLKOUT2_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT2_PHASE = 0.000;
+ parameter real CLKOUT3_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT3_PHASE = 0.000;
+ parameter real CLKOUT4_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT4_PHASE = 0.000;
+ parameter real CLKOUT5_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT5_PHASE = 0.000;
+ parameter [0:0] IS_CLKINSEL_INVERTED = 1'b0;
+ parameter [0:0] IS_PWRDWN_INVERTED = 1'b0;
+ parameter [0:0] IS_RST_INVERTED = 1'b0;
+ parameter real REF_JITTER1 = 0.010;
+ parameter real REF_JITTER2 = 0.010;
+ parameter real VCOCLK_FREQ_MAX = 2133.000;
+ parameter real VCOCLK_FREQ_MIN = 800.000;
+ parameter real CLKIN_FREQ_MAX = 1066.000;
+ parameter real CLKIN_FREQ_MIN = 19.000;
+ parameter real CLKPFD_FREQ_MAX = 550.0;
+ parameter real CLKPFD_FREQ_MIN = 19.0;
+ output CLKFBOUT;
+ output CLKOUT0;
+ output CLKOUT1;
+ output CLKOUT2;
+ output CLKOUT3;
+ output CLKOUT4;
+ output CLKOUT5;
+ output DRDY;
+ output LOCKED;
+ output [15:0] DO;
+ input CLKFBIN;
+ input CLKIN1;
+ input CLKIN2;
+ input CLKINSEL;
+ input DCLK;
+ input DEN;
+ input DWE;
+ input PWRDWN;
+ input RST;
+ input [15:0] DI;
+ input [6:0] DADDR;
+endmodule
+
+module PLLE2_BASE (...);
+ parameter BANDWIDTH = "OPTIMIZED";
+ parameter integer CLKFBOUT_MULT = 5;
+ parameter real CLKFBOUT_PHASE = 0.000;
+ parameter real CLKIN1_PERIOD = 0.000;
+ parameter integer CLKOUT0_DIVIDE = 1;
+ parameter real CLKOUT0_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT0_PHASE = 0.000;
+ parameter integer CLKOUT1_DIVIDE = 1;
+ parameter real CLKOUT1_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT1_PHASE = 0.000;
+ parameter integer CLKOUT2_DIVIDE = 1;
+ parameter real CLKOUT2_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT2_PHASE = 0.000;
+ parameter integer CLKOUT3_DIVIDE = 1;
+ parameter real CLKOUT3_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT3_PHASE = 0.000;
+ parameter integer CLKOUT4_DIVIDE = 1;
+ parameter real CLKOUT4_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT4_PHASE = 0.000;
+ parameter integer CLKOUT5_DIVIDE = 1;
+ parameter real CLKOUT5_DUTY_CYCLE = 0.500;
+ parameter real CLKOUT5_PHASE = 0.000;
+ parameter integer DIVCLK_DIVIDE = 1;
+ parameter real REF_JITTER1 = 0.010;
+ parameter STARTUP_WAIT = "FALSE";
+ output CLKFBOUT;
+ output CLKOUT0;
+ output CLKOUT1;
+ output CLKOUT2;
+ output CLKOUT3;
+ output CLKOUT4;
+ output CLKOUT5;
+ output LOCKED;
+ input CLKFBIN;
+ input CLKIN1;
+ input PWRDWN;
+ input RST;
+endmodule
+
+module PULLDOWN (...);
+ output O;
+endmodule
+
+module PULLUP (...);
+ output O;
+endmodule
+
+module RAM128X1S (...);
+ parameter [127:0] INIT = 128'h00000000000000000000000000000000;
+ parameter [0:0] IS_WCLK_INVERTED = 1'b0;
+ output O;
+ input A0, A1, A2, A3, A4, A5, A6, D, WCLK, WE;
+endmodule
+
+module RAM256X1S (...);
+ parameter [255:0] INIT = 256'h0;
+ parameter [0:0] IS_WCLK_INVERTED = 1'b0;
+ output O;
+ input [7:0] A;
+ input D;
+ input WCLK;
+ input WE;
+endmodule
+
+module RAM32M (...);
+ parameter [63:0] INIT_A = 64'h0000000000000000;
+ parameter [63:0] INIT_B = 64'h0000000000000000;
+ parameter [63:0] INIT_C = 64'h0000000000000000;
+ parameter [63:0] INIT_D = 64'h0000000000000000;
+ parameter [0:0] IS_WCLK_INVERTED = 1'b0;
+ output [1:0] DOA;
+ output [1:0] DOB;
+ output [1:0] DOC;
+ output [1:0] DOD;
+ input [4:0] ADDRA;
+ input [4:0] ADDRB;
+ input [4:0] ADDRC;
+ input [4:0] ADDRD;
+ input [1:0] DIA;
+ input [1:0] DIB;
+ input [1:0] DIC;
+ input [1:0] DID;
+ input WCLK;
+ input WE;
+endmodule
+
+module RAM32X1D (...);
+ parameter [31:0] INIT = 32'h00000000;
+ parameter [0:0] IS_WCLK_INVERTED = 1'b0;
+ output DPO, SPO;
+ input A0, A1, A2, A3, A4, D, DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, WCLK, WE;
+endmodule
+
+module RAM32X1S (...);
+ parameter [31:0] INIT = 32'h00000000;
+ parameter [0:0] IS_WCLK_INVERTED = 1'b0;
+ output O;
+ input A0, A1, A2, A3, A4, D, WCLK, WE;
+endmodule
+
+module RAM32X1S_1 (...);
+ parameter [31:0] INIT = 32'h00000000;
+ parameter [0:0] IS_WCLK_INVERTED = 1'b0;
+ output O;
+ input A0, A1, A2, A3, A4, D, WCLK, WE;
+endmodule
+
+module RAM32X2S (...);
+ parameter [31:0] INIT_00 = 32'h00000000;
+ parameter [31:0] INIT_01 = 32'h00000000;
+ parameter [0:0] IS_WCLK_INVERTED = 1'b0;
+ output O0, O1;
+ input A0, A1, A2, A3, A4, D0, D1, WCLK, WE;
+endmodule
+
+module RAM64M (...);
+ parameter [63:0] INIT_A = 64'h0000000000000000;
+ parameter [63:0] INIT_B = 64'h0000000000000000;
+ parameter [63:0] INIT_C = 64'h0000000000000000;
+ parameter [63:0] INIT_D = 64'h0000000000000000;
+ parameter [0:0] IS_WCLK_INVERTED = 1'b0;
+ output DOA;
+ output DOB;
+ output DOC;
+ output DOD;
+ input [5:0] ADDRA;
+ input [5:0] ADDRB;
+ input [5:0] ADDRC;
+ input [5:0] ADDRD;
+ input DIA;
+ input DIB;
+ input DIC;
+ input DID;
+ input WCLK;
+ input WE;
+endmodule
+
+module RAM64X1S (...);
+ parameter [63:0] INIT = 64'h0000000000000000;
+ parameter [0:0] IS_WCLK_INVERTED = 1'b0;
+ output O;
+ input A0, A1, A2, A3, A4, A5, D, WCLK, WE;
+endmodule
+
+module RAM64X1S_1 (...);
+ parameter [63:0] INIT = 64'h0000000000000000;
+ parameter [0:0] IS_WCLK_INVERTED = 1'b0;
+ output O;
+ input A0, A1, A2, A3, A4, A5, D, WCLK, WE;
+endmodule
+
+module RAM64X2S (...);
+ parameter [63:0] INIT_00 = 64'h0000000000000000;
+ parameter [63:0] INIT_01 = 64'h0000000000000000;
+ parameter [0:0] IS_WCLK_INVERTED = 1'b0;
+ output O0, O1;
+ input A0, A1, A2, A3, A4, A5, D0, D1, WCLK, WE;
+endmodule
+
+module ROM128X1 (...);
+ parameter [127:0] INIT = 128'h00000000000000000000000000000000;
+ output O;
+ input A0, A1, A2, A3, A4, A5, A6;
+endmodule
+
+module ROM256X1 (...);
+ parameter [255:0] INIT = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ output O;
+ input A0, A1, A2, A3, A4, A5, A6, A7;
+endmodule
+
+module ROM32X1 (...);
+ parameter [31:0] INIT = 32'h00000000;
+ output O;
+ input A0, A1, A2, A3, A4;
+endmodule
+
+module ROM64X1 (...);
+ parameter [63:0] INIT = 64'h0000000000000000;
+ output O;
+ input A0, A1, A2, A3, A4, A5;
+endmodule
+
+module SRL16E (...);
+ parameter [15:0] INIT = 16'h0000;
+ parameter [0:0] IS_CLK_INVERTED = 1'b0;
+ output Q;
+ input A0, A1, A2, A3, CE, CLK, D;
+endmodule
+
+module SRLC32E (...);
+ parameter [31:0] INIT = 32'h00000000;
+ parameter [0:0] IS_CLK_INVERTED = 1'b0;
+ output Q;
+ output Q31;
+ input [4:0] A;
+ input CE, CLK, D;
+endmodule
+
+module STARTUPE2 (...);
+ parameter PROG_USR = "FALSE";
+ parameter real SIM_CCLK_FREQ = 0.0;
+ output CFGCLK;
+ output CFGMCLK;
+ output EOS;
+ output PREQ;
+ input CLK;
+ input GSR;
+ input GTS;
+ input KEYCLEARB;
+ input PACK;
+ input USRCCLKO;
+ input USRCCLKTS;
+ input USRDONEO;
+ input USRDONETS;
+endmodule
+
+module USR_ACCESSE2 (...);
+ output CFGCLK;
+ output DATAVALID;
+ output [31:0] DATA;
+endmodule
+
+module XADC (...);
+ output BUSY;
+ output DRDY;
+ output EOC;
+ output EOS;
+ output JTAGBUSY;
+ output JTAGLOCKED;
+ output JTAGMODIFIED;
+ output OT;
+ output [15:0] DO;
+ output [7:0] ALM;
+ output [4:0] CHANNEL;
+ output [4:0] MUXADDR;
+ input CONVST;
+ input CONVSTCLK;
+ input DCLK;
+ input DEN;
+ input DWE;
+ input RESET;
+ input VN;
+ input VP;
+ input [15:0] DI;
+ input [15:0] VAUXN;
+ input [15:0] VAUXP;
+ input [6:0] DADDR;
+ parameter [15:0] INIT_40 = 16'h0;
+ parameter [15:0] INIT_41 = 16'h0;
+ parameter [15:0] INIT_42 = 16'h0800;
+ parameter [15:0] INIT_43 = 16'h0;
+ parameter [15:0] INIT_44 = 16'h0;
+ parameter [15:0] INIT_45 = 16'h0;
+ parameter [15:0] INIT_46 = 16'h0;
+ parameter [15:0] INIT_47 = 16'h0;
+ parameter [15:0] INIT_48 = 16'h0;
+ parameter [15:0] INIT_49 = 16'h0;
+ parameter [15:0] INIT_4A = 16'h0;
+ parameter [15:0] INIT_4B = 16'h0;
+ parameter [15:0] INIT_4C = 16'h0;
+ parameter [15:0] INIT_4D = 16'h0;
+ parameter [15:0] INIT_4E = 16'h0;
+ parameter [15:0] INIT_4F = 16'h0;
+ parameter [15:0] INIT_50 = 16'h0;
+ parameter [15:0] INIT_51 = 16'h0;
+ parameter [15:0] INIT_52 = 16'h0;
+ parameter [15:0] INIT_53 = 16'h0;
+ parameter [15:0] INIT_54 = 16'h0;
+ parameter [15:0] INIT_55 = 16'h0;
+ parameter [15:0] INIT_56 = 16'h0;
+ parameter [15:0] INIT_57 = 16'h0;
+ parameter [15:0] INIT_58 = 16'h0;
+ parameter [15:0] INIT_59 = 16'h0;
+ parameter [15:0] INIT_5A = 16'h0;
+ parameter [15:0] INIT_5B = 16'h0;
+ parameter [15:0] INIT_5C = 16'h0;
+ parameter [15:0] INIT_5D = 16'h0;
+ parameter [15:0] INIT_5E = 16'h0;
+ parameter [15:0] INIT_5F = 16'h0;
+ parameter IS_CONVSTCLK_INVERTED = 1'b0;
+ parameter IS_DCLK_INVERTED = 1'b0;
+ parameter SIM_DEVICE = "7SERIES";
+ parameter SIM_MONITOR_FILE = "design.txt";
+endmodule
+
diff --git a/techlibs/xilinx/drams.txt b/techlibs/xilinx/drams.txt
new file mode 100644
index 00000000..e6635d0e
--- /dev/null
+++ b/techlibs/xilinx/drams.txt
@@ -0,0 +1,36 @@
+
+bram $__XILINX_RAM64X1D
+ init 1
+ abits 6
+ dbits 1
+ groups 2
+ ports 1 1
+ wrmode 0 1
+ enable 0 1
+ transp 0 0
+ clocks 0 1
+ clkpol 0 2
+endbram
+
+bram $__XILINX_RAM128X1D
+ init 1
+ abits 7
+ dbits 1
+ groups 2
+ ports 1 1
+ wrmode 0 1
+ enable 0 1
+ transp 0 0
+ clocks 0 1
+ clkpol 0 2
+endbram
+
+match $__XILINX_RAM64X1D
+ make_outreg
+ or_next_if_better
+endmatch
+
+match $__XILINX_RAM128X1D
+ make_outreg
+endmatch
+
diff --git a/techlibs/xilinx/drams_bb.v b/techlibs/xilinx/drams_bb.v
new file mode 100644
index 00000000..11168fe1
--- /dev/null
+++ b/techlibs/xilinx/drams_bb.v
@@ -0,0 +1,20 @@
+
+module RAM64X1D (
+ output DPO, SPO,
+ input D, WCLK, WE,
+ input A0, A1, A2, A3, A4, A5,
+ input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, DPRA5
+);
+ parameter INIT = 64'h0;
+ parameter IS_WCLK_INVERTED = 1'b0;
+endmodule
+
+module RAM128X1D (
+ output DPO, SPO,
+ input D, WCLK, WE,
+ input [6:0] A, DPRA
+);
+ parameter INIT = 128'h0;
+ parameter IS_WCLK_INVERTED = 1'b0;
+endmodule
+
diff --git a/techlibs/xilinx/drams_map.v b/techlibs/xilinx/drams_map.v
new file mode 100644
index 00000000..47476b59
--- /dev/null
+++ b/techlibs/xilinx/drams_map.v
@@ -0,0 +1,63 @@
+
+module \$__XILINX_RAM64X1D (CLK1, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN);
+ parameter [63:0] INIT = 64'bx;
+ parameter CLKPOL2 = 1;
+ input CLK1;
+
+ input [5:0] A1ADDR;
+ output A1DATA;
+
+ input [5:0] B1ADDR;
+ input B1DATA;
+ input B1EN;
+
+ RAM64X1D #(
+ .INIT(INIT),
+ .IS_WCLK_INVERTED(!CLKPOL2)
+ ) _TECHMAP_REPLACE_ (
+ .DPRA0(A1ADDR[0]),
+ .DPRA1(A1ADDR[1]),
+ .DPRA2(A1ADDR[2]),
+ .DPRA3(A1ADDR[3]),
+ .DPRA4(A1ADDR[4]),
+ .DPRA5(A1ADDR[5]),
+ .DPO(A1DATA),
+
+ .A0(B1ADDR[0]),
+ .A1(B1ADDR[1]),
+ .A2(B1ADDR[2]),
+ .A3(B1ADDR[3]),
+ .A4(B1ADDR[4]),
+ .A5(B1ADDR[5]),
+ .D(B1DATA),
+ .WCLK(CLK1),
+ .WE(B1EN)
+ );
+endmodule
+
+module \$__XILINX_RAM128X1D (CLK1, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN);
+ parameter [127:0] INIT = 128'bx;
+ parameter CLKPOL2 = 1;
+ input CLK1;
+
+ input [6:0] A1ADDR;
+ output A1DATA;
+
+ input [6:0] B1ADDR;
+ input B1DATA;
+ input B1EN;
+
+ RAM128X1D #(
+ .INIT(INIT),
+ .IS_WCLK_INVERTED(!CLKPOL2)
+ ) _TECHMAP_REPLACE_ (
+ .DPRA(A1ADDR),
+ .DPO(A1DATA),
+
+ .A(B1ADDR),
+ .D(B1DATA),
+ .WCLK(CLK1),
+ .WE(B1EN)
+ );
+endmodule
+
diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc
new file mode 100644
index 00000000..e7ec1e6e
--- /dev/null
+++ b/techlibs/xilinx/synth_xilinx.cc
@@ -0,0 +1,239 @@
+/*
+ * 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/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+bool check_label(bool &active, std::string run_from, std::string run_to, std::string label)
+{
+ if (label == run_from)
+ active = true;
+ if (label == run_to)
+ active = false;
+ return active;
+}
+
+struct SynthXilinxPass : public Pass {
+ SynthXilinxPass() : Pass("synth_xilinx", "synthesis for Xilinx FPGAs") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" synth_xilinx [options]\n");
+ log("\n");
+ log("This command runs synthesis for Xilinx FPGAs. This command does not operate on\n");
+ log("partly selected designs. At the moment this command creates netlists that are\n");
+ log("compatible with 7-Series Xilinx devices.\n");
+ log("\n");
+ log(" -top <module>\n");
+ log(" use the specified module as top module\n");
+ log("\n");
+ log(" -edif <file>\n");
+ log(" write the design to the specified edif file. writing of an output file\n");
+ log(" is omitted if this parameter is not specified.\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 'begin', and empty to label is\n");
+ log(" synonymous to the end of the command list.\n");
+ log("\n");
+ log(" -flatten\n");
+ log(" flatten design before synthesis\n");
+ log("\n");
+ log(" -retime\n");
+ log(" run 'abc' with -dff option\n");
+ log("\n");
+ log("\n");
+ log("The following commands are executed by this synthesis command:\n");
+ log("\n");
+ log(" begin:\n");
+ log(" read_verilog -lib +/xilinx/cells_sim.v\n");
+ log(" read_verilog -lib +/xilinx/cells_xtra.v\n");
+ log(" read_verilog -lib +/xilinx/brams_bb.v\n");
+ log(" read_verilog -lib +/xilinx/drams_bb.v\n");
+ log(" hierarchy -check -top <top>\n");
+ log("\n");
+ log(" flatten: (only if -flatten)\n");
+ log(" proc\n");
+ log(" flatten\n");
+ log("\n");
+ log(" coarse:\n");
+ log(" synth -run coarse\n");
+ log("\n");
+ log(" bram:\n");
+ log(" memory_bram -rules +/xilinx/brams.txt\n");
+ log(" techmap -map +/xilinx/brams_map.v\n");
+ log("\n");
+ log(" dram:\n");
+ log(" memory_bram -rules +/xilinx/drams.txt\n");
+ log(" techmap -map +/xilinx/drams_map.v\n");
+ log("\n");
+ log(" fine:\n");
+ log(" opt -fast -full\n");
+ log(" memory_map\n");
+ log(" dffsr2dff\n");
+ log(" dff2dffe\n");
+ log(" opt -full\n");
+ log(" techmap -map +/techmap.v -map +/xilinx/arith_map.v\n");
+ log(" opt -fast\n");
+ log("\n");
+ log(" map_luts:\n");
+ log(" abc -luts 2:2,3,6:5,10,20 [-dff]\n");
+ log(" clean\n");
+ log("\n");
+ log(" map_cells:\n");
+ log(" techmap -map +/xilinx/cells_map.v\n");
+ log(" dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT\n");
+ log(" clean\n");
+ log("\n");
+ log(" check:\n");
+ log(" hierarchy -check\n");
+ log(" stat\n");
+ log(" check -noinit\n");
+ log("\n");
+ log(" edif: (only if -edif)\n");
+ log(" write_edif <file-name>\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::string top_opt = "-auto-top";
+ std::string edif_file;
+ std::string run_from, run_to;
+ bool flatten = false;
+ bool retime = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-top" && argidx+1 < args.size()) {
+ top_opt = "-top " + args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-edif" && argidx+1 < args.size()) {
+ edif_file = args[++argidx];
+ continue;
+ }
+ 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] == "-flatten") {
+ flatten = true;
+ continue;
+ }
+ if (args[argidx] == "-retime") {
+ retime = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!design->full_selection())
+ log_cmd_error("This comannd only operates on fully selected designs!\n");
+
+ bool active = run_from.empty();
+
+ log_header(design, "Executing SYNTH_XILINX pass.\n");
+ log_push();
+
+ if (check_label(active, run_from, run_to, "begin"))
+ {
+ Pass::call(design, "read_verilog -lib +/xilinx/cells_sim.v");
+ Pass::call(design, "read_verilog -lib +/xilinx/cells_xtra.v");
+ Pass::call(design, "read_verilog -lib +/xilinx/brams_bb.v");
+ Pass::call(design, "read_verilog -lib +/xilinx/drams_bb.v");
+ Pass::call(design, stringf("hierarchy -check %s", top_opt.c_str()));
+ }
+
+ if (flatten && check_label(active, run_from, run_to, "flatten"))
+ {
+ Pass::call(design, "proc");
+ Pass::call(design, "flatten");
+ }
+
+ if (check_label(active, run_from, run_to, "coarse"))
+ {
+ Pass::call(design, "synth -run coarse");
+ }
+
+ if (check_label(active, run_from, run_to, "bram"))
+ {
+ Pass::call(design, "memory_bram -rules +/xilinx/brams.txt");
+ Pass::call(design, "techmap -map +/xilinx/brams_map.v");
+ }
+
+ if (check_label(active, run_from, run_to, "dram"))
+ {
+ Pass::call(design, "memory_bram -rules +/xilinx/drams.txt");
+ Pass::call(design, "techmap -map +/xilinx/drams_map.v");
+ }
+
+ if (check_label(active, run_from, run_to, "fine"))
+ {
+ Pass::call(design, "opt -fast -full");
+ Pass::call(design, "memory_map");
+ Pass::call(design, "dffsr2dff");
+ Pass::call(design, "dff2dffe");
+ Pass::call(design, "opt -full");
+ Pass::call(design, "techmap -map +/techmap.v -map +/xilinx/arith_map.v");
+ Pass::call(design, "opt -fast");
+ }
+
+ if (check_label(active, run_from, run_to, "map_luts"))
+ {
+ Pass::call(design, "abc -luts 2:2,3,6:5,10,20" + string(retime ? " -dff" : ""));
+ Pass::call(design, "clean");
+ }
+
+ if (check_label(active, run_from, run_to, "map_cells"))
+ {
+ Pass::call(design, "techmap -map +/xilinx/cells_map.v");
+ Pass::call(design, "dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT");
+ Pass::call(design, "clean");
+ }
+
+ if (check_label(active, run_from, run_to, "check"))
+ {
+ Pass::call(design, "hierarchy -check");
+ Pass::call(design, "stat");
+ Pass::call(design, "check -noinit");
+ }
+
+ if (check_label(active, run_from, run_to, "edif"))
+ {
+ if (!edif_file.empty())
+ Pass::call(design, stringf("write_edif %s", edif_file.c_str()));
+ }
+
+ log_pop();
+ }
+} SynthXilinxPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/xilinx/tests/.gitignore b/techlibs/xilinx/tests/.gitignore
new file mode 100644
index 00000000..496b8746
--- /dev/null
+++ b/techlibs/xilinx/tests/.gitignore
@@ -0,0 +1,6 @@
+bram1_cmp
+bram1.mk
+bram1_[0-9]*/
+bram2.log
+bram2_syn.v
+bram2_tb
diff --git a/techlibs/xilinx/tests/bram1.sh b/techlibs/xilinx/tests/bram1.sh
new file mode 100644
index 00000000..7451a1b3
--- /dev/null
+++ b/techlibs/xilinx/tests/bram1.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+set -e
+
+transp_list="0 1"
+abits_list="1 2 4 8 10 16 20"
+dbits_list="1 2 4 8 10 16 20 24 30 32 40 48 50 56 60 64 70 72 80"
+
+use_xsim=false
+unisims=/opt/Xilinx/Vivado/2014.4/data/verilog/src/unisims
+
+echo "all: all_list" > bram1.mk
+all_list=""
+
+for transp in $transp_list; do
+for abits in $abits_list; do
+for dbits in $dbits_list; do
+ if [ $(( (1 << $abits) * $dbits )) -gt 1000000 ]; then continue; fi
+ id=`printf "%d%02d%02d" $transp $abits $dbits`
+ echo "Creating bram1_$id.."
+ rm -rf bram1_$id
+ mkdir -p bram1_$id
+ cp bram1.v bram1_tb.v bram1_$id/
+ sed -i "/parameter/ s,ABITS *= *[0-9]*,ABITS = $abits," bram1_$id/*.v
+ sed -i "/parameter/ s,DBITS *= *[0-9]*,DBITS = $dbits," bram1_$id/*.v
+ sed -i "/parameter/ s,TRANSP *= *[0-9]*,TRANSP = $transp," bram1_$id/*.v
+ {
+ echo "set -e"
+ echo "../../../../yosys -q -lsynth.log -p 'synth_xilinx -top bram1; write_verilog synth.v' bram1.v"
+ if $use_xsim; then
+ echo "xvlog --work gold bram1_tb.v bram1.v > gold.txt"
+ echo "xvlog --work gate bram1_tb.v synth.v > gate.txt"
+ echo "xelab -R gold.bram1_tb >> gold.txt"
+ echo "xelab -L unisim -R gate.bram1_tb >> gate.txt"
+ else
+ echo "iverilog -o bram1_tb_gold bram1_tb.v bram1.v > gold.txt 2>&1"
+ echo "iverilog -o bram1_tb_gate bram1_tb.v synth.v -y $unisims $unisims/../glbl.v > gate.txt 2>&1"
+ echo "./bram1_tb_gold >> gold.txt"
+ echo "./bram1_tb_gate >> gate.txt"
+ fi
+ echo "../bram1_cmp <( grep '#OUT#' gold.txt; ) <( grep '#OUT#' gate.txt; )"
+ } > bram1_$id/run.sh
+ {
+ echo "bram1_$id/ok:"
+ echo " @cd bram1_$id && bash run.sh"
+ echo " @echo -n '[$id]'"
+ echo " @touch \$@"
+ } >> bram1.mk
+ all_list="$all_list bram1_$id/ok"
+done; done; done
+
+cc -o bram1_cmp ../../../tests/tools/cmp_tbdata.c
+echo all_list: $(echo $all_list | tr ' ' '\n' | sort -R) >> bram1.mk
+
+echo "Testing..."
+${MAKE:-make} -f bram1.mk
+echo
+
+echo "Used bram types:"
+grep -h 'Mapping to bram type' bram1_*/synth.log | sort | uniq -c
+
+echo "Cleaning up..."
+rm -rf bram1_cmp bram1.mk bram1_[0-9]*/
+
diff --git a/techlibs/xilinx/tests/bram1.v b/techlibs/xilinx/tests/bram1.v
new file mode 100644
index 00000000..ac7140a0
--- /dev/null
+++ b/techlibs/xilinx/tests/bram1.v
@@ -0,0 +1,41 @@
+module bram1 #(
+ parameter ABITS = 8, DBITS = 8, TRANSP = 0
+) (
+ input clk,
+
+ input [ABITS-1:0] WR_ADDR,
+ input [DBITS-1:0] WR_DATA,
+ input WR_EN,
+
+ input [ABITS-1:0] RD_ADDR,
+ output [DBITS-1:0] RD_DATA
+);
+ localparam [ABITS-1:0] INIT_ADDR_0 = 1234;
+ localparam [ABITS-1:0] INIT_ADDR_1 = 4321;
+ localparam [ABITS-1:0] INIT_ADDR_2 = 2**ABITS-1;
+ localparam [ABITS-1:0] INIT_ADDR_3 = (2**ABITS-1) / 2;
+
+ localparam [DBITS-1:0] INIT_DATA_0 = 128'h 51e152a7300e309ccb8cd06d34558f49;
+ localparam [DBITS-1:0] INIT_DATA_1 = 128'h 07b1fe94a530ddf3027520f9d23ab43e;
+ localparam [DBITS-1:0] INIT_DATA_2 = 128'h 3cedc6de43ef3f607af3193658d0eb0b;
+ localparam [DBITS-1:0] INIT_DATA_3 = 128'h f6bc5514a8abf1e2810df966bcc13b46;
+
+ reg [DBITS-1:0] memory [0:2**ABITS-1];
+ reg [ABITS-1:0] RD_ADDR_BUF;
+ reg [DBITS-1:0] RD_DATA_BUF;
+
+ initial begin
+ memory[INIT_ADDR_0] <= INIT_DATA_0;
+ memory[INIT_ADDR_1] <= INIT_DATA_1;
+ memory[INIT_ADDR_2] <= INIT_DATA_2;
+ memory[INIT_ADDR_3] <= INIT_DATA_3;
+ end
+
+ always @(posedge clk) begin
+ if (WR_EN) memory[WR_ADDR] <= WR_DATA;
+ RD_ADDR_BUF <= RD_ADDR;
+ RD_DATA_BUF <= memory[RD_ADDR];
+ end
+
+ assign RD_DATA = TRANSP ? memory[RD_ADDR_BUF] : RD_DATA_BUF;
+endmodule
diff --git a/techlibs/xilinx/tests/bram1_tb.v b/techlibs/xilinx/tests/bram1_tb.v
new file mode 100644
index 00000000..e75dfe31
--- /dev/null
+++ b/techlibs/xilinx/tests/bram1_tb.v
@@ -0,0 +1,148 @@
+module bram1_tb #(
+ parameter ABITS = 8, DBITS = 8, TRANSP = 0
+);
+ reg clk;
+ reg [ABITS-1:0] WR_ADDR;
+ reg [DBITS-1:0] WR_DATA;
+ reg WR_EN;
+ reg [ABITS-1:0] RD_ADDR;
+ wire [DBITS-1:0] RD_DATA;
+
+ localparam [ABITS-1:0] INIT_ADDR_0 = 1234;
+ localparam [ABITS-1:0] INIT_ADDR_1 = 4321;
+ localparam [ABITS-1:0] INIT_ADDR_2 = 2**ABITS-1;
+ localparam [ABITS-1:0] INIT_ADDR_3 = (2**ABITS-1) / 2;
+
+ localparam [DBITS-1:0] INIT_DATA_0 = 128'h 51e152a7300e309ccb8cd06d34558f49;
+ localparam [DBITS-1:0] INIT_DATA_1 = 128'h 07b1fe94a530ddf3027520f9d23ab43e;
+ localparam [DBITS-1:0] INIT_DATA_2 = 128'h 3cedc6de43ef3f607af3193658d0eb0b;
+ localparam [DBITS-1:0] INIT_DATA_3 = 128'h f6bc5514a8abf1e2810df966bcc13b46;
+
+ bram1 #(
+ // .ABITS(ABITS),
+ // .DBITS(DBITS),
+ // .TRANSP(TRANSP)
+ ) uut (
+ .clk (clk ),
+ .WR_ADDR(WR_ADDR),
+ .WR_DATA(WR_DATA),
+ .WR_EN (WR_EN ),
+ .RD_ADDR(RD_ADDR),
+ .RD_DATA(RD_DATA)
+ );
+
+ reg [63:0] xorshift64_state = 64'd88172645463325252 ^ (ABITS << 24) ^ (DBITS << 16) ^ (TRANSP << 8);
+
+ task xorshift64_next;
+ begin
+ // see page 4 of Marsaglia, George (July 2003). "Xorshift RNGs". Journal of Statistical Software 8 (14).
+ xorshift64_state = xorshift64_state ^ (xorshift64_state << 13);
+ xorshift64_state = xorshift64_state ^ (xorshift64_state >> 7);
+ xorshift64_state = xorshift64_state ^ (xorshift64_state << 17);
+ end
+ endtask
+
+ reg [ABITS-1:0] randaddr1;
+ reg [ABITS-1:0] randaddr2;
+ reg [ABITS-1:0] randaddr3;
+
+ function [31:0] getaddr(input [3:0] n);
+ begin
+ case (n)
+ 0: getaddr = 0;
+ 1: getaddr = 2**ABITS-1;
+ 2: getaddr = 'b101 << (ABITS / 3);
+ 3: getaddr = 'b101 << (2*ABITS / 3);
+ 4: getaddr = 'b11011 << (ABITS / 4);
+ 5: getaddr = 'b11011 << (2*ABITS / 4);
+ 6: getaddr = 'b11011 << (3*ABITS / 4);
+ 7: getaddr = randaddr1;
+ 8: getaddr = randaddr2;
+ 9: getaddr = randaddr3;
+ default: begin
+ getaddr = 1 << (2*n-16);
+ if (!getaddr) getaddr = xorshift64_state;
+ end
+ endcase
+ end
+ endfunction
+
+ reg [DBITS-1:0] memory [0:2**ABITS-1];
+ reg [DBITS-1:0] expected_rd, expected_rd_masked;
+
+ event error;
+ reg error_ind = 0;
+
+ integer i, j;
+ initial begin
+ // $dumpfile("testbench.vcd");
+ // $dumpvars(0, bram1_tb);
+
+ memory[INIT_ADDR_0] = INIT_DATA_0;
+ memory[INIT_ADDR_1] = INIT_DATA_1;
+ memory[INIT_ADDR_2] = INIT_DATA_2;
+ memory[INIT_ADDR_3] = INIT_DATA_3;
+
+ xorshift64_next;
+ xorshift64_next;
+ xorshift64_next;
+ xorshift64_next;
+
+ randaddr1 = xorshift64_state;
+ xorshift64_next;
+
+ randaddr2 = xorshift64_state;
+ xorshift64_next;
+
+ randaddr3 = xorshift64_state;
+ xorshift64_next;
+
+ clk <= 0;
+ for (i = 0; i < 512; i = i+1) begin
+ if (i == 0) begin
+ WR_EN <= 0;
+ RD_ADDR <= INIT_ADDR_0;
+ end else
+ if (i == 1) begin
+ WR_EN <= 0;
+ RD_ADDR <= INIT_ADDR_1;
+ end else
+ if (i == 2) begin
+ WR_EN <= 0;
+ RD_ADDR <= INIT_ADDR_2;
+ end else
+ if (i == 3) begin
+ WR_EN <= 0;
+ RD_ADDR <= INIT_ADDR_3;
+ end else begin
+ if (DBITS > 64)
+ WR_DATA <= (xorshift64_state << (DBITS-64)) ^ xorshift64_state;
+ else
+ WR_DATA <= xorshift64_state;
+ xorshift64_next;
+ WR_ADDR <= getaddr(i < 256 ? i[7:4] : xorshift64_state[63:60]);
+ xorshift64_next;
+ RD_ADDR <= getaddr(i < 256 ? i[3:0] : xorshift64_state[59:56]);
+ WR_EN <= xorshift64_state[55];
+ xorshift64_next;
+ end
+
+ #1; clk <= 1;
+ #1; clk <= 0;
+
+ if (TRANSP) begin
+ if (WR_EN) memory[WR_ADDR] = WR_DATA;
+ expected_rd = memory[RD_ADDR];
+ end else begin
+ expected_rd = memory[RD_ADDR];
+ if (WR_EN) memory[WR_ADDR] = WR_DATA;
+ end
+
+ for (j = 0; j < DBITS; j = j+1)
+ expected_rd_masked[j] = expected_rd[j] !== 1'bx ? expected_rd[j] : RD_DATA[j];
+
+ $display("#OUT# %3d | WA=%x WD=%x WE=%x | RA=%x RD=%x (%x) | %s", i, WR_ADDR, WR_DATA, WR_EN, RD_ADDR, RD_DATA, expected_rd, expected_rd_masked === RD_DATA ? "ok" : "ERROR");
+ if (expected_rd_masked !== RD_DATA) begin -> error; error_ind = ~error_ind; end
+ end
+ end
+endmodule
diff --git a/techlibs/xilinx/tests/bram2.sh b/techlibs/xilinx/tests/bram2.sh
new file mode 100644
index 00000000..5d9c84da
--- /dev/null
+++ b/techlibs/xilinx/tests/bram2.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+set -ex
+unisims=/opt/Xilinx/Vivado/2014.4/data/verilog/src/unisims
+../../../yosys -v2 -l bram2.log -p synth_xilinx -o bram2_syn.v bram2.v
+iverilog -T typ -o bram2_tb bram2_tb.v bram2_syn.v -y $unisims $unisims/../glbl.v
+vvp -N bram2_tb
+
diff --git a/techlibs/xilinx/tests/bram2.v b/techlibs/xilinx/tests/bram2.v
new file mode 100644
index 00000000..0a6013ca
--- /dev/null
+++ b/techlibs/xilinx/tests/bram2.v
@@ -0,0 +1,35 @@
+module myram(
+ input rd_clk,
+ input [ 7:0] rd_addr,
+ output reg [17:0] rd_data,
+ input wr_clk,
+ input wr_enable,
+ input [ 7:0] wr_addr,
+ input [17:0] wr_data
+);
+ reg [17:0] memory [0:255];
+ integer i;
+
+ function [17:0] hash(input [7:0] k);
+ reg [31:0] x;
+ begin
+ x = {k, ~k, k, ~k};
+ x = x ^ (x << 13);
+ x = x ^ (x >> 17);
+ x = x ^ (x << 5);
+ hash = x;
+ end
+ endfunction
+
+ initial begin
+ for (i = 0; i < 256; i = i+1)
+ memory[i] = hash(i);
+ end
+
+ always @(posedge rd_clk)
+ rd_data <= memory[rd_addr];
+
+ always @(posedge wr_clk)
+ if (wr_enable)
+ memory[wr_addr] <= wr_data;
+endmodule
diff --git a/techlibs/xilinx/tests/bram2_tb.v b/techlibs/xilinx/tests/bram2_tb.v
new file mode 100644
index 00000000..0fe4137c
--- /dev/null
+++ b/techlibs/xilinx/tests/bram2_tb.v
@@ -0,0 +1,56 @@
+`timescale 1 ns / 1 ps
+
+module testbench;
+ reg rd_clk;
+ reg [ 7:0] rd_addr;
+ wire [17:0] rd_data;
+
+ wire wr_clk = 0;
+ wire wr_enable = 0;
+ wire [ 7:0] wr_addr = 0;
+ wire [17:0] wr_data = 0;
+
+ function [17:0] hash(input [7:0] k);
+ reg [31:0] x;
+ begin
+ x = {k, ~k, k, ~k};
+ x = x ^ (x << 13);
+ x = x ^ (x >> 17);
+ x = x ^ (x << 5);
+ hash = x;
+ end
+ endfunction
+
+ myram uut (
+ .rd_clk (rd_clk ),
+ .rd_addr (rd_addr ),
+ .rd_data (rd_data ),
+ .wr_clk (wr_clk ),
+ .wr_enable(wr_enable),
+ .wr_addr (wr_addr ),
+ .wr_data (wr_data )
+ );
+
+ initial begin
+ rd_clk = 0;
+ #1000;
+ forever #10 rd_clk <= ~rd_clk;
+ end
+
+ integer i;
+ initial begin
+ rd_addr <= 0;
+ @(posedge rd_clk);
+ for (i = 0; i < 256; i=i+1) begin
+ rd_addr <= rd_addr + 1;
+ @(posedge rd_clk);
+ // $display("%3d %3d", i, rd_data);
+ if (hash(i) !== rd_data) begin
+ $display("[%1t] ERROR: addr=%3d, data_mem=%18b, data_ref=%18b", $time, i, rd_data, hash(i));
+ $stop;
+ end
+ end
+ $display("[%1t] Passed bram2 test.", $time);
+ $finish;
+ end
+endmodule
diff --git a/tests/asicworld/.gitignore b/tests/asicworld/.gitignore
new file mode 100644
index 00000000..073f4615
--- /dev/null
+++ b/tests/asicworld/.gitignore
@@ -0,0 +1,2 @@
+*.log
+*.out
diff --git a/tests/asicworld/README b/tests/asicworld/README
new file mode 100644
index 00000000..4657e7a2
--- /dev/null
+++ b/tests/asicworld/README
@@ -0,0 +1 @@
+Borrowed Verilog examples from http://www.asic-world.com/.
diff --git a/tests/asicworld/code_hdl_models_GrayCounter.v b/tests/asicworld/code_hdl_models_GrayCounter.v
new file mode 100644
index 00000000..23f0da04
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_GrayCounter.v
@@ -0,0 +1,33 @@
+//==========================================
+// Function : Code Gray counter.
+// Coder : Alex Claros F.
+// Date : 15/May/2005.
+//=======================================
+
+module GrayCounter
+ #(parameter COUNTER_WIDTH = 4)
+
+ (output reg [COUNTER_WIDTH-1:0] GrayCount_out, //'Gray' code count output.
+
+ input wire Enable_in, //Count enable.
+ input wire Clear_in, //Count reset.
+
+ input wire Clk);
+
+ /////////Internal connections & variables///////
+ reg [COUNTER_WIDTH-1:0] BinaryCount;
+
+ /////////Code///////////////////////
+
+ always @ (posedge Clk)
+ if (Clear_in) begin
+ BinaryCount <= {COUNTER_WIDTH{1'b 0}} + 1; //Gray count begins @ '1' with
+ GrayCount_out <= {COUNTER_WIDTH{1'b 0}}; // first 'Enable_in'.
+ end
+ else if (Enable_in) begin
+ BinaryCount <= BinaryCount + 1;
+ GrayCount_out <= {BinaryCount[COUNTER_WIDTH-1],
+ BinaryCount[COUNTER_WIDTH-2:0] ^ BinaryCount[COUNTER_WIDTH-1:1]};
+ end
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_arbiter.v b/tests/asicworld/code_hdl_models_arbiter.v
new file mode 100644
index 00000000..d3e3a66f
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_arbiter.v
@@ -0,0 +1,123 @@
+//----------------------------------------------------
+// A four level, round-robin arbiter. This was
+// orginally coded by WD Peterson in VHDL.
+//----------------------------------------------------
+module arbiter (
+ clk,
+ rst,
+ req3,
+ req2,
+ req1,
+ req0,
+ gnt3,
+ gnt2,
+ gnt1,
+ gnt0
+);
+// --------------Port Declaration-----------------------
+input clk;
+input rst;
+input req3;
+input req2;
+input req1;
+input req0;
+output gnt3;
+output gnt2;
+output gnt1;
+output gnt0;
+
+//--------------Internal Registers----------------------
+wire [1:0] gnt ;
+wire comreq ;
+wire beg ;
+wire [1:0] lgnt ;
+wire lcomreq ;
+reg lgnt0 ;
+reg lgnt1 ;
+reg lgnt2 ;
+reg lgnt3 ;
+reg lasmask ;
+reg lmask0 ;
+reg lmask1 ;
+reg ledge ;
+
+//--------------Code Starts Here-----------------------
+always @ (posedge clk)
+if (rst) begin
+ lgnt0 <= 0;
+ lgnt1 <= 0;
+ lgnt2 <= 0;
+ lgnt3 <= 0;
+end else begin
+ lgnt0 <=(~lcomreq & ~lmask1 & ~lmask0 & ~req3 & ~req2 & ~req1 & req0)
+ | (~lcomreq & ~lmask1 & lmask0 & ~req3 & ~req2 & req0)
+ | (~lcomreq & lmask1 & ~lmask0 & ~req3 & req0)
+ | (~lcomreq & lmask1 & lmask0 & req0 )
+ | ( lcomreq & lgnt0 );
+ lgnt1 <=(~lcomreq & ~lmask1 & ~lmask0 & req1)
+ | (~lcomreq & ~lmask1 & lmask0 & ~req3 & ~req2 & req1 & ~req0)
+ | (~lcomreq & lmask1 & ~lmask0 & ~req3 & req1 & ~req0)
+ | (~lcomreq & lmask1 & lmask0 & req1 & ~req0)
+ | ( lcomreq & lgnt1);
+ lgnt2 <=(~lcomreq & ~lmask1 & ~lmask0 & req2 & ~req1)
+ | (~lcomreq & ~lmask1 & lmask0 & req2)
+ | (~lcomreq & lmask1 & ~lmask0 & ~req3 & req2 & ~req1 & ~req0)
+ | (~lcomreq & lmask1 & lmask0 & req2 & ~req1 & ~req0)
+ | ( lcomreq & lgnt2);
+ lgnt3 <=(~lcomreq & ~lmask1 & ~lmask0 & req3 & ~req2 & ~req1)
+ | (~lcomreq & ~lmask1 & lmask0 & req3 & ~req2)
+ | (~lcomreq & lmask1 & ~lmask0 & req3)
+ | (~lcomreq & lmask1 & lmask0 & req3 & ~req2 & ~req1 & ~req0)
+ | ( lcomreq & lgnt3);
+end
+
+//----------------------------------------------------
+// lasmask state machine.
+//----------------------------------------------------
+assign beg = (req3 | req2 | req1 | req0) & ~lcomreq;
+always @ (posedge clk)
+begin
+ lasmask <= (beg & ~ledge & ~lasmask);
+ ledge <= (beg & ~ledge & lasmask)
+ | (beg & ledge & ~lasmask);
+end
+
+//----------------------------------------------------
+// comreq logic.
+//----------------------------------------------------
+assign lcomreq = ( req3 & lgnt3 )
+ | ( req2 & lgnt2 )
+ | ( req1 & lgnt1 )
+ | ( req0 & lgnt0 );
+
+//----------------------------------------------------
+// Encoder logic.
+//----------------------------------------------------
+assign lgnt = {(lgnt3 | lgnt2),(lgnt3 | lgnt1)};
+
+//----------------------------------------------------
+// lmask register.
+//----------------------------------------------------
+always @ (posedge clk )
+if( rst ) begin
+ lmask1 <= 0;
+ lmask0 <= 0;
+end else if(lasmask) begin
+ lmask1 <= lgnt[1];
+ lmask0 <= lgnt[0];
+end else begin
+ lmask1 <= lmask1;
+ lmask0 <= lmask0;
+end
+
+assign comreq = lcomreq;
+assign gnt = lgnt;
+//----------------------------------------------------
+// Drive the outputs
+//----------------------------------------------------
+assign gnt3 = lgnt3;
+assign gnt2 = lgnt2;
+assign gnt1 = lgnt1;
+assign gnt0 = lgnt0;
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_arbiter_tb.v b/tests/asicworld/code_hdl_models_arbiter_tb.v
new file mode 100644
index 00000000..78d1168e
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_arbiter_tb.v
@@ -0,0 +1,60 @@
+module testbench ();
+
+reg clk = 0;
+reg rst = 1;
+reg req3 = 0;
+reg req2 = 0;
+reg req1 = 0;
+reg req0 = 0;
+wire gnt3;
+wire gnt2;
+wire gnt1;
+wire gnt0;
+
+// Clock generator
+always #1 clk = ~clk;
+integer file;
+
+always @(posedge clk)
+ $fdisplay(file, "%b", {gnt3, gnt2, gnt1, gnt0});
+
+initial begin
+ file = $fopen(`outfile);
+ repeat (5) @ (posedge clk);
+ rst <= 0;
+ repeat (1) @ (posedge clk);
+ req0 <= 1;
+ repeat (1) @ (posedge clk);
+ req0 <= 0;
+ repeat (1) @ (posedge clk);
+ req0 <= 1;
+ req1 <= 1;
+ repeat (1) @ (posedge clk);
+ req2 <= 1;
+ req1 <= 0;
+ repeat (1) @ (posedge clk);
+ req3 <= 1;
+ req2 <= 0;
+ repeat (1) @ (posedge clk);
+ req3 <= 0;
+ repeat (1) @ (posedge clk);
+ req0 <= 0;
+ repeat (1) @ (posedge clk);
+ #10 $finish;
+end
+
+// Connect the DUT
+arbiter U (
+ clk,
+ rst,
+ req3,
+ req2,
+ req1,
+ req0,
+ gnt3,
+ gnt2,
+ gnt1,
+ gnt0
+);
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_cam.v b/tests/asicworld/code_hdl_models_cam.v
new file mode 100644
index 00000000..0cebc07c
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_cam.v
@@ -0,0 +1,60 @@
+//-----------------------------------------------------
+// Design Name : cam
+// File Name : cam.v
+// Function : CAM
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module cam (
+clk , // Cam clock
+cam_enable , // Cam enable
+cam_data_in , // Cam data to match
+cam_hit_out , // Cam match has happened
+cam_addr_out // Cam output address
+);
+
+parameter ADDR_WIDTH = 8;
+parameter DEPTH = 1 << ADDR_WIDTH;
+//------------Input Ports--------------
+input clk;
+input cam_enable;
+input [DEPTH-1:0] cam_data_in;
+//----------Output Ports--------------
+output cam_hit_out;
+output [ADDR_WIDTH-1:0] cam_addr_out;
+//------------Internal Variables--------
+reg [ADDR_WIDTH-1:0] cam_addr_out;
+reg cam_hit_out;
+reg [ADDR_WIDTH-1:0] cam_addr_combo;
+reg cam_hit_combo;
+reg found_match;
+integer i;
+//-------------Code Starts Here-------
+always @(cam_data_in) begin
+ cam_addr_combo = {ADDR_WIDTH{1'b0}};
+ found_match = 1'b0;
+ cam_hit_combo = 1'b0;
+ for (i=0; i<DEPTH; i=i+1) begin
+ if (cam_data_in[i] && !found_match) begin
+ found_match = 1'b1;
+ cam_hit_combo = 1'b1;
+ cam_addr_combo = i;
+ end else begin
+ found_match = found_match;
+ cam_hit_combo = cam_hit_combo;
+ cam_addr_combo = cam_addr_combo;
+ end
+ end
+end
+
+// Register the outputs
+always @(posedge clk) begin
+ if (cam_enable) begin
+ cam_hit_out <= cam_hit_combo;
+ cam_addr_out <= cam_addr_combo;
+ end else begin
+ cam_hit_out <= 1'b0;
+ cam_addr_out <= {ADDR_WIDTH{1'b0}};
+ end
+end
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_clk_div.v b/tests/asicworld/code_hdl_models_clk_div.v
new file mode 100644
index 00000000..c48ab0dd
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_clk_div.v
@@ -0,0 +1,27 @@
+//-----------------------------------------------------
+// Design Name : clk_div
+// File Name : clk_div.v
+// Function : Divide by two counter
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+
+module clk_div (clk_in, enable,reset, clk_out);
+ // --------------Port Declaration-----------------------
+ input clk_in ;
+ input reset ;
+ input enable ;
+ output clk_out ;
+ //--------------Port data type declaration-------------
+ wire clk_in ;
+ wire enable ;
+//--------------Internal Registers----------------------
+reg clk_out ;
+//--------------Code Starts Here-----------------------
+always @ (posedge clk_in)
+if (reset) begin
+ clk_out <= 1'b0;
+end else if (enable) begin
+ clk_out <= !clk_out ;
+end
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_clk_div_45.v b/tests/asicworld/code_hdl_models_clk_div_45.v
new file mode 100644
index 00000000..d9d28967
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_clk_div_45.v
@@ -0,0 +1,54 @@
+//-----------------------------------------------------
+// Design Name : clk_div_45
+// File Name : clk_div_45.v
+// Function : Divide by 4.5
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module clk_div_45 (
+clk_in, // Input Clock
+enable, // Enable is sync with falling edge of clk_in
+clk_out // Output Clock
+);
+
+// --------------Port Declaration-----------------------
+input clk_in ;
+input enable ;
+output clk_out ;
+
+//--------------Port data type declaration-------------
+wire clk_in ;
+wire enable ;
+wire clk_out ;
+
+//--------------Internal Registers----------------------
+reg [3:0] counter1 ;
+reg [3:0] counter2 ;
+reg toggle1 ;
+reg toggle2 ;
+
+//--------------Code Starts Here-----------------------
+always @ (posedge clk_in)
+if (enable == 1'b0) begin
+ counter1 <= 4'b0;
+ toggle1 <= 0;
+end else if ((counter1 == 3 && toggle2) || (~toggle1 && counter1 == 4)) begin
+ counter1 <= 4'b0;
+ toggle1 <= ~toggle1;
+end else begin
+ counter1 <= counter1 + 1;
+end
+
+always @ (negedge clk_in)
+if (enable == 1'b0) begin
+ counter2 <= 4'b0;
+ toggle2 <= 0;
+end else if ((counter2 == 3 && ~toggle2) || (toggle2 && counter2 == 4)) begin
+ counter2 <= 4'b0;
+ toggle2 <= ~toggle2;
+end else begin
+ counter2 <= counter2 + 1;
+end
+
+assign clk_out = (counter1 <3 && counter2 < 3) & enable;
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_d_ff_gates.v b/tests/asicworld/code_hdl_models_d_ff_gates.v
new file mode 100644
index 00000000..8706f154
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_d_ff_gates.v
@@ -0,0 +1,29 @@
+module d_ff_gates(d,clk,q,q_bar);
+input d,clk;
+output q, q_bar;
+
+wire n1,n2,n3,q_bar_n;
+wire cn,dn,n4,n5,n6;
+
+// First Latch
+not (n1,d);
+
+nand (n2,d,clk);
+nand (n3,n1,clk);
+
+nand (dn,q_bar_n,n2);
+nand (q_bar_n,dn,n3);
+
+// Second Latch
+not (cn,clk);
+
+not (n4,dn);
+
+nand (n5,dn,cn);
+nand (n6,n4,cn);
+
+nand (q,q_bar,n5);
+nand (q_bar,q,n6);
+
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_d_latch_gates.v b/tests/asicworld/code_hdl_models_d_latch_gates.v
new file mode 100644
index 00000000..3f5f6b2b
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_d_latch_gates.v
@@ -0,0 +1,15 @@
+module d_latch_gates(d,clk,q,q_bar);
+input d,clk;
+output q, q_bar;
+
+wire n1,n2,n3;
+
+not (n1,d);
+
+nand (n2,d,clk);
+nand (n3,n1,clk);
+
+nand (q,q_bar,n2);
+nand (q_bar,q,n3);
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_decoder_2to4_gates.v b/tests/asicworld/code_hdl_models_decoder_2to4_gates.v
new file mode 100644
index 00000000..810003a8
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_decoder_2to4_gates.v
@@ -0,0 +1,14 @@
+module decoder_2to4_gates (x,y,f0,f1,f2,f3);
+input x,y;
+output f0,f1,f2,f3;
+
+wire n1,n2;
+
+not i1 (n1,x);
+not i2 (n2,y);
+and a1 (f0,n1,n2);
+and a2 (f1,n1,y);
+and a3 (f2,x,n2);
+and a4 (f3,x,y);
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_decoder_using_assign.v b/tests/asicworld/code_hdl_models_decoder_using_assign.v
new file mode 100644
index 00000000..ec0dc95b
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_decoder_using_assign.v
@@ -0,0 +1,20 @@
+//-----------------------------------------------------
+// Design Name : decoder_using_assign
+// File Name : decoder_using_assign.v
+// Function : decoder using assign
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module decoder_using_assign (
+binary_in , // 4 bit binary input
+decoder_out , // 16-bit out
+enable // Enable for the decoder
+);
+input [3:0] binary_in ;
+input enable ;
+output [15:0] decoder_out ;
+
+wire [15:0] decoder_out ;
+
+assign decoder_out = (enable) ? (1 << binary_in) : 16'b0 ;
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_decoder_using_case.v b/tests/asicworld/code_hdl_models_decoder_using_case.v
new file mode 100644
index 00000000..ad42acdf
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_decoder_using_case.v
@@ -0,0 +1,43 @@
+//-----------------------------------------------------
+// Design Name : decoder_using_case
+// File Name : decoder_using_case.v
+// Function : decoder using case
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module decoder_using_case (
+binary_in , // 4 bit binary input
+decoder_out , // 16-bit out
+enable // Enable for the decoder
+);
+input [3:0] binary_in ;
+input enable ;
+output [15:0] decoder_out ;
+
+reg [15:0] decoder_out ;
+
+always @ (enable or binary_in)
+begin
+ decoder_out = 0;
+ if (enable) begin
+ case (binary_in)
+ 4'h0 : decoder_out = 16'h0001;
+ 4'h1 : decoder_out = 16'h0002;
+ 4'h2 : decoder_out = 16'h0004;
+ 4'h3 : decoder_out = 16'h0008;
+ 4'h4 : decoder_out = 16'h0010;
+ 4'h5 : decoder_out = 16'h0020;
+ 4'h6 : decoder_out = 16'h0040;
+ 4'h7 : decoder_out = 16'h0080;
+ 4'h8 : decoder_out = 16'h0100;
+ 4'h9 : decoder_out = 16'h0200;
+ 4'hA : decoder_out = 16'h0400;
+ 4'hB : decoder_out = 16'h0800;
+ 4'hC : decoder_out = 16'h1000;
+ 4'hD : decoder_out = 16'h2000;
+ 4'hE : decoder_out = 16'h4000;
+ 4'hF : decoder_out = 16'h8000;
+ endcase
+ end
+end
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_dff_async_reset.v b/tests/asicworld/code_hdl_models_dff_async_reset.v
new file mode 100644
index 00000000..a156082f
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_dff_async_reset.v
@@ -0,0 +1,30 @@
+//-----------------------------------------------------
+// Design Name : dff_async_reset
+// File Name : dff_async_reset.v
+// Function : D flip-flop async reset
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module dff_async_reset (
+data , // Data Input
+clk , // Clock Input
+reset , // Reset input
+q // Q output
+);
+//-----------Input Ports---------------
+input data, clk, reset ;
+
+//-----------Output Ports---------------
+output q;
+
+//------------Internal Variables--------
+reg q;
+
+//-------------Code Starts Here---------
+always @ ( posedge clk or negedge reset)
+if (~reset) begin
+ q <= 1'b0;
+end else begin
+ q <= data;
+end
+
+endmodule //End Of Module dff_async_reset
diff --git a/tests/asicworld/code_hdl_models_dff_sync_reset.v b/tests/asicworld/code_hdl_models_dff_sync_reset.v
new file mode 100644
index 00000000..7ef40454
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_dff_sync_reset.v
@@ -0,0 +1,30 @@
+//-----------------------------------------------------
+// Design Name : dff_sync_reset
+// File Name : dff_sync_reset.v
+// Function : D flip-flop sync reset
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module dff_sync_reset (
+data , // Data Input
+clk , // Clock Input
+reset , // Reset input
+q // Q output
+);
+//-----------Input Ports---------------
+input data, clk, reset ;
+
+//-----------Output Ports---------------
+output q;
+
+//------------Internal Variables--------
+reg q;
+
+//-------------Code Starts Here---------
+always @ ( posedge clk)
+if (~reset) begin
+ q <= 1'b0;
+end else begin
+ q <= data;
+end
+
+endmodule //End Of Module dff_sync_reset
diff --git a/tests/asicworld/code_hdl_models_encoder_4to2_gates.v b/tests/asicworld/code_hdl_models_encoder_4to2_gates.v
new file mode 100644
index 00000000..0bfdc28a
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_encoder_4to2_gates.v
@@ -0,0 +1,8 @@
+module encoder_4to2_gates (i0,i1,i2,i3,y);
+input i0,i1,i2,i3;
+output [1:0] y;
+
+or o1 (y[0],i1,i3);
+or o2 (y[1],i2,i3);
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_encoder_using_case.v b/tests/asicworld/code_hdl_models_encoder_using_case.v
new file mode 100644
index 00000000..32e1b720
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_encoder_using_case.v
@@ -0,0 +1,42 @@
+//-----------------------------------------------------
+// Design Name : encoder_using_case
+// File Name : encoder_using_case.v
+// Function : Encoder using Case
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module encoder_using_case(
+binary_out , // 4 bit binary Output
+encoder_in , // 16-bit Input
+enable // Enable for the encoder
+);
+output [3:0] binary_out ;
+input enable ;
+input [15:0] encoder_in ;
+
+reg [3:0] binary_out ;
+
+always @ (enable or encoder_in)
+begin
+ binary_out = 0;
+ if (enable) begin
+ case (encoder_in)
+ 16'h0002 : binary_out = 1;
+ 16'h0004 : binary_out = 2;
+ 16'h0008 : binary_out = 3;
+ 16'h0010 : binary_out = 4;
+ 16'h0020 : binary_out = 5;
+ 16'h0040 : binary_out = 6;
+ 16'h0080 : binary_out = 7;
+ 16'h0100 : binary_out = 8;
+ 16'h0200 : binary_out = 9;
+ 16'h0400 : binary_out = 10;
+ 16'h0800 : binary_out = 11;
+ 16'h1000 : binary_out = 12;
+ 16'h2000 : binary_out = 13;
+ 16'h4000 : binary_out = 14;
+ 16'h8000 : binary_out = 15;
+ endcase
+ end
+end
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_encoder_using_if.v b/tests/asicworld/code_hdl_models_encoder_using_if.v
new file mode 100644
index 00000000..2c97ddba
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_encoder_using_if.v
@@ -0,0 +1,58 @@
+//-----------------------------------------------------
+// Design Name : encoder_using_if
+// File Name : encoder_using_if.v
+// Function : Encoder using If
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module encoder_using_if(
+binary_out , // 4 bit binary output
+encoder_in , // 16-bit input
+enable // Enable for the encoder
+);
+//-----------Output Ports---------------
+output [3:0] binary_out ;
+//-----------Input Ports---------------
+input enable ;
+input [15:0] encoder_in ;
+//------------Internal Variables--------
+reg [3:0] binary_out ;
+//-------------Code Start-----------------
+always @ (enable or encoder_in)
+ begin
+ binary_out = 0;
+ if (enable) begin
+ if (encoder_in == 16'h0002) begin
+ binary_out = 1;
+ end if (encoder_in == 16'h0004) begin
+ binary_out = 2;
+ end if (encoder_in == 16'h0008) begin
+ binary_out = 3;
+ end if (encoder_in == 16'h0010) begin
+ binary_out = 4;
+ end if (encoder_in == 16'h0020) begin
+ binary_out = 5;
+ end if (encoder_in == 16'h0040) begin
+ binary_out = 6;
+ end if (encoder_in == 16'h0080) begin
+ binary_out = 7;
+ end if (encoder_in == 16'h0100) begin
+ binary_out = 8;
+ end if (encoder_in == 16'h0200) begin
+ binary_out = 9;
+ end if (encoder_in == 16'h0400) begin
+ binary_out = 10;
+ end if (encoder_in == 16'h0800) begin
+ binary_out = 11;
+ end if (encoder_in == 16'h1000) begin
+ binary_out = 12;
+ end if (encoder_in == 16'h2000) begin
+ binary_out = 13;
+ end if (encoder_in == 16'h4000) begin
+ binary_out = 14;
+ end if (encoder_in == 16'h8000) begin
+ binary_out = 15;
+ end
+ end
+end
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_full_adder_gates.v b/tests/asicworld/code_hdl_models_full_adder_gates.v
new file mode 100644
index 00000000..1ddc4c56
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_full_adder_gates.v
@@ -0,0 +1,18 @@
+//-----------------------------------------------------
+// Design Name : full_adder_gates
+// File Name : full_adder_gates.v
+// Function : Full Adder Using Gates
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module full_adder_gates(x,y,z,sum,carry);
+input x,y,z;
+output sum,carry;
+wire and1,and2,and3,sum1;
+
+and U_and1 (and1,x,y),
+ U_and2 (and2,x,z),
+ U_and3 (and3,y,z);
+or U_or (carry,and1,and2,and3);
+xor U_sum (sum,x,y,z);
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_full_subtracter_gates.v b/tests/asicworld/code_hdl_models_full_subtracter_gates.v
new file mode 100644
index 00000000..c24588ec
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_full_subtracter_gates.v
@@ -0,0 +1,20 @@
+//-----------------------------------------------------
+// Design Name : full_subtracter_gates
+// File Name : full_subtracter_gates.v
+// Function : Full Subtracter Using Gates
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module full_subtracter_gates(x,y,z,difference,borrow);
+input x,y,z;
+output difference,borrow;
+
+wire inv_x,borrow1,borrow2,borrow3;
+
+not (inv_x,x);
+and U_borrow1 (borrow1,inv_x,y),
+ U_borrow2 (borrow2,inv_x,z),
+ U_borrow3 (borrow3,y,z);
+
+xor U_diff (difference,borrow1,borrow2,borrows);
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_gray_counter.v b/tests/asicworld/code_hdl_models_gray_counter.v
new file mode 100644
index 00000000..bc1e740a
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_gray_counter.v
@@ -0,0 +1,33 @@
+//-----------------------------------------------------
+// Design Name : gray_counter
+// File Name : gray_counter.v
+// Function : 8 bit gray counterS
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module gray_counter (
+ out , // counter out
+ enable , // enable for counter
+ clk , // clock
+ rst // active hight reset
+ );
+
+ //------------Input Ports--------------
+ input clk, rst, enable;
+ //----------Output Ports----------------
+ output [ 7:0] out;
+ //------------Internal Variables--------
+ wire [7:0] out;
+ reg [7:0] count;
+ //-------------Code Starts Here---------
+ always @ (posedge clk)
+ if (rst)
+ count <= 0;
+ else if (enable)
+ count <= count + 1;
+
+ assign out = { count[7], (count[7] ^ count[6]),(count[6] ^
+ count[5]),(count[5] ^ count[4]), (count[4] ^
+ count[3]),(count[3] ^ count[2]), (count[2] ^
+ count[1]),(count[1] ^ count[0]) };
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_half_adder_gates.v b/tests/asicworld/code_hdl_models_half_adder_gates.v
new file mode 100644
index 00000000..6acf243f
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_half_adder_gates.v
@@ -0,0 +1,14 @@
+//-----------------------------------------------------
+// Design Name : half_adder_gates
+// File Name : half_adder_gates.v
+// Function : CCITT Serial CRC
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module half_adder_gates(x,y,sum,carry);
+input x,y;
+output sum,carry;
+
+and U_carry (carry,x,y);
+xor U_sum (sum,x,y);
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_lfsr.v b/tests/asicworld/code_hdl_models_lfsr.v
new file mode 100644
index 00000000..63978083
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_lfsr.v
@@ -0,0 +1,35 @@
+//-----------------------------------------------------
+// Design Name : lfsr
+// File Name : lfsr.v
+// Function : Linear feedback shift register
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module lfsr (
+out , // Output of the counter
+enable , // Enable for counter
+clk , // clock input
+reset // reset input
+);
+
+//----------Output Ports--------------
+output [7:0] out;
+//------------Input Ports--------------
+input enable, clk, reset;
+//------------Internal Variables--------
+reg [7:0] out;
+wire linear_feedback;
+
+//-------------Code Starts Here-------
+assign linear_feedback = !(out[7] ^ out[3]);
+
+always @(posedge clk)
+if (reset) begin // active high reset
+ out <= 8'b0 ;
+end else if (enable) begin
+ out <= {out[6],out[5],
+ out[4],out[3],
+ out[2],out[1],
+ out[0], linear_feedback};
+end
+
+endmodule // End Of Module counter
diff --git a/tests/asicworld/code_hdl_models_lfsr_updown.v b/tests/asicworld/code_hdl_models_lfsr_updown.v
new file mode 100644
index 00000000..0bd29b83
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_lfsr_updown.v
@@ -0,0 +1,35 @@
+`define WIDTH 8
+module lfsr_updown (
+clk , // Clock input
+reset , // Reset input
+enable , // Enable input
+up_down , // Up Down input
+count , // Count output
+overflow // Overflow output
+);
+
+ input clk;
+ input reset;
+ input enable;
+ input up_down;
+
+ output [`WIDTH-1 : 0] count;
+ output overflow;
+
+ reg [`WIDTH-1 : 0] count;
+
+ assign overflow = (up_down) ? (count == {{`WIDTH-1{1'b0}}, 1'b1}) :
+ (count == {1'b1, {`WIDTH-1{1'b0}}}) ;
+
+ always @(posedge clk)
+ if (reset)
+ count <= {`WIDTH{1'b0}};
+ else if (enable) begin
+ if (up_down) begin
+ count <= {~(^(count & `WIDTH'b01100011)),count[`WIDTH-1:1]};
+ end else begin
+ count <= {count[`WIDTH-2:0],~(^(count & `WIDTH'b10110001))};
+ end
+ end
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_misc1.v b/tests/asicworld/code_hdl_models_misc1.v
new file mode 100644
index 00000000..e3d9d5d6
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_misc1.v
@@ -0,0 +1,22 @@
+module misc1 (a,b,c,d,y);
+input a, b,c,d;
+output y;
+
+wire net1,net2,net3;
+
+supply1 vdd;
+supply0 vss;
+
+// y = !((a+b+c).d)
+
+pmos p1 (vdd,net1,a);
+pmos p2 (net1,net2,b);
+pmos p3 (net2,y,c);
+pmos p4 (vdd,y,d);
+
+nmos n1 (vss,net3,a);
+nmos n2 (vss,net3,b);
+nmos n3 (vss,net3,c);
+nmos n4 (net3,y,d);
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_mux21_switch.v b/tests/asicworld/code_hdl_models_mux21_switch.v
new file mode 100644
index 00000000..519c07fc
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_mux21_switch.v
@@ -0,0 +1,22 @@
+//-----------------------------------------------------
+// Design Name : mux21_switch
+// File Name : mux21_switch.v
+// Function : 2:1 Mux using Switch Primitives
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module mux21_switch (out, ctrl, in1, in2);
+
+ output out;
+ input ctrl, in1, in2;
+ wire w;
+
+ supply1 power;
+ supply0 ground;
+
+ pmos N1 (w, power, ctrl);
+ nmos N2 (w, ground, ctrl);
+
+ cmos C1 (out, in1, w, ctrl);
+ cmos C2 (out, in2, ctrl, w);
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_mux_2to1_gates.v b/tests/asicworld/code_hdl_models_mux_2to1_gates.v
new file mode 100644
index 00000000..fc762159
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_mux_2to1_gates.v
@@ -0,0 +1,18 @@
+//-----------------------------------------------------
+// Design Name : mux_2to1_gates
+// File Name : mux_2to1_gates.v
+// Function : 2:1 Mux using Gate Primitives
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module mux_2to1_gates(a,b,sel,y);
+input a,b,sel;
+output y;
+
+wire sel,a_sel,b_sel;
+
+not U_inv (inv_sel,sel);
+and U_anda (asel,a,inv_sel),
+ U_andb (bsel,b,sel);
+or U_or (y,asel,bsel);
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_mux_using_assign.v b/tests/asicworld/code_hdl_models_mux_using_assign.v
new file mode 100644
index 00000000..4284f10c
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_mux_using_assign.v
@@ -0,0 +1,22 @@
+//-----------------------------------------------------
+// Design Name : mux_using_assign
+// File Name : mux_using_assign.v
+// Function : 2:1 Mux using Assign
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module mux_using_assign(
+din_0 , // Mux first input
+din_1 , // Mux Second input
+sel , // Select input
+mux_out // Mux output
+);
+//-----------Input Ports---------------
+input din_0, din_1, sel ;
+//-----------Output Ports---------------
+output mux_out;
+//------------Internal Variables--------
+wire mux_out;
+//-------------Code Start-----------------
+assign mux_out = (sel) ? din_1 : din_0;
+
+endmodule //End Of Module mux
diff --git a/tests/asicworld/code_hdl_models_mux_using_case.v b/tests/asicworld/code_hdl_models_mux_using_case.v
new file mode 100644
index 00000000..123da448
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_mux_using_case.v
@@ -0,0 +1,28 @@
+//-----------------------------------------------------
+// Design Name : mux_using_case
+// File Name : mux_using_case.v
+// Function : 2:1 Mux using Case
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module mux_using_case(
+din_0 , // Mux first input
+din_1 , // Mux Second input
+sel , // Select input
+mux_out // Mux output
+);
+//-----------Input Ports---------------
+input din_0, din_1, sel ;
+//-----------Output Ports---------------
+output mux_out;
+//------------Internal Variables--------
+reg mux_out;
+//-------------Code Starts Here---------
+always @ (sel or din_0 or din_1)
+begin : MUX
+ case(sel )
+ 1'b0 : mux_out = din_0;
+ 1'b1 : mux_out = din_1;
+ endcase
+end
+
+endmodule //End Of Module mux
diff --git a/tests/asicworld/code_hdl_models_mux_using_if.v b/tests/asicworld/code_hdl_models_mux_using_if.v
new file mode 100644
index 00000000..4d42e208
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_mux_using_if.v
@@ -0,0 +1,29 @@
+//-----------------------------------------------------
+// Design Name : mux_using_if
+// File Name : mux_using_if.v
+// Function : 2:1 Mux using If
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module mux_using_if(
+din_0 , // Mux first input
+din_1 , // Mux Second input
+sel , // Select input
+mux_out // Mux output
+);
+//-----------Input Ports---------------
+input din_0, din_1, sel ;
+//-----------Output Ports---------------
+output mux_out;
+//------------Internal Variables--------
+reg mux_out;
+//-------------Code Starts Here---------
+always @ (sel or din_0 or din_1)
+begin : MUX
+ if (sel == 1'b0) begin
+ mux_out = din_0;
+ end else begin
+ mux_out = din_1 ;
+ end
+end
+
+endmodule //End Of Module mux
diff --git a/tests/asicworld/code_hdl_models_nand_switch.v b/tests/asicworld/code_hdl_models_nand_switch.v
new file mode 100644
index 00000000..1ccdd3a7
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_nand_switch.v
@@ -0,0 +1,14 @@
+module nand_switch(a,b,out);
+input a,b;
+output out;
+
+supply0 vss;
+supply1 vdd;
+wire net1;
+
+pmos p1 (vdd,out,a);
+pmos p2 (vdd,out,b);
+nmos n1 (vss,net1,a);
+nmos n2 (net1,out,b);
+
+endmodule \ No newline at end of file
diff --git a/tests/asicworld/code_hdl_models_one_hot_cnt.v b/tests/asicworld/code_hdl_models_one_hot_cnt.v
new file mode 100644
index 00000000..f6b84c6e
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_one_hot_cnt.v
@@ -0,0 +1,31 @@
+//-----------------------------------------------------
+// Design Name : one_hot_cnt
+// File Name : one_hot_cnt.v
+// Function : 8 bit one hot counter
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module one_hot_cnt (
+out , // Output of the counter
+enable , // enable for counter
+clk , // clock input
+reset // reset input
+);
+//----------Output Ports--------------
+output [7:0] out;
+
+//------------Input Ports--------------
+input enable, clk, reset;
+
+//------------Internal Variables--------
+reg [7:0] out;
+
+//-------------Code Starts Here-------
+always @ (posedge clk)
+if (reset) begin
+ out <= 8'b0000_0001 ;
+end else if (enable) begin
+ out <= {out[6],out[5],out[4],out[3],
+ out[2],out[1],out[0],out[7]};
+end
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_parallel_crc.v b/tests/asicworld/code_hdl_models_parallel_crc.v
new file mode 100644
index 00000000..d8d0bf1c
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_parallel_crc.v
@@ -0,0 +1,53 @@
+//-----------------------------------------------------
+// Design Name : parallel_crc_ccitt
+// File Name : parallel_crc.v
+// Function : CCITT Parallel CRC
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module parallel_crc_ccitt (
+clk ,
+reset ,
+enable ,
+init ,
+data_in ,
+crc_out
+);
+//-----------Input Ports---------------
+input clk ;
+input reset ;
+input enable ;
+input init ;
+input [7:0] data_in ;
+//-----------Output Ports---------------
+output [15:0] crc_out;
+//------------Internal Variables--------
+reg [15:0] crc_reg;
+wire [15:0] next_crc;
+//-------------Code Start-----------------
+assign crc_out = crc_reg;
+// CRC Control logic
+always @ (posedge clk)
+if (reset) begin
+ crc_reg <= 16'hFFFF;
+end else if (enable) begin
+ if (init) begin
+ crc_reg <= 16'hFFFF;
+ end else begin
+ crc_reg <= next_crc;
+ end
+end
+// Parallel CRC calculation
+assign next_crc[0] = data_in[7] ^ data_in[0] ^ crc_reg[4] ^ crc_reg[11];
+assign next_crc[1] = data_in[1] ^ crc_reg[5];
+assign next_crc[2] = data_in[2] ^ crc_reg[6];
+assign next_crc[3] = data_in[3] ^ crc_reg[7];
+assign next_crc[4] = data_in[4] ^ crc_reg[8];
+assign next_crc[5] = data_in[7] ^ data_in[5] ^ data_in[0] ^ crc_reg[4] ^ crc_reg[9] ^ crc_reg[11];
+assign next_crc[6] = data_in[6] ^ data_in[1] ^ crc_reg[5] ^ crc_reg[10];
+assign next_crc[7] = data_in[7] ^ data_in[2] ^ crc_reg[6] ^ crc_reg[11];
+assign next_crc[8] = data_in[3] ^ crc_reg[0] ^ crc_reg[7];
+assign next_crc[9] = data_in[4] ^ crc_reg[1] ^ crc_reg[8];
+assign next_crc[10] = data_in[5] ^ crc_reg[2] ^ crc_reg[9];
+assign next_crc[11] = data_in[6] ^ crc_reg[3] ^ crc_reg[10];
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_parity_using_assign.v b/tests/asicworld/code_hdl_models_parity_using_assign.v
new file mode 100644
index 00000000..b0282e8d
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_parity_using_assign.v
@@ -0,0 +1,21 @@
+//-----------------------------------------------------
+// Design Name : parity_using_assign
+// File Name : parity_using_assign.v
+// Function : Parity using assign
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module parity_using_assign (
+data_in , // 8 bit data in
+parity_out // 1 bit parity out
+);
+output parity_out ;
+input [7:0] data_in ;
+
+wire parity_out ;
+
+assign parity_out = (data_in[0] ^ data_in[1]) ^
+ (data_in[2] ^ data_in[3]) ^
+ (data_in[4] ^ data_in[5]) ^
+ (data_in[6] ^ data_in[7]);
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_parity_using_bitwise.v b/tests/asicworld/code_hdl_models_parity_using_bitwise.v
new file mode 100644
index 00000000..0046fb14
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_parity_using_bitwise.v
@@ -0,0 +1,16 @@
+//-----------------------------------------------------
+// Design Name : parity_using_bitwise
+// File Name : parity_using_bitwise.v
+// Function : Parity using bitwise xor
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module parity_using_bitwise (
+data_in , // 8 bit data in
+parity_out // 1 bit parity out
+);
+output parity_out ;
+input [7:0] data_in ;
+
+assign parity_out = ^data_in;
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_parity_using_function.v b/tests/asicworld/code_hdl_models_parity_using_function.v
new file mode 100644
index 00000000..0d07aaeb
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_parity_using_function.v
@@ -0,0 +1,29 @@
+//-----------------------------------------------------
+// Design Name : parity_using_function
+// File Name : parity_using_function.v
+// Function : Parity using function
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module parity_using_function (
+data_in , // 8 bit data in
+parity_out // 1 bit parity out
+);
+output parity_out ;
+input [7:0] data_in ;
+
+wire parity_out ;
+
+function parity;
+ input [31:0] data;
+ begin
+ parity = (data_in[0] ^ data_in[1]) ^
+ (data_in[2] ^ data_in[3]) ^
+ (data_in[4] ^ data_in[5]) ^
+ (data_in[6] ^ data_in[7]);
+ end
+endfunction
+
+
+assign parity_out = parity(data_in);
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_pri_encoder_using_assign.v b/tests/asicworld/code_hdl_models_pri_encoder_using_assign.v
new file mode 100644
index 00000000..c1ce960c
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_pri_encoder_using_assign.v
@@ -0,0 +1,36 @@
+//-----------------------------------------------------
+// Design Name : pri_encoder_using_assign
+// File Name : pri_encoder_using_assign.v
+// Function : Pri Encoder using assign
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module pri_encoder_using_assign (
+binary_out , // 4 bit binary output
+encoder_in , // 16-bit input
+enable // Enable for the encoder
+);
+
+output [3:0] binary_out ;
+input enable ;
+input [15:0] encoder_in ;
+
+wire [3:0] binary_out ;
+
+assign binary_out = (!enable) ? 0 : (
+ (encoder_in == 16'bxxxx_xxxx_xxxx_xxx1) ? 0 :
+ (encoder_in == 16'bxxxx_xxxx_xxxx_xx10) ? 1 :
+ (encoder_in == 16'bxxxx_xxxx_xxxx_x100) ? 2 :
+ (encoder_in == 16'bxxxx_xxxx_xxxx_1000) ? 3 :
+ (encoder_in == 16'bxxxx_xxxx_xxx1_0000) ? 4 :
+ (encoder_in == 16'bxxxx_xxxx_xx10_0000) ? 5 :
+ (encoder_in == 16'bxxxx_xxxx_x100_0000) ? 6 :
+ (encoder_in == 16'bxxxx_xxxx_1000_0000) ? 7 :
+ (encoder_in == 16'bxxxx_xxx1_0000_0000) ? 8 :
+ (encoder_in == 16'bxxxx_xx10_0000_0000) ? 9 :
+ (encoder_in == 16'bxxxx_x100_0000_0000) ? 10 :
+ (encoder_in == 16'bxxxx_1000_0000_0000) ? 11 :
+ (encoder_in == 16'bxxx1_0000_0000_0000) ? 12 :
+ (encoder_in == 16'bxx10_0000_0000_0000) ? 13 :
+ (encoder_in == 16'bx100_0000_0000_0000) ? 14 : 15);
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_rom_using_case.v b/tests/asicworld/code_hdl_models_rom_using_case.v
new file mode 100644
index 00000000..6b700993
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_rom_using_case.v
@@ -0,0 +1,42 @@
+//-----------------------------------------------------
+// Design Name : rom_using_case
+// File Name : rom_using_case.v
+// Function : ROM using case
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module rom_using_case (
+address , // Address input
+data , // Data output
+read_en , // Read Enable
+ce // Chip Enable
+);
+input [3:0] address;
+output [7:0] data;
+input read_en;
+input ce;
+
+reg [7:0] data ;
+
+always @ (ce or read_en or address)
+begin
+ case (address)
+ 0 : data = 10;
+ 1 : data = 55;
+ 2 : data = 244;
+ 3 : data = 0;
+ 4 : data = 1;
+ 5 : data = 8'hff;
+ 6 : data = 8'h11;
+ 7 : data = 8'h1;
+ 8 : data = 8'h10;
+ 9 : data = 8'h0;
+ 10 : data = 8'h10;
+ 11 : data = 8'h15;
+ 12 : data = 8'h60;
+ 13 : data = 8'h90;
+ 14 : data = 8'h70;
+ 15 : data = 8'h90;
+ endcase
+end
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_serial_crc.v b/tests/asicworld/code_hdl_models_serial_crc.v
new file mode 100644
index 00000000..a4a63a26
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_serial_crc.v
@@ -0,0 +1,54 @@
+//-----------------------------------------------------
+// Design Name : serial_crc_ccitt
+// File Name : serial_crc.v
+// Function : CCITT Serial CRC
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module serial_crc_ccitt (
+clk ,
+reset ,
+enable ,
+init ,
+data_in ,
+crc_out
+);
+//-----------Input Ports---------------
+input clk ;
+input reset ;
+input enable ;
+input init ;
+input data_in ;
+//-----------Output Ports---------------
+output [15:0] crc_out;
+//------------Internal Variables--------
+reg [15:0] lfsr;
+//-------------Code Start-----------------
+assign crc_out = lfsr;
+// Logic to CRC Calculation
+always @ (posedge clk)
+if (reset) begin
+ lfsr <= 16'hFFFF;
+end else if (enable) begin
+ if (init) begin
+ lfsr <= 16'hFFFF;
+ end else begin
+ lfsr[0] <= data_in ^ lfsr[15];
+ lfsr[1] <= lfsr[0];
+ lfsr[2] <= lfsr[1];
+ lfsr[3] <= lfsr[2];
+ lfsr[4] <= lfsr[3];
+ lfsr[5] <= lfsr[4] ^ data_in ^ lfsr[15];
+ lfsr[6] <= lfsr[5];
+ lfsr[7] <= lfsr[6];
+ lfsr[8] <= lfsr[7];
+ lfsr[9] <= lfsr[8];
+ lfsr[10] <= lfsr[9];
+ lfsr[11] <= lfsr[10];
+ lfsr[12] <= lfsr[11] ^ data_in ^ lfsr[15];
+ lfsr[13] <= lfsr[12];
+ lfsr[14] <= lfsr[13];
+ lfsr[15] <= lfsr[14];
+ end
+end
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_t_gate_switch.v b/tests/asicworld/code_hdl_models_t_gate_switch.v
new file mode 100644
index 00000000..5a7e0eaf
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_t_gate_switch.v
@@ -0,0 +1,11 @@
+module t_gate_switch (L,R,nC,C);
+ inout L;
+ inout R;
+ input nC;
+ input C;
+
+ //Syntax: keyword unique_name (drain. source, gate);
+ pmos p1 (L,R,nC);
+ nmos p2 (L,R,C);
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_tff_async_reset.v b/tests/asicworld/code_hdl_models_tff_async_reset.v
new file mode 100644
index 00000000..4c5a1fa9
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_tff_async_reset.v
@@ -0,0 +1,27 @@
+//-----------------------------------------------------
+// Design Name : tff_async_reset
+// File Name : tff_async_reset.v
+// Function : T flip-flop async reset
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module tff_async_reset (
+data , // Data Input
+clk , // Clock Input
+reset , // Reset input
+q // Q output
+);
+//-----------Input Ports---------------
+input data, clk, reset ;
+//-----------Output Ports---------------
+output q;
+//------------Internal Variables--------
+reg q;
+//-------------Code Starts Here---------
+always @ ( posedge clk or negedge reset)
+if (~reset) begin
+ q <= 1'b0;
+end else if (data) begin
+ q <= !q;
+end
+
+endmodule //End Of Module tff_async_reset
diff --git a/tests/asicworld/code_hdl_models_tff_sync_reset.v b/tests/asicworld/code_hdl_models_tff_sync_reset.v
new file mode 100644
index 00000000..a962d53d
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_tff_sync_reset.v
@@ -0,0 +1,27 @@
+//-----------------------------------------------------
+// Design Name : tff_sync_reset
+// File Name : tff_sync_reset.v
+// Function : T flip-flop sync reset
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module tff_sync_reset (
+data , // Data Input
+clk , // Clock Input
+reset , // Reset input
+q // Q output
+);
+//-----------Input Ports---------------
+input data, clk, reset ;
+//-----------Output Ports---------------
+output q;
+//------------Internal Variables--------
+reg q;
+//-------------Code Starts Here---------
+always @ ( posedge clk)
+if (~reset) begin
+ q <= 1'b0;
+end else if (data) begin
+ q <= !q;
+end
+
+endmodule //End Of Module tff_async_reset
diff --git a/tests/asicworld/code_hdl_models_uart.v b/tests/asicworld/code_hdl_models_uart.v
new file mode 100644
index 00000000..40205250
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_uart.v
@@ -0,0 +1,154 @@
+//-----------------------------------------------------
+// Design Name : uart
+// File Name : uart.v
+// Function : Simple UART
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module uart (
+reset ,
+txclk ,
+ld_tx_data ,
+tx_data ,
+tx_enable ,
+tx_out ,
+tx_empty ,
+rxclk ,
+uld_rx_data ,
+rx_data ,
+rx_enable ,
+rx_in ,
+rx_empty
+);
+// Port declarations
+input reset ;
+input txclk ;
+input ld_tx_data ;
+input [7:0] tx_data ;
+input tx_enable ;
+output tx_out ;
+output tx_empty ;
+input rxclk ;
+input uld_rx_data ;
+output [7:0] rx_data ;
+input rx_enable ;
+input rx_in ;
+output rx_empty ;
+
+// Internal Variables
+reg [7:0] tx_reg ;
+reg tx_empty ;
+reg tx_over_run ;
+reg [3:0] tx_cnt ;
+reg tx_out ;
+reg [7:0] rx_reg ;
+reg [7:0] rx_data ;
+reg [3:0] rx_sample_cnt ;
+reg [3:0] rx_cnt ;
+reg rx_frame_err ;
+reg rx_over_run ;
+reg rx_empty ;
+reg rx_d1 ;
+reg rx_d2 ;
+reg rx_busy ;
+
+// UART RX Logic
+always @ (posedge rxclk or posedge reset)
+if (reset) begin
+ rx_reg <= 0;
+ rx_data <= 0;
+ rx_sample_cnt <= 0;
+ rx_cnt <= 0;
+ rx_frame_err <= 0;
+ rx_over_run <= 0;
+ rx_empty <= 1;
+ rx_d1 <= 1;
+ rx_d2 <= 1;
+ rx_busy <= 0;
+end else begin
+ // Synchronize the asynch signal
+ rx_d1 <= rx_in;
+ rx_d2 <= rx_d1;
+ // Uload the rx data
+ if (uld_rx_data) begin
+ rx_data <= rx_reg;
+ rx_empty <= 1;
+ end
+ // Receive data only when rx is enabled
+ if (rx_enable) begin
+ // Check if just received start of frame
+ if (!rx_busy && !rx_d2) begin
+ rx_busy <= 1;
+ rx_sample_cnt <= 1;
+ rx_cnt <= 0;
+ end
+ // Start of frame detected, Proceed with rest of data
+ if (rx_busy) begin
+ rx_sample_cnt <= rx_sample_cnt + 1;
+ // Logic to sample at middle of data
+ if (rx_sample_cnt == 7) begin
+ if ((rx_d2 == 1) && (rx_cnt == 0)) begin
+ rx_busy <= 0;
+ end else begin
+ rx_cnt <= rx_cnt + 1;
+ // Start storing the rx data
+ if (rx_cnt > 0 && rx_cnt < 9) begin
+ rx_reg[rx_cnt - 1] <= rx_d2;
+ end
+ if (rx_cnt == 9) begin
+ rx_busy <= 0;
+ // Check if End of frame received correctly
+ if (rx_d2 == 0) begin
+ rx_frame_err <= 1;
+ end else begin
+ rx_empty <= 0;
+ rx_frame_err <= 0;
+ // Check if last rx data was not unloaded,
+ rx_over_run <= (rx_empty) ? 0 : 1;
+ end
+ end
+ end
+ end
+ end
+ end
+ if (!rx_enable) begin
+ rx_busy <= 0;
+ end
+end
+
+// UART TX Logic
+always @ (posedge txclk or posedge reset)
+if (reset) begin
+ tx_reg <= 0;
+ tx_empty <= 1;
+ tx_over_run <= 0;
+ tx_out <= 1;
+ tx_cnt <= 0;
+end else begin
+ if (ld_tx_data) begin
+ if (!tx_empty) begin
+ tx_over_run <= 0;
+ end else begin
+ tx_reg <= tx_data;
+ tx_empty <= 0;
+ end
+ end
+ if (tx_enable && !tx_empty) begin
+ tx_cnt <= tx_cnt + 1;
+ if (tx_cnt == 0) begin
+ tx_out <= 0;
+ end
+ if (tx_cnt > 0 && tx_cnt < 9) begin
+ tx_out <= tx_reg[tx_cnt -1];
+ end
+ if (tx_cnt == 9) begin
+ tx_out <= 1;
+ tx_cnt <= 0;
+ tx_empty <= 1;
+ end
+ end
+ if (!tx_enable) begin
+ tx_cnt <= 0;
+ end
+end
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_up_counter.v b/tests/asicworld/code_hdl_models_up_counter.v
new file mode 100644
index 00000000..e0530218
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_up_counter.v
@@ -0,0 +1,29 @@
+//-----------------------------------------------------
+// Design Name : up_counter
+// File Name : up_counter.v
+// Function : Up counter
+// Coder : Deepak
+//-----------------------------------------------------
+module up_counter (
+out , // Output of the counter
+enable , // enable for counter
+clk , // clock Input
+reset // reset Input
+);
+//----------Output Ports--------------
+ output [7:0] out;
+//------------Input Ports--------------
+ input enable, clk, reset;
+//------------Internal Variables--------
+ reg [7:0] out;
+//-------------Code Starts Here-------
+always @(posedge clk)
+if (reset) begin
+ out <= 8'b0 ;
+end else if (enable) begin
+ out <= out + 1;
+end
+
+
+endmodule
+
diff --git a/tests/asicworld/code_hdl_models_up_counter_load.v b/tests/asicworld/code_hdl_models_up_counter_load.v
new file mode 100644
index 00000000..92ad895a
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_up_counter_load.v
@@ -0,0 +1,32 @@
+//-----------------------------------------------------
+// Design Name : up_counter_load
+// File Name : up_counter_load.v
+// Function : Up counter with load
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module up_counter_load (
+out , // Output of the counter
+data , // Parallel load for the counter
+load , // Parallel load enable
+enable , // Enable counting
+clk , // clock input
+reset // reset input
+);
+//----------Output Ports--------------
+output [7:0] out;
+//------------Input Ports--------------
+input [7:0] data;
+input load, enable, clk, reset;
+//------------Internal Variables--------
+reg [7:0] out;
+//-------------Code Starts Here-------
+always @(posedge clk)
+if (reset) begin
+ out <= 8'b0 ;
+end else if (load) begin
+ out <= data;
+end else if (enable) begin
+ out <= out + 1;
+end
+
+endmodule
diff --git a/tests/asicworld/code_hdl_models_up_down_counter.v b/tests/asicworld/code_hdl_models_up_down_counter.v
new file mode 100644
index 00000000..fff2982a
--- /dev/null
+++ b/tests/asicworld/code_hdl_models_up_down_counter.v
@@ -0,0 +1,29 @@
+//-----------------------------------------------------
+// Design Name : up_down_counter
+// File Name : up_down_counter.v
+// Function : Up down counter
+// Coder : Deepak Kumar Tala
+//-----------------------------------------------------
+module up_down_counter (
+out , // Output of the counter
+up_down , // up_down control for counter
+clk , // clock input
+reset // reset input
+);
+//----------Output Ports--------------
+output [7:0] out;
+//------------Input Ports--------------
+input up_down, clk, reset;
+//------------Internal Variables--------
+reg [7:0] out;
+//-------------Code Starts Here-------
+always @(posedge clk)
+if (reset) begin // active high reset
+ out <= 8'b0 ;
+end else if (up_down) begin
+ out <= out + 1;
+end else begin
+ out <= out - 1;
+end
+
+endmodule
diff --git a/tests/asicworld/code_specman_switch_fabric.v b/tests/asicworld/code_specman_switch_fabric.v
new file mode 100644
index 00000000..1ac7ee70
--- /dev/null
+++ b/tests/asicworld/code_specman_switch_fabric.v
@@ -0,0 +1,82 @@
+module switch_fabric(
+ clk, reset, data_in0, data_in1, data_in2,
+ data_in3, data_in4, data_in5, data_in_valid0,
+ data_in_valid1, data_in_valid2, data_in_valid3,
+ data_in_valid4, data_in_valid5, data_out0,
+ data_out1, data_out2, data_out3, data_out4,
+ data_out5, data_out_ack0, data_out_ack1,
+ data_out_ack2, data_out_ack3, data_out_ack4,
+ data_out_ack5
+);
+
+input clk, reset;
+input [7:0] data_in0, data_in1, data_in2, data_in3;
+input [7:0] data_in4, data_in5;
+input data_in_valid0, data_in_valid1, data_in_valid2;
+input [7:0] data_in_valid3, data_in_valid4, data_in_valid5;
+output [7:0] data_out0, data_out1, data_out2, data_out3;
+output [7:0] data_out4, data_out5;
+output data_out_ack0, data_out_ack1, data_out_ack2;
+output [7:0] data_out_ack3, data_out_ack4, data_out_ack5;
+
+(* gentb_clock *)
+wire clk;
+
+switch port_0 ( .clk(clk), .reset(reset), .data_in(data_in0),
+ .data_in_valid(data_in_valid0), .data_out(data_out0),
+ .data_out_ack(data_out_ack0));
+
+switch port_1 ( .clk(clk), .reset(reset), .data_in(data_in1),
+ .data_in_valid(data_in_valid1), .data_out(data_out1),
+ .data_out_ack(data_out_ack1));
+
+switch port_2 ( .clk(clk), .reset(reset), .data_in(data_in2),
+ .data_in_valid(data_in_valid2), .data_out(data_out2), .
+ data_out_ack(data_out_ack2));
+
+switch port_3 ( .clk(clk), .reset(reset), .data_in(data_in3),
+ .data_in_valid(data_in_valid3), .data_out(data_out3),
+ .data_out_ack(data_out_ack3));
+
+switch port_4 ( .clk(clk), .reset(reset), .data_in(data_in4),
+ .data_in_valid(data_in_valid4), .data_out(data_out4),
+ .data_out_ack(data_out_ack4));
+
+switch port_5 ( .clk(clk), .reset(reset), .data_in(data_in5),
+ .data_in_valid(data_in_valid5), .data_out(data_out5),
+ .data_out_ack(data_out_ack5));
+
+endmodule
+
+module switch (
+ clk,
+ reset,
+ data_in,
+ data_in_valid,
+ data_out,
+ data_out_ack
+);
+
+input clk;
+input reset;
+input [7:0] data_in;
+input data_in_valid;
+output [7:0] data_out;
+output data_out_ack;
+
+reg [7:0] data_out;
+reg data_out_ack;
+
+always @ (posedge clk)
+if (reset) begin
+ data_out <= 0;
+ data_out_ack <= 0;
+end else if (data_in_valid) begin
+ data_out <= data_in;
+ data_out_ack <= 1;
+end else begin
+ data_out <= 0;
+ data_out_ack <= 0;
+end
+
+endmodule
diff --git a/tests/asicworld/code_tidbits_asyn_reset.v b/tests/asicworld/code_tidbits_asyn_reset.v
new file mode 100644
index 00000000..58e47c56
--- /dev/null
+++ b/tests/asicworld/code_tidbits_asyn_reset.v
@@ -0,0 +1,18 @@
+module asyn_reset(clk,reset,a,c);
+ input clk;
+ input reset;
+ input a;
+ output c;
+
+ wire clk;
+ wire reset;
+ wire a;
+ reg c;
+
+always @ (posedge clk or posedge reset)
+ if ( reset == 1'b1) begin
+ c <= 0;
+ end else begin
+ c <= a;
+ end
+endmodule
diff --git a/tests/asicworld/code_tidbits_blocking.v b/tests/asicworld/code_tidbits_blocking.v
new file mode 100644
index 00000000..e13b72cc
--- /dev/null
+++ b/tests/asicworld/code_tidbits_blocking.v
@@ -0,0 +1,17 @@
+module blocking (clk,a,c);
+input clk;
+input a;
+output c;
+
+wire clk;
+wire a;
+reg c;
+reg b;
+
+always @ (posedge clk )
+begin
+ b = a;
+ c = b;
+end
+
+endmodule
diff --git a/tests/asicworld/code_tidbits_fsm_using_always.v b/tests/asicworld/code_tidbits_fsm_using_always.v
new file mode 100644
index 00000000..8a8775b9
--- /dev/null
+++ b/tests/asicworld/code_tidbits_fsm_using_always.v
@@ -0,0 +1,91 @@
+//-----------------------------------------------------
+// This is FSM demo program using always block
+// Design Name : fsm_using_always
+// File Name : fsm_using_always.v
+//-----------------------------------------------------
+module fsm_using_always (
+clock , // clock
+reset , // Active high, syn reset
+req_0 , // Request 0
+req_1 , // Request 1
+gnt_0 , // Grant 0
+gnt_1
+);
+//-------------Input Ports-----------------------------
+input clock,reset,req_0,req_1;
+ //-------------Output Ports----------------------------
+output gnt_0,gnt_1;
+//-------------Input ports Data Type-------------------
+wire clock,reset,req_0,req_1;
+//-------------Output Ports Data Type------------------
+reg gnt_0,gnt_1;
+//-------------Internal Constants--------------------------
+parameter SIZE = 3 ;
+parameter IDLE = 3'b001,GNT0 = 3'b010,GNT1 = 3'b100 ;
+//-------------Internal Variables---------------------------
+reg [SIZE-1:0] state ;// Seq part of the FSM
+reg [SIZE-1:0] next_state ;// combo part of FSM
+//----------Code startes Here------------------------
+always @ (state or req_0 or req_1)
+begin : FSM_COMBO
+ next_state = 3'b000;
+ case(state)
+ IDLE : if (req_0 == 1'b1) begin
+ next_state = GNT0;
+ end else if (req_1 == 1'b1) begin
+ next_state= GNT1;
+ end else begin
+ next_state = IDLE;
+ end
+ GNT0 : if (req_0 == 1'b1) begin
+ next_state = GNT0;
+ end else begin
+ next_state = IDLE;
+ end
+ GNT1 : if (req_1 == 1'b1) begin
+ next_state = GNT1;
+ end else begin
+ next_state = IDLE;
+ end
+ default : next_state = IDLE;
+ endcase
+end
+//----------Seq Logic-----------------------------
+always @ (posedge clock)
+begin : FSM_SEQ
+ if (reset == 1'b1) begin
+ state <= #1 IDLE;
+ end else begin
+ state <= #1 next_state;
+ end
+end
+//----------Output Logic-----------------------------
+always @ (posedge clock)
+begin : OUTPUT_LOGIC
+if (reset == 1'b1) begin
+ gnt_0 <= #1 1'b0;
+ gnt_1 <= #1 1'b0;
+end
+else begin
+ case(state)
+ IDLE : begin
+ gnt_0 <= #1 1'b0;
+ gnt_1 <= #1 1'b0;
+ end
+ GNT0 : begin
+ gnt_0 <= #1 1'b1;
+ gnt_1 <= #1 1'b0;
+ end
+ GNT1 : begin
+ gnt_0 <= #1 1'b0;
+ gnt_1 <= #1 1'b1;
+ end
+ default : begin
+ gnt_0 <= #1 1'b0;
+ gnt_1 <= #1 1'b0;
+ end
+ endcase
+end
+end // End Of Block OUTPUT_LOGIC
+
+endmodule // End of Module arbiter
diff --git a/tests/asicworld/code_tidbits_fsm_using_function.v b/tests/asicworld/code_tidbits_fsm_using_function.v
new file mode 100644
index 00000000..404498a0
--- /dev/null
+++ b/tests/asicworld/code_tidbits_fsm_using_function.v
@@ -0,0 +1,94 @@
+//-----------------------------------------------------
+// This is FSM demo program using function
+// Design Name : fsm_using_function
+// File Name : fsm_using_function.v
+//-----------------------------------------------------
+module fsm_using_function (
+clock , // clock
+reset , // Active high, syn reset
+req_0 , // Request 0
+req_1 , // Request 1
+gnt_0 , // Grant 0
+gnt_1
+);
+//-------------Input Ports-----------------------------
+input clock,reset,req_0,req_1;
+ //-------------Output Ports----------------------------
+output gnt_0,gnt_1;
+//-------------Input ports Data Type-------------------
+wire clock,reset,req_0,req_1;
+//-------------Output Ports Data Type------------------
+reg gnt_0,gnt_1;
+//-------------Internal Constants--------------------------
+parameter SIZE = 3 ;
+parameter IDLE = 3'b001,GNT0 = 3'b010,GNT1 = 3'b100 ;
+//-------------Internal Variables---------------------------
+reg [SIZE-1:0] state ;// Seq part of the FSM
+wire [SIZE-1:0] next_state ;// combo part of FSM
+//----------Code startes Here------------------------
+assign next_state = fsm_function(state, req_0, req_1);
+//----------Function for Combo Logic-----------------
+function [SIZE-1:0] fsm_function;
+ input [SIZE-1:0] state ;
+ input req_0 ;
+ input req_1 ;
+ case(state)
+ IDLE : if (req_0 == 1'b1) begin
+ fsm_function = GNT0;
+ end else if (req_1 == 1'b1) begin
+ fsm_function= GNT1;
+ end else begin
+ fsm_function = IDLE;
+ end
+ GNT0 : if (req_0 == 1'b1) begin
+ fsm_function = GNT0;
+ end else begin
+ fsm_function = IDLE;
+ end
+ GNT1 : if (req_1 == 1'b1) begin
+ fsm_function = GNT1;
+ end else begin
+ fsm_function = IDLE;
+ end
+ default : fsm_function = IDLE;
+ endcase
+endfunction
+//----------Seq Logic-----------------------------
+always @ (posedge clock)
+begin : FSM_SEQ
+ if (reset == 1'b1) begin
+ state <= #1 IDLE;
+ end else begin
+ state <= #1 next_state;
+ end
+end
+//----------Output Logic-----------------------------
+always @ (posedge clock)
+begin : OUTPUT_LOGIC
+if (reset == 1'b1) begin
+ gnt_0 <= #1 1'b0;
+ gnt_1 <= #1 1'b0;
+end
+else begin
+ case(state)
+ IDLE : begin
+ gnt_0 <= #1 1'b0;
+ gnt_1 <= #1 1'b0;
+ end
+ GNT0 : begin
+ gnt_0 <= #1 1'b1;
+ gnt_1 <= #1 1'b0;
+ end
+ GNT1 : begin
+ gnt_0 <= #1 1'b0;
+ gnt_1 <= #1 1'b1;
+ end
+ default : begin
+ gnt_0 <= #1 1'b0;
+ gnt_1 <= #1 1'b0;
+ end
+ endcase
+end
+end // End Of Block OUTPUT_LOGIC
+
+endmodule // End of Module arbiter
diff --git a/tests/asicworld/code_tidbits_fsm_using_single_always.v b/tests/asicworld/code_tidbits_fsm_using_single_always.v
new file mode 100644
index 00000000..67cc0884
--- /dev/null
+++ b/tests/asicworld/code_tidbits_fsm_using_single_always.v
@@ -0,0 +1,63 @@
+//====================================================
+// This is FSM demo program using single always
+// for both seq and combo logic
+// Design Name : fsm_using_single_always
+// File Name : fsm_using_single_always.v
+//=====================================================
+module fsm_using_single_always (
+clock , // clock
+reset , // Active high, syn reset
+req_0 , // Request 0
+req_1 , // Request 1
+gnt_0 , // Grant 0
+gnt_1
+);
+//=============Input Ports=============================
+input clock,reset,req_0,req_1;
+ //=============Output Ports===========================
+output gnt_0,gnt_1;
+//=============Input ports Data Type===================
+wire clock,reset,req_0,req_1;
+//=============Output Ports Data Type==================
+reg gnt_0,gnt_1;
+//=============Internal Constants======================
+parameter SIZE = 3 ;
+parameter IDLE = 3'b001,GNT0 = 3'b010,GNT1 = 3'b100 ;
+//=============Internal Variables======================
+reg [SIZE-1:0] state ;// Seq part of the FSM
+reg [SIZE-1:0] next_state ;// combo part of FSM
+//==========Code startes Here==========================
+always @ (posedge clock)
+begin : FSM
+if (reset == 1'b1) begin
+ state <= #1 IDLE;
+ gnt_0 <= 0;
+ gnt_1 <= 0;
+end else
+ case(state)
+ IDLE : if (req_0 == 1'b1) begin
+ state <= #1 GNT0;
+ gnt_0 <= 1;
+ end else if (req_1 == 1'b1) begin
+ gnt_1 <= 1;
+ state <= #1 GNT1;
+ end else begin
+ state <= #1 IDLE;
+ end
+ GNT0 : if (req_0 == 1'b1) begin
+ state <= #1 GNT0;
+ end else begin
+ gnt_0 <= 0;
+ state <= #1 IDLE;
+ end
+ GNT1 : if (req_1 == 1'b1) begin
+ state <= #1 GNT1;
+ end else begin
+ gnt_1 <= 0;
+ state <= #1 IDLE;
+ end
+ default : state <= #1 IDLE;
+endcase
+end
+
+endmodule // End of Module arbiter
diff --git a/tests/asicworld/code_tidbits_nonblocking.v b/tests/asicworld/code_tidbits_nonblocking.v
new file mode 100644
index 00000000..4a0d365e
--- /dev/null
+++ b/tests/asicworld/code_tidbits_nonblocking.v
@@ -0,0 +1,17 @@
+module nonblocking (clk,a,c);
+input clk;
+input a;
+output c;
+
+wire clk;
+wire a;
+reg c;
+reg b;
+
+always @ (posedge clk )
+begin
+ b <= a;
+ c <= b;
+end
+
+endmodule
diff --git a/tests/asicworld/code_tidbits_reg_combo_example.v b/tests/asicworld/code_tidbits_reg_combo_example.v
new file mode 100644
index 00000000..9689788c
--- /dev/null
+++ b/tests/asicworld/code_tidbits_reg_combo_example.v
@@ -0,0 +1,13 @@
+module reg_combo_example( a, b, y);
+input a, b;
+output y;
+
+reg y;
+wire a, b;
+
+always @ ( a or b)
+begin
+ y = a & b;
+end
+
+endmodule
diff --git a/tests/asicworld/code_tidbits_reg_seq_example.v b/tests/asicworld/code_tidbits_reg_seq_example.v
new file mode 100644
index 00000000..458c8792
--- /dev/null
+++ b/tests/asicworld/code_tidbits_reg_seq_example.v
@@ -0,0 +1,15 @@
+module reg_seq_example( clk, reset, d, q);
+input clk, reset, d;
+output q;
+
+reg q;
+wire clk, reset, d;
+
+always @ (posedge clk or posedge reset)
+if (reset) begin
+ q <= 1'b0;
+end else begin
+ q <= d;
+end
+
+endmodule
diff --git a/tests/asicworld/code_tidbits_syn_reset.v b/tests/asicworld/code_tidbits_syn_reset.v
new file mode 100644
index 00000000..994771b1
--- /dev/null
+++ b/tests/asicworld/code_tidbits_syn_reset.v
@@ -0,0 +1,19 @@
+module syn_reset (clk,reset,a,c);
+ input clk;
+ input reset;
+ input a;
+ output c;
+
+ wire clk;
+ wire reset;
+ wire a;
+ reg c;
+
+always @ (posedge clk )
+ if ( reset == 1'b1) begin
+ c <= 0;
+ end else begin
+ c <= a;
+ end
+
+endmodule
diff --git a/tests/asicworld/code_tidbits_wire_example.v b/tests/asicworld/code_tidbits_wire_example.v
new file mode 100644
index 00000000..577a535d
--- /dev/null
+++ b/tests/asicworld/code_tidbits_wire_example.v
@@ -0,0 +1,9 @@
+module wire_example( a, b, y);
+ input a, b;
+ output y;
+
+ wire a, b, y;
+
+ assign y = a & b;
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_addbit.v b/tests/asicworld/code_verilog_tutorial_addbit.v
new file mode 100644
index 00000000..22063b05
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_addbit.v
@@ -0,0 +1,24 @@
+module addbit (
+a , // first input
+b , // Second input
+ci , // Carry input
+sum , // sum output
+co // carry output
+);
+//Input declaration
+input a;
+input b;
+input ci;
+//Ouput declaration
+output sum;
+output co;
+//Port Data types
+wire a;
+wire b;
+wire ci;
+wire sum;
+wire co;
+//Code starts here
+assign {co,sum} = a + b + ci;
+
+endmodule // End of Module addbit
diff --git a/tests/asicworld/code_verilog_tutorial_always_example.v b/tests/asicworld/code_verilog_tutorial_always_example.v
new file mode 100644
index 00000000..8b0fc206
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_always_example.v
@@ -0,0 +1,11 @@
+module always_example();
+reg clk,reset,enable,q_in,data;
+
+always @ (posedge clk)
+if (reset) begin
+ data <= 0;
+end else if (enable) begin
+ data <= q_in;
+end
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_bus_con.v b/tests/asicworld/code_verilog_tutorial_bus_con.v
new file mode 100644
index 00000000..b100c813
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_bus_con.v
@@ -0,0 +1,8 @@
+module bus_con (a,b, y);
+ input [3:0] a, b;
+ output [7:0] y;
+ wire [7:0] y;
+
+ assign y = {a,b};
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_comment.v b/tests/asicworld/code_verilog_tutorial_comment.v
new file mode 100644
index 00000000..1cc0eb42
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_comment.v
@@ -0,0 +1,25 @@
+/* This is a
+ Multi line comment
+ example */
+module addbit (
+a,
+b,
+ci,
+sum,
+co);
+
+// Input Ports Single line comment
+input a;
+input b;
+input ci;
+// Output ports
+output sum;
+output co;
+// Data Types
+wire a;
+wire b;
+wire ci;
+wire sum;
+wire co;
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_counter.v b/tests/asicworld/code_verilog_tutorial_counter.v
new file mode 100644
index 00000000..10ca00df
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_counter.v
@@ -0,0 +1,19 @@
+//-----------------------------------------------------
+// Design Name : counter
+// File Name : counter.v
+// Function : 4 bit up counter
+// Coder : Deepak
+//-----------------------------------------------------
+module counter (clk, reset, enable, count);
+input clk, reset, enable;
+output [3:0] count;
+reg [3:0] count;
+
+always @ (posedge clk)
+if (reset == 1'b1) begin
+ count <= 0;
+end else if ( enable == 1'b1) begin
+ count <= count + 1;
+end
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_counter_tb.v b/tests/asicworld/code_verilog_tutorial_counter_tb.v
new file mode 100644
index 00000000..33d54050
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_counter_tb.v
@@ -0,0 +1,101 @@
+///////////////////////////////////////////////////////////////////////////
+// MODULE : counter_tb //
+// TOP MODULE : -- //
+// //
+// PURPOSE : 4-bit up counter test bench //
+// //
+// DESIGNER : Deepak Kumar Tala //
+// //
+// Revision History //
+// //
+// DEVELOPMENT HISTORY : //
+// Rev0.0 : Jan 03, 2003 //
+// Initial Revision //
+// //
+///////////////////////////////////////////////////////////////////////////
+module testbench;
+
+integer file;
+reg clk = 0, reset = 0, enable = 0;
+wire [3:0] count;
+reg dut_error = 0;
+
+counter U0 (
+.clk (clk),
+.reset (reset),
+.enable (enable),
+.count (count)
+);
+
+event reset_enable;
+event terminate_sim;
+
+initial
+ file = $fopen(`outfile);
+
+always
+ #5 clk = !clk;
+
+initial
+@ (terminate_sim) begin
+ $fdisplay (file, "Terminating simulation");
+ if (dut_error == 0) begin
+ $fdisplay (file, "Simulation Result : PASSED");
+ end
+ else begin
+ $fdisplay (file, "Simulation Result : FAILED");
+ end
+ $fdisplay (file, "###################################################");
+ #1 $finish;
+end
+
+
+
+event reset_done;
+
+initial
+forever begin
+ @ (reset_enable);
+ @ (negedge clk)
+ $fdisplay (file, "Applying reset");
+ reset = 1;
+ @ (negedge clk)
+ reset = 0;
+ $fdisplay (file, "Came out of Reset");
+ -> reset_done;
+end
+
+initial begin
+ #10 -> reset_enable;
+ @ (reset_done);
+ @ (negedge clk);
+ enable = 1;
+ repeat (5)
+ begin
+ @ (negedge clk);
+ end
+ enable = 0;
+ #5 -> terminate_sim;
+end
+
+
+reg [3:0] count_compare;
+
+always @ (posedge clk)
+if (reset == 1'b1)
+ count_compare <= 0;
+else if ( enable == 1'b1)
+ count_compare <= count_compare + 1;
+
+
+
+always @ (negedge clk)
+if (count_compare != count) begin
+ $fdisplay (file, "DUT ERROR AT TIME%d",$time);
+ $fdisplay (file, "Expected value %d, Got Value %d", count_compare, count);
+ dut_error = 1;
+ #5 -> terminate_sim;
+end
+
+endmodule
+
diff --git a/tests/asicworld/code_verilog_tutorial_d_ff.v b/tests/asicworld/code_verilog_tutorial_d_ff.v
new file mode 100644
index 00000000..7a408360
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_d_ff.v
@@ -0,0 +1,14 @@
+// D flip-flop Code
+module d_ff ( d, clk, q, q_bar);
+input d ,clk;
+output q, q_bar;
+wire d ,clk;
+reg q, q_bar;
+
+always @ (posedge clk)
+begin
+ q <= d;
+ q_bar <= !d;
+end
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_decoder.v b/tests/asicworld/code_verilog_tutorial_decoder.v
new file mode 100644
index 00000000..5efdbd7e
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_decoder.v
@@ -0,0 +1,14 @@
+module decoder (in,out);
+input [2:0] in;
+output [7:0] out;
+wire [7:0] out;
+assign out = (in == 3'b000 ) ? 8'b0000_0001 :
+(in == 3'b001 ) ? 8'b0000_0010 :
+(in == 3'b010 ) ? 8'b0000_0100 :
+(in == 3'b011 ) ? 8'b0000_1000 :
+(in == 3'b100 ) ? 8'b0001_0000 :
+(in == 3'b101 ) ? 8'b0010_0000 :
+(in == 3'b110 ) ? 8'b0100_0000 :
+(in == 3'b111 ) ? 8'b1000_0000 : 8'h00;
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_decoder_always.v b/tests/asicworld/code_verilog_tutorial_decoder_always.v
new file mode 100644
index 00000000..4418ec70
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_decoder_always.v
@@ -0,0 +1,20 @@
+module decoder_always (in,out);
+input [2:0] in;
+output [7:0] out;
+reg [7:0] out;
+
+always @ (in)
+begin
+ out = 0;
+ case (in)
+ 3'b001 : out = 8'b0000_0001;
+ 3'b010 : out = 8'b0000_0010;
+ 3'b011 : out = 8'b0000_0100;
+ 3'b100 : out = 8'b0000_1000;
+ 3'b101 : out = 8'b0001_0000;
+ 3'b110 : out = 8'b0100_0000;
+ 3'b111 : out = 8'b1000_0000;
+ endcase
+end
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_escape_id.v b/tests/asicworld/code_verilog_tutorial_escape_id.v
new file mode 100644
index 00000000..6c33da17
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_escape_id.v
@@ -0,0 +1,14 @@
+// There must be white space after the
+// string which uses escape character
+module \1dff (
+q, // Q output
+\q~ , // Q_out output
+d, // D input
+cl$k, // CLOCK input
+\reset* // Reset input
+);
+
+input d, cl$k, \reset* ;
+output q, \q~ ;
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_explicit.v b/tests/asicworld/code_verilog_tutorial_explicit.v
new file mode 100644
index 00000000..88427ff0
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_explicit.v
@@ -0,0 +1,35 @@
+module explicit();
+reg clk,d,rst,pre;
+wire q;
+
+// Here q_bar is not connected
+// We can connect ports in any order
+dff u0 (
+.q (q),
+.d (d),
+.clk (clk),
+.q_bar (),
+.rst (rst),
+.pre (pre)
+);
+
+endmodule
+
+// D fli-flop
+module dff (q, q_bar, clk, d, rst, pre);
+input clk, d, rst, pre;
+output q, q_bar;
+reg q;
+
+assign q_bar = ~q;
+
+always @ (posedge clk)
+if (rst == 1'b1) begin
+ q <= 0;
+end else if (pre == 1'b1) begin
+ q <= 1;
+end else begin
+ q <= d;
+end
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_first_counter.v b/tests/asicworld/code_verilog_tutorial_first_counter.v
new file mode 100644
index 00000000..d35d4aac
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_first_counter.v
@@ -0,0 +1,47 @@
+//-----------------------------------------------------
+// This is my second Verilog Design
+// Design Name : first_counter
+// File Name : first_counter.v
+// Function : This is a 4 bit up-counter with
+// Synchronous active high reset and
+// with active high enable signal
+//-----------------------------------------------------
+module first_counter (
+clock , // Clock input of the design
+reset , // active high, synchronous Reset input
+enable , // Active high enable signal for counter
+counter_out // 4 bit vector output of the counter
+); // End of port list
+//-------------Input Ports-----------------------------
+input clock ;
+input reset ;
+input enable ;
+//-------------Output Ports----------------------------
+output [3:0] counter_out ;
+//-------------Input ports Data Type-------------------
+// By rule all the input ports should be wires
+wire clock ;
+wire reset ;
+wire enable ;
+//-------------Output Ports Data Type------------------
+// Output port can be a storage element (reg) or a wire
+reg [3:0] counter_out ;
+
+//------------Code Starts Here-------------------------
+// Since this counter is a positive edge trigged one,
+// We trigger the below block with respect to positive
+// edge of the clock.
+always @ (posedge clock)
+begin : COUNTER // Block Name
+ // At every rising edge of clock we check if reset is active
+ // If active, we load the counter output with 4'b0000
+ if (reset == 1'b1) begin
+ counter_out <= 4'b0000;
+ end
+ // If enable is active, then we increment the counter
+ else if (enable == 1'b1) begin
+ counter_out <= counter_out + 1;
+ end
+end // End of Block COUNTER
+
+endmodule // End of Module counter
diff --git a/tests/asicworld/code_verilog_tutorial_first_counter_tb.v b/tests/asicworld/code_verilog_tutorial_first_counter_tb.v
new file mode 100644
index 00000000..806e1773
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_first_counter_tb.v
@@ -0,0 +1,37 @@
+module testbench();
+// Declare inputs as regs and outputs as wires
+reg clock = 1, reset = 0, enable = 0;
+wire [3:0] counter_out;
+integer file;
+
+// Initialize all variables
+initial begin
+ file = $fopen(`outfile);
+ $fdisplay (file, "time\t clk reset enable counter");
+ #5 reset = 1; // Assert the reset
+ #10 reset = 0; // De-assert the reset
+ #10 enable = 1; // Assert enable
+ #100 enable = 0; // De-assert enable
+ #5 $finish; // Terminate simulation
+end
+
+always @(negedge clock)
+ $fdisplay (file, "%g\t %b %b %b %b",
+ $time, clock, reset, enable, counter_out);
+
+// Clock generator
+initial begin
+ #1;
+ forever
+ #5 clock = ~clock; // Toggle clock every 5 ticks
+end
+
+// Connect DUT to test bench
+first_counter U_counter (
+clock,
+reset,
+enable,
+counter_out
+);
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_flip_flop.v b/tests/asicworld/code_verilog_tutorial_flip_flop.v
new file mode 100644
index 00000000..ed2e88c2
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_flip_flop.v
@@ -0,0 +1,15 @@
+module flif_flop (clk,reset, q, d);
+input clk, reset, d;
+output q;
+reg q;
+
+always @ (posedge clk )
+begin
+ if (reset == 1) begin
+ q <= 0;
+ end else begin
+ q <= d;
+ end
+end
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_fsm_full.v b/tests/asicworld/code_verilog_tutorial_fsm_full.v
new file mode 100644
index 00000000..fd2d559b
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_fsm_full.v
@@ -0,0 +1,114 @@
+module fsm_full(
+clock , // Clock
+reset , // Active high reset
+req_0 , // Active high request from agent 0
+req_1 , // Active high request from agent 1
+req_2 , // Active high request from agent 2
+req_3 , // Active high request from agent 3
+gnt_0 , // Active high grant to agent 0
+gnt_1 , // Active high grant to agent 1
+gnt_2 , // Active high grant to agent 2
+gnt_3 // Active high grant to agent 3
+);
+// Port declaration here
+input clock ; // Clock
+input reset ; // Active high reset
+input req_0 ; // Active high request from agent 0
+input req_1 ; // Active high request from agent 1
+input req_2 ; // Active high request from agent 2
+input req_3 ; // Active high request from agent 3
+output gnt_0 ; // Active high grant to agent 0
+output gnt_1 ; // Active high grant to agent 1
+output gnt_2 ; // Active high grant to agent 2
+output gnt_3 ; // Active high grant to agent
+
+// Internal Variables
+reg gnt_0 ; // Active high grant to agent 0
+reg gnt_1 ; // Active high grant to agent 1
+reg gnt_2 ; // Active high grant to agent 2
+reg gnt_3 ; // Active high grant to agent
+
+parameter [2:0] IDLE = 3'b000;
+parameter [2:0] GNT0 = 3'b001;
+parameter [2:0] GNT1 = 3'b010;
+parameter [2:0] GNT2 = 3'b011;
+parameter [2:0] GNT3 = 3'b100;
+
+reg [2:0] state, next_state;
+
+always @ (state or req_0 or req_1 or req_2 or req_3)
+begin
+ next_state = 0;
+ case(state)
+ IDLE : if (req_0 == 1'b1) begin
+ next_state = GNT0;
+ end else if (req_1 == 1'b1) begin
+ next_state= GNT1;
+ end else if (req_2 == 1'b1) begin
+ next_state= GNT2;
+ end else if (req_3 == 1'b1) begin
+ next_state= GNT3;
+ end else begin
+ next_state = IDLE;
+ end
+ GNT0 : if (req_0 == 1'b0) begin
+ next_state = IDLE;
+ end else begin
+ next_state = GNT0;
+ end
+ GNT1 : if (req_1 == 1'b0) begin
+ next_state = IDLE;
+ end else begin
+ next_state = GNT1;
+ end
+ GNT2 : if (req_2 == 1'b0) begin
+ next_state = IDLE;
+ end else begin
+ next_state = GNT2;
+ end
+ GNT3 : if (req_3 == 1'b0) begin
+ next_state = IDLE;
+ end else begin
+ next_state = GNT3;
+ end
+ default : next_state = IDLE;
+ endcase
+end
+
+always @ (posedge clock)
+begin : OUTPUT_LOGIC
+ if (reset) begin
+ gnt_0 <= 1'b0;
+ gnt_1 <= 1'b0;
+ gnt_2 <= 1'b0;
+ gnt_3 <= 1'b0;
+ state <= IDLE;
+ end else begin
+ state <= next_state;
+ case(state)
+ IDLE : begin
+ gnt_0 <= 1'b0;
+ gnt_1 <= 1'b0;
+ gnt_2 <= 1'b0;
+ gnt_3 <= 1'b0;
+ end
+ GNT0 : begin
+ gnt_0 <= 1'b1;
+ end
+ GNT1 : begin
+ gnt_1 <= 1'b1;
+ end
+ GNT2 : begin
+ gnt_2 <= 1'b1;
+ end
+ GNT3 : begin
+ gnt_3 <= 1'b1;
+ end
+ default : begin
+ state <= IDLE;
+ end
+ endcase
+ end
+end
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_fsm_full_tb.v b/tests/asicworld/code_verilog_tutorial_fsm_full_tb.v
new file mode 100644
index 00000000..a8e15568
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_fsm_full_tb.v
@@ -0,0 +1,54 @@
+module testbench();
+reg clock = 0 , reset ;
+reg req_0 , req_1 , req_2 , req_3;
+wire gnt_0 , gnt_1 , gnt_2 , gnt_3 ;
+integer file;
+
+initial begin
+ // $dumpfile("testbench.vcd");
+ // $dumpvars(0, testbench);
+ file = $fopen(`outfile);
+ $fdisplay(file, "Time\t R0 R1 R2 R3 G0 G1 G2 G3");
+ clock = 0;
+ reset = 1;
+ req_0 = 0;
+ req_1 = 0;
+ req_2 = 0;
+ req_3 = 0;
+ #10 reset = 1;
+ #10 reset = 0;
+ #10 req_0 = 1;
+ #20 req_0 = 0;
+ #10 req_1 = 1;
+ #20 req_1 = 0;
+ #10 req_2 = 1;
+ #20 req_2 = 0;
+ #10 req_3 = 1;
+ #20 req_3 = 0;
+ #10 $finish;
+end
+
+always @(negedge clock)
+ $fdisplay(file, "%g\t %b %b %b %b %b %b %b %b",
+ $time, req_0, req_1, req_2, req_3, gnt_0, gnt_1, gnt_2, gnt_3);
+
+initial begin
+ #1;
+ forever
+ #2 clock = ~clock;
+end
+
+fsm_full U_fsm_full(
+clock , // Clock
+reset , // Active high reset
+req_0 , // Active high request from agent 0
+req_1 , // Active high request from agent 1
+req_2 , // Active high request from agent 2
+req_3 , // Active high request from agent 3
+gnt_0 , // Active high grant to agent 0
+gnt_1 , // Active high grant to agent 1
+gnt_2 , // Active high grant to agent 2
+gnt_3 // Active high grant to agent 3
+);
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_good_code.v b/tests/asicworld/code_verilog_tutorial_good_code.v
new file mode 100644
index 00000000..6ba77644
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_good_code.v
@@ -0,0 +1,18 @@
+ module addbit (
+ a,
+ b,
+ ci,
+ sum,
+ co);
+ input a;
+ input b;
+ input ci;
+ output sum;
+ output co;
+ wire a;
+ wire b;
+ wire ci;
+ wire sum;
+ wire co;
+
+ endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_if_else.v b/tests/asicworld/code_verilog_tutorial_if_else.v
new file mode 100644
index 00000000..19b91d3f
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_if_else.v
@@ -0,0 +1,13 @@
+module if_else();
+
+reg dff;
+wire clk,din,reset;
+
+always @ (posedge clk)
+if (reset) begin
+ dff <= 0;
+end else begin
+ dff <= din;
+end
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_multiply.v b/tests/asicworld/code_verilog_tutorial_multiply.v
new file mode 100644
index 00000000..1912e1e2
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_multiply.v
@@ -0,0 +1,8 @@
+module muliply (a,product);
+ input [3:0] a;
+ output [4:0] product;
+ wire [4:0] product;
+
+ assign product = a << 1;
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_mux_21.v b/tests/asicworld/code_verilog_tutorial_mux_21.v
new file mode 100644
index 00000000..a6a0d35e
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_mux_21.v
@@ -0,0 +1,9 @@
+module mux_21 (a,b,sel,y);
+ input a, b;
+ output y;
+ input sel;
+ wire y;
+
+ assign y = (sel) ? b : a;
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_n_out_primitive.v b/tests/asicworld/code_verilog_tutorial_n_out_primitive.v
new file mode 100644
index 00000000..814385a4
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_n_out_primitive.v
@@ -0,0 +1,13 @@
+module n_out_primitive();
+
+wire out,out_0,out_1,out_2,out_3,out_a,out_b,out_c;
+wire in;
+
+// one output Buffer gate
+buf u_buf0 (out,in);
+// four output Buffer gate
+buf u_buf1 (out_0, out_1, out_2, out_3, in);
+// three output Invertor gate
+not u_not0 (out_a, out_b, out_c, in);
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_parallel_if.v b/tests/asicworld/code_verilog_tutorial_parallel_if.v
new file mode 100644
index 00000000..1dbe737e
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_parallel_if.v
@@ -0,0 +1,21 @@
+module parallel_if();
+
+reg [3:0] counter;
+wire clk,reset,enable, up_en, down_en;
+
+always @ (posedge clk)
+// If reset is asserted
+if (reset == 1'b0) begin
+ counter <= 4'b0000;
+end else begin
+ // If counter is enable and up count is mode
+ if (enable == 1'b1 && up_en == 1'b1) begin
+ counter <= counter + 1'b1;
+ end
+ // If counter is enable and down count is mode
+ if (enable == 1'b1 && down_en == 1'b1) begin
+ counter <= counter - 1'b1;
+ end
+end
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_parity.v b/tests/asicworld/code_verilog_tutorial_parity.v
new file mode 100644
index 00000000..764396c2
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_parity.v
@@ -0,0 +1,41 @@
+//-----------------------------------------------------
+// This is simple parity Program
+// Design Name : parity
+// File Name : parity.v
+// Function : This program shows how a verilog
+// primitive/module port connection are done
+// Coder : Deepak
+//-----------------------------------------------------
+module parity (
+a , // First input
+b , // Second input
+c , // Third Input
+d , // Fourth Input
+y // Parity output
+);
+
+// Input Declaration
+input a ;
+input b ;
+input c ;
+input d ;
+// Ouput Declaration
+output y ;
+// port data types
+wire a ;
+wire b ;
+wire c ;
+wire d ;
+wire y ;
+// Internal variables
+wire out_0 ;
+wire out_1 ;
+
+// Code starts Here
+xor u0 (out_0,a,b);
+
+xor u1 (out_1,c,d);
+
+xor u2 (y,out_0,out_1);
+
+endmodule // End Of Module parity
diff --git a/tests/asicworld/code_verilog_tutorial_simple_function.v b/tests/asicworld/code_verilog_tutorial_simple_function.v
new file mode 100644
index 00000000..5818a1d4
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_simple_function.v
@@ -0,0 +1,10 @@
+module simple_function();
+
+function myfunction;
+input a, b, c, d;
+begin
+ myfunction = ((a+b) + (c-d));
+end
+endfunction
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_simple_if.v b/tests/asicworld/code_verilog_tutorial_simple_if.v
new file mode 100644
index 00000000..a68cc4a8
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_simple_if.v
@@ -0,0 +1,11 @@
+module simple_if();
+
+reg latch;
+wire enable,din;
+
+always @ (enable or din)
+if (enable) begin
+ latch <= din;
+end
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_task_global.v b/tests/asicworld/code_verilog_tutorial_task_global.v
new file mode 100644
index 00000000..3ae86279
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_task_global.v
@@ -0,0 +1,12 @@
+module task_global();
+
+reg [7:0] temp_out;
+reg [7:0] temp_in;
+
+task convert;
+begin
+ temp_out = (9/5) *( temp_in + 32);
+end
+endtask
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_tri_buf.v b/tests/asicworld/code_verilog_tutorial_tri_buf.v
new file mode 100644
index 00000000..a55b29ca
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_tri_buf.v
@@ -0,0 +1,9 @@
+module tri_buf (a,b,enable);
+ input a;
+ output b;
+ input enable;
+ wire b;
+
+assign b = (enable) ? a : 1'bz;
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_v2k_reg.v b/tests/asicworld/code_verilog_tutorial_v2k_reg.v
new file mode 100644
index 00000000..537a9e85
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_v2k_reg.v
@@ -0,0 +1,24 @@
+module v2k_reg();
+
+// v2k allows to init variables
+reg a = 0;
+// Here only last variable is set to 0, i.e d = 0
+// Rest b, c are set to x
+reg b, c, d = 0;
+// reg data type can be signed in v2k
+// We can assign with signed constants
+reg signed [7:0] data = 8'shF0;
+
+// Function can return signed values
+// Its ports can contain signed ports
+function signed [7:0] adder;
+ input a_in;
+ input b_in;
+ input c_in;
+ input signed [7:0] data_in;
+ begin
+ adder = a_in + b_in + c_in + data_in;
+ end
+endfunction
+
+endmodule
diff --git a/tests/asicworld/code_verilog_tutorial_which_clock.v b/tests/asicworld/code_verilog_tutorial_which_clock.v
new file mode 100644
index 00000000..418a2cfa
--- /dev/null
+++ b/tests/asicworld/code_verilog_tutorial_which_clock.v
@@ -0,0 +1,12 @@
+module which_clock (x,y,q,d);
+input x,y,d;
+output q;
+reg q;
+
+always @ (posedge x or posedge y)
+ if (x)
+ q <= 1'b0;
+ else
+ q <= d;
+
+endmodule
diff --git a/tests/asicworld/run-test.sh b/tests/asicworld/run-test.sh
new file mode 100755
index 00000000..d5708c45
--- /dev/null
+++ b/tests/asicworld/run-test.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+OPTIND=1
+seed="" # default to no seed specified
+while getopts "S:" opt
+do
+ case "$opt" in
+ S) arg="${OPTARG#"${OPTARG%%[![:space:]]*}"}" # remove leading space
+ seed="SEED=$arg" ;;
+ esac
+done
+shift "$((OPTIND-1))"
+
+exec ${MAKE:-make} -f ../tools/autotest.mk $seed EXTRA_FLAGS="-e" *.v
diff --git a/tests/bram/.gitignore b/tests/bram/.gitignore
new file mode 100644
index 00000000..9c595a6f
--- /dev/null
+++ b/tests/bram/.gitignore
@@ -0,0 +1 @@
+temp
diff --git a/tests/bram/generate.py b/tests/bram/generate.py
new file mode 100644
index 00000000..def0b23c
--- /dev/null
+++ b/tests/bram/generate.py
@@ -0,0 +1,287 @@
+#!/usr/bin/env python3
+
+import argparse
+import os
+import sys
+import random
+
+debug_mode = False
+
+def create_bram(dsc_f, sim_f, ref_f, tb_f, k1, k2, or_next):
+ while True:
+ init = 0 # random.randrange(2)
+ abits = random.randrange(1, 8)
+ dbits = random.randrange(1, 8)
+ groups = random.randrange(2, 5)
+
+ if random.randrange(2):
+ abits = 2 ** random.randrange(1, 4)
+ if random.randrange(2):
+ dbits = 2 ** random.randrange(1, 4)
+
+ while True:
+ wrmode = [ random.randrange(0, 2) for i in range(groups) ]
+ if wrmode.count(1) == 0: continue
+ if wrmode.count(0) == 0: continue
+ break
+
+ if random.randrange(2):
+ maxpol = 4
+ maxtransp = 1
+ maxclocks = 4
+ else:
+ maxpol = None
+ clkpol = random.randrange(4)
+ maxtransp = 2
+ maxclocks = 1
+
+ def generate_enable(i):
+ if wrmode[i]:
+ v = 2 ** random.randrange(0, 4)
+ while dbits < v or dbits % v != 0:
+ v //= 2
+ return v
+ return 0
+
+ def generate_transp(i):
+ if wrmode[i] == 0:
+ return random.randrange(maxtransp)
+ return 0
+
+ def generate_clkpol(i):
+ if maxpol is None:
+ return clkpol
+ return random.randrange(maxpol)
+
+ ports = [ random.randrange(1, 3) for i in range(groups) ]
+ enable = [ generate_enable(i) for i in range(groups) ]
+ transp = [ generate_transp(i) for i in range(groups) ]
+ clocks = [ random.randrange(maxclocks)+1 for i in range(groups) ]
+ clkpol = [ generate_clkpol(i) for i in range(groups) ]
+ break
+
+ print("bram bram_%02d_%02d" % (k1, k2), file=dsc_f)
+ print(" init %d" % init, file=dsc_f)
+ print(" abits %d" % abits, file=dsc_f)
+ print(" dbits %d" % dbits, file=dsc_f)
+ print(" groups %d" % groups, file=dsc_f)
+ print(" ports %s" % " ".join(["%d" % i for i in ports]), file=dsc_f)
+ print(" wrmode %s" % " ".join(["%d" % i for i in wrmode]), file=dsc_f)
+ print(" enable %s" % " ".join(["%d" % i for i in enable]), file=dsc_f)
+ print(" transp %s" % " ".join(["%d" % i for i in transp]), file=dsc_f)
+ print(" clocks %s" % " ".join(["%d" % i for i in clocks]), file=dsc_f)
+ print(" clkpol %s" % " ".join(["%d" % i for i in clkpol]), file=dsc_f)
+ print("endbram", file=dsc_f)
+ print("match bram_%02d_%02d" % (k1, k2), file=dsc_f)
+ if random.randrange(2):
+ non_zero_enables = [chr(ord('A') + i) for i in range(len(enable)) if enable[i]]
+ if len(non_zero_enables):
+ print(" shuffle_enable %c" % random.choice(non_zero_enables), file=dsc_f)
+ if or_next:
+ print(" or_next_if_better", file=dsc_f)
+ print("endmatch", file=dsc_f)
+
+ states = set()
+ v_ports = set()
+ v_stmts = list()
+ v_always = dict()
+
+ tb_decls = list()
+ tb_clocks = list()
+ tb_addr = list()
+ tb_din = list()
+ tb_dout = list()
+ tb_addrlist = list()
+
+ for i in range(10):
+ tb_addrlist.append(random.randrange(1048576))
+
+ t = random.randrange(1048576)
+ for i in range(10):
+ tb_addrlist.append(t ^ (1 << i))
+
+ v_stmts.append("(* nomem2reg *) reg [%d:0] memory [0:%d];" % (dbits-1, 2**abits-1))
+
+ portindex = 0
+ last_always_hdr = (-1, "")
+
+ for p1 in range(groups):
+ for p2 in range(ports[p1]):
+ pf = "%c%d" % (chr(ord("A") + p1), p2 + 1)
+ portindex += 1
+
+ v_stmts.append("`ifndef SYNTHESIS")
+ v_stmts.append(" event UPDATE_%s;" % pf)
+ v_stmts.append("`endif")
+
+ if clocks[p1] and not ("CLK%d" % clocks[p1]) in v_ports:
+ v_ports.add("CLK%d" % clocks[p1])
+ v_stmts.append("input CLK%d;" % clocks[p1])
+ tb_decls.append("reg CLK%d = 0;" % clocks[p1])
+ tb_clocks.append("CLK%d" % clocks[p1])
+
+ v_ports.add("%sADDR" % pf)
+ v_stmts.append("input [%d:0] %sADDR;" % (abits-1, pf))
+ if transp[p1]:
+ v_stmts.append("reg [%d:0] %sADDR_Q;" % (abits-1, pf))
+ tb_decls.append("reg [%d:0] %sADDR;" % (abits-1, pf))
+ tb_addr.append("%sADDR" % pf)
+
+ v_ports.add("%sDATA" % pf)
+ v_stmts.append("%s [%d:0] %sDATA;" % ("input" if wrmode[p1] else "output reg", dbits-1, pf))
+
+ if wrmode[p1]:
+ tb_decls.append("reg [%d:0] %sDATA;" % (dbits-1, pf))
+ tb_din.append("%sDATA" % pf)
+ else:
+ tb_decls.append("wire [%d:0] %sDATA;" % (dbits-1, pf))
+ tb_decls.append("wire [%d:0] %sDATA_R;" % (dbits-1, pf))
+ tb_dout.append("%sDATA" % pf)
+
+ if wrmode[p1] and enable[p1]:
+ v_ports.add("%sEN" % pf)
+ v_stmts.append("input [%d:0] %sEN;" % (enable[p1]-1, pf))
+ tb_decls.append("reg [%d:0] %sEN;" % (enable[p1]-1, pf))
+ tb_din.append("%sEN" % pf)
+
+ assign_op = "<="
+ if clocks[p1] == 0:
+ always_hdr = "always @* begin"
+ assign_op = "="
+ elif clkpol[p1] == 0:
+ always_hdr = "always @(negedge CLK%d) begin" % clocks[p1]
+ elif clkpol[p1] == 1:
+ always_hdr = "always @(posedge CLK%d) begin" % clocks[p1]
+ else:
+ if not ("CP", clkpol[p1]) in states:
+ v_stmts.append("parameter CLKPOL%d = 0;" % clkpol[p1])
+ states.add(("CP", clkpol[p1]))
+ if not ("CPW", clocks[p1], clkpol[p1]) in states:
+ v_stmts.append("wire CLK%d_CLKPOL%d = CLK%d == CLKPOL%d;" % (clocks[p1], clkpol[p1], clocks[p1], clkpol[p1]))
+ states.add(("CPW", clocks[p1], clkpol[p1]))
+ always_hdr = "always @(posedge CLK%d_CLKPOL%d) begin" % (clocks[p1], clkpol[p1])
+
+ if last_always_hdr[1] != always_hdr:
+ last_always_hdr = (portindex, always_hdr)
+ v_always[last_always_hdr] = list()
+
+ if wrmode[p1]:
+ for i in range(enable[p1]):
+ enrange = "[%d:%d]" % ((i+1)*dbits/enable[p1]-1, i*dbits/enable[p1])
+ v_always[last_always_hdr].append((portindex, pf, "if (%sEN[%d]) memory[%sADDR]%s = %sDATA%s;" % (pf, i, pf, enrange, pf, enrange)))
+ elif transp[p1]:
+ v_always[last_always_hdr].append((sum(ports)+1, pf, "%sADDR_Q %s %sADDR;" % (pf, assign_op, pf)))
+ v_stmts.append("always @* %sDATA = memory[%sADDR_Q];" % (pf, pf))
+ else:
+ v_always[last_always_hdr].append((0, pf, "%sDATA %s memory[%sADDR];" % (pf, assign_op, pf)))
+
+ for always_hdr in sorted(v_always):
+ v_stmts.append(always_hdr[1])
+ triggered_events = set()
+ time_cursor = 0
+ v_always[always_hdr].sort()
+ for t, p, s in v_always[always_hdr]:
+ if time_cursor != t or not p in triggered_events:
+ v_stmts.append(" `ifndef SYNTHESIS")
+ stmt = ""
+ if time_cursor != t:
+ stmt += " #%d;" % (t-time_cursor)
+ time_cursor = t
+ if not p in triggered_events:
+ stmt += (" -> UPDATE_%s;" % p)
+ triggered_events.add(p)
+ v_stmts.append(" %s" % stmt)
+ v_stmts.append(" `endif")
+ v_stmts.append(" %s" % s)
+ v_stmts.append("end")
+
+ print("module bram_%02d_%02d(%s);" % (k1, k2, ", ".join(v_ports)), file=sim_f)
+ for stmt in v_stmts:
+ print(" %s" % stmt, file=sim_f)
+ print("endmodule", file=sim_f)
+
+ print("module bram_%02d_%02d_ref(%s);" % (k1, k2, ", ".join(v_ports)), file=ref_f)
+ for stmt in v_stmts:
+ print(" %s" % stmt, file=ref_f)
+ print("endmodule", file=ref_f)
+
+ print("module bram_%02d_%02d_tb;" % (k1, k2), file=tb_f)
+ for stmt in tb_decls:
+ print(" %s" % stmt, file=tb_f)
+ print(" bram_%02d_%02d uut (" % (k1, k2), file=tb_f)
+ print(" " + ",\n ".join([".%s(%s)" % (p, p) for p in (tb_clocks + tb_addr + tb_din + tb_dout)]), file=tb_f)
+ print(" );", file=tb_f)
+ print(" bram_%02d_%02d_ref ref (" % (k1, k2), file=tb_f)
+ print(" " + ",\n ".join([".%s(%s)" % (p, p) for p in (tb_clocks + tb_addr + tb_din)]) + ",", file=tb_f)
+ print(" " + ",\n ".join([".%s(%s_R)" % (p, p) for p in tb_dout]), file=tb_f)
+ print(" );", file=tb_f)
+
+ expr_dout = "{%s}" % ", ".join(tb_dout)
+ expr_dout_ref = "{%s}" % ", ".join(i + "_R" for i in tb_dout)
+
+ print(" wire error = %s !== %s;" % (expr_dout, expr_dout_ref), file=tb_f)
+
+ print(" initial begin", file=tb_f)
+
+ if debug_mode:
+ print(" $dumpfile(`vcd_file);", file=tb_f)
+ print(" $dumpvars(0, bram_%02d_%02d_tb);" % (k1, k2), file=tb_f)
+ print(" #%d;" % (1000 + k2), file=tb_f)
+
+ for p in (tb_clocks + tb_addr + tb_din):
+ if p[-2:] == "EN":
+ print(" %s <= ~0;" % p, file=tb_f)
+ else:
+ print(" %s <= 0;" % p, file=tb_f)
+ print(" #1000;", file=tb_f)
+
+ for v in [1, 0, 1, 0]:
+ for p in tb_clocks:
+ print(" %s = %d;" % (p, v), file=tb_f)
+ print(" #1000;", file=tb_f)
+
+ for i in range(20 if debug_mode else 100):
+ if len(tb_clocks):
+ c = random.choice(tb_clocks)
+ print(" %s = !%s;" % (c, c), file=tb_f)
+ print(" #100;", file=tb_f)
+ print(" $display(\"bram_%02d_%02d %3d: %%b %%b %%s\", %s, %s, error ? \"ERROR\" : \"OK\");" %
+ (k1, k2, i, expr_dout, expr_dout_ref), file=tb_f)
+ for p in tb_din:
+ print(" %s <= %d;" % (p, random.randrange(1048576)), file=tb_f)
+ for p in tb_addr:
+ print(" %s <= %d;" % (p, random.choice(tb_addrlist)), file=tb_f)
+ print(" #900;", file=tb_f)
+
+ print(" end", file=tb_f)
+ print("endmodule", file=tb_f)
+
+parser = argparse.ArgumentParser(formatter_class = argparse.ArgumentDefaultsHelpFormatter)
+parser.add_argument('-S', '--seed', type = int, help = 'seed for PRNG')
+parser.add_argument('-c', '--count', type = int, default = 5, help = 'number of test cases to generate')
+parser.add_argument('-d', '--debug', action='store_true')
+args = parser.parse_args()
+
+debug_mode = args.debug
+
+if args.seed is not None:
+ seed = args.seed
+else:
+ seed = (int(os.times()[4]*100) + os.getpid()) % 900000 + 100000
+
+print("PRNG seed: %d" % seed)
+random.seed(seed)
+
+for k1 in range(args.count):
+ dsc_f = open("temp/brams_%02d.txt" % k1, "w")
+ sim_f = open("temp/brams_%02d.v" % k1, "w")
+ ref_f = open("temp/brams_%02d_ref.v" % k1, "w")
+ tb_f = open("temp/brams_%02d_tb.v" % k1, "w")
+
+ for f in [sim_f, ref_f, tb_f]:
+ print("`timescale 1 ns / 1 ns", file=f)
+
+ lenk2 = 1 if debug_mode else 10
+ for k2 in range(lenk2):
+ create_bram(dsc_f, sim_f, ref_f, tb_f, k1, k2, random.randrange(2 if k2+1 < lenk2 else 1))
+
diff --git a/tests/bram/run-single.sh b/tests/bram/run-single.sh
new file mode 100644
index 00000000..98a45b61
--- /dev/null
+++ b/tests/bram/run-single.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+set -e
+../../yosys -qq -p "proc; opt; memory -nomap -bram temp/brams_${2}.txt; opt -fast -full" \
+ -l temp/synth_${1}_${2}.log -o temp/synth_${1}_${2}.v temp/brams_${1}.v
+iverilog -Dvcd_file=\"temp/tb_${1}_${2}.vcd\" -DSIMLIB_MEMDELAY=1 -o temp/tb_${1}_${2}.tb temp/brams_${1}_tb.v \
+ temp/brams_${1}_ref.v temp/synth_${1}_${2}.v temp/brams_${2}.v ../../techlibs/common/simlib.v
+temp/tb_${1}_${2}.tb > temp/tb_${1}_${2}.txt
+if grep -q ERROR temp/tb_${1}_${2}.txt; then
+ grep -HC2 ERROR temp/tb_${1}_${2}.txt | head
+ exit 1
+fi
+exit 0
diff --git a/tests/bram/run-test.sh b/tests/bram/run-test.sh
new file mode 100755
index 00000000..d6ba0de4
--- /dev/null
+++ b/tests/bram/run-test.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+# run this test many times:
+# MAKE="make -j8" time bash -c 'for ((i=0; i<100; i++)); do echo "-- $i --"; bash run-test.sh || exit 1; done'
+
+set -e
+
+OPTIND=1
+count=5
+seed="" # default to no seed specified
+debug=""
+while getopts "c:dS:" opt
+do
+ case "$opt" in
+ c) count="$OPTARG" ;;
+ d) debug="-d" ;;
+ S) seed="-S $OPTARG" ;;
+ esac
+done
+shift "$((OPTIND-1))"
+
+rm -rf temp
+mkdir -p temp
+
+echo "generating tests.."
+python3 generate.py $debug -c $count $seed
+
+{
+ echo -n "all:"
+ for i in $( ls temp/brams_*.txt | sed 's,.*_,,; s,\..*,,;' ); do
+ for j in $( ls temp/brams_*.txt | sed 's,.*_,,; s,\..*,,;' | grep -v $i ); do
+ echo -n " temp/job_${i}_${j}.ok"
+ done; done
+ echo
+ for i in $( ls temp/brams_*.txt | sed 's,.*_,,; s,\..*,,;' ); do
+ for j in $( ls temp/brams_*.txt | sed 's,.*_,,; s,\..*,,;' | grep -v $i ); do
+ echo "temp/job_${i}_${j}.ok:"
+ echo " @bash run-single.sh ${i} ${j}"
+ echo " @echo 'Passed memory_bram test ${i}_${j}.'"
+ echo " @touch \$@"
+ done; done
+} > temp/makefile
+
+echo "running tests.."
+${MAKE:-make} -f temp/makefile
+
+exit 0
diff --git a/tests/fsm/.gitignore b/tests/fsm/.gitignore
new file mode 100644
index 00000000..9c595a6f
--- /dev/null
+++ b/tests/fsm/.gitignore
@@ -0,0 +1 @@
+temp
diff --git a/tests/fsm/generate.py b/tests/fsm/generate.py
new file mode 100644
index 00000000..c8eda0cd
--- /dev/null
+++ b/tests/fsm/generate.py
@@ -0,0 +1,118 @@
+#!/usr/bin/env python3
+
+import argparse
+import sys
+import random
+from contextlib import contextmanager
+
+# set to 'True' to compare verific with yosys
+test_verific = False
+
+@contextmanager
+def redirect_stdout(new_target):
+ old_target, sys.stdout = sys.stdout, new_target
+ try:
+ yield new_target
+ finally:
+ sys.stdout = old_target
+
+def random_expr(variables):
+ c = random.choice(['bin', 'uni', 'var', 'const'])
+ if c == 'bin':
+ op = random.choice(['+', '-', '*', '<', '<=', '==', '!=', '>=', '>', '<<', '>>', '<<<', '>>>', '|', '&', '^', '~^', '||', '&&'])
+ return "(%s %s %s)" % (random_expr(variables), op, random_expr(variables))
+ if c == 'uni':
+ op = random.choice(['+', '-', '~', '|', '&', '^', '~^', '!', '$signed', '$unsigned'])
+ return "%s(%s)" % (op, random_expr(variables))
+ if c == 'var':
+ return random.choice(variables)
+ if c == 'const':
+ bits = random.randint(1, 32)
+ return "%d'd%s" % (bits, random.randint(0, 2**bits-1))
+ raise AssertionError
+
+parser = argparse.ArgumentParser(formatter_class = argparse.ArgumentDefaultsHelpFormatter)
+parser.add_argument('-S', '--seed', type = int, help = 'seed for PRNG')
+parser.add_argument('-c', '--count', type = int, default = 50, help = 'number of test cases to generate')
+args = parser.parse_args()
+
+if args.seed is not None:
+ print("PRNG seed: %d" % args.seed)
+ random.seed(args.seed)
+
+for idx in range(args.count):
+ with open('temp/uut_%05d.v' % idx, 'w') as f:
+ with redirect_stdout(f):
+ rst2 = random.choice([False, True])
+ if rst2:
+ print('module uut_%05d(clk, rst1, rst2, rst, a, b, c, x, y, z);' % (idx))
+ print(' input clk, rst1, rst2;')
+ print(' output rst;')
+ print(' assign rst = rst1 || rst2;')
+ else:
+ print('module uut_%05d(clk, rst, a, b, c, x, y, z);' % (idx))
+ print(' input clk, rst;')
+ variables=['a', 'b', 'c', 'x', 'y', 'z']
+ print(' input%s [%d:0] a;' % (random.choice(['', ' signed']), random.randint(0, 31)))
+ print(' input%s [%d:0] b;' % (random.choice(['', ' signed']), random.randint(0, 31)))
+ print(' input%s [%d:0] c;' % (random.choice(['', ' signed']), random.randint(0, 31)))
+ print(' output reg%s [%d:0] x;' % (random.choice(['', ' signed']), random.randint(0, 31)))
+ print(' output reg%s [%d:0] y;' % (random.choice(['', ' signed']), random.randint(0, 31)))
+ print(' output reg%s [%d:0] z;' % (random.choice(['', ' signed']), random.randint(0, 31)))
+ state_bits = random.randint(5, 16);
+ print(' %sreg [%d:0] state;' % (random.choice(['', '(* fsm_encoding = "one-hot" *)',
+ '(* fsm_encoding = "binary" *)']), state_bits-1))
+ states=[]
+ for i in range(random.randint(2, 10)):
+ n = random.randint(0, 2**state_bits-1)
+ if n not in states:
+ states.append(n)
+ print(' always @(posedge clk) begin')
+ print(' if (%s) begin' % ('rst1' if rst2 else 'rst'))
+ print(' x <= %d;' % random.randint(0, 2**31-1))
+ print(' y <= %d;' % random.randint(0, 2**31-1))
+ print(' z <= %d;' % random.randint(0, 2**31-1))
+ print(' state <= %d;' % random.choice(states))
+ print(' end else begin')
+ print(' case (state)')
+ for state in states:
+ print(' %d: begin' % state)
+ for var in ('x', 'y', 'z'):
+ print(' %s <= %s;' % (var, random_expr(variables)))
+ next_states = states[:]
+ for i in range(random.randint(0, len(states))):
+ next_state = random.choice(next_states)
+ next_states.remove(next_state)
+ print(' if ((%s) %s (%s)) state <= %s;' % (random_expr(variables),
+ random.choice(['<', '<=', '>=', '>']), random_expr(variables), next_state))
+ print(' end')
+ print(' endcase')
+ if rst2:
+ print(' if (rst2) begin')
+ print(' x <= a;')
+ print(' y <= b;')
+ print(' z <= c;')
+ print(' state <= %d;' % random.choice(states))
+ print(' end')
+ print(' end')
+ print(' end')
+ print('endmodule')
+ with open('temp/uut_%05d.ys' % idx, 'w') as f:
+ with redirect_stdout(f):
+ if test_verific:
+ print('read_verilog temp/uut_%05d.v' % idx)
+ print('proc;; rename uut_%05d gold' % idx)
+ print('verific -vlog2k temp/uut_%05d.v' % idx)
+ print('verific -import uut_%05d' % idx)
+ print('rename uut_%05d gate' % idx)
+ else:
+ print('read_verilog temp/uut_%05d.v' % idx)
+ print('proc;;')
+ print('copy uut_%05d gold' % idx)
+ print('rename uut_%05d gate' % idx)
+ print('cd gate')
+ print('opt; wreduce; share%s; opt; fsm;;' % random.choice(['', ' -aggressive']))
+ print('cd ..')
+ print('miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp gold gate miter')
+ print('sat -verify-no-timeout -timeout 20 -seq 5 -set-at 1 %s_rst 1 -prove trigger 0 -prove-skip 1 -show-inputs -show-outputs miter' % ('gold' if rst2 else 'in'))
+
diff --git a/tests/fsm/run-test.sh b/tests/fsm/run-test.sh
new file mode 100755
index 00000000..cf506470
--- /dev/null
+++ b/tests/fsm/run-test.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+# run this test many times:
+# time bash -c 'for ((i=0; i<100; i++)); do echo "-- $i --"; bash run-test.sh || exit 1; done'
+
+set -e
+
+OPTIND=1
+count=100
+seed="" # default to no seed specified
+while getopts "c:S:" opt
+do
+ case "$opt" in
+ c) count="$OPTARG" ;;
+ S) seed="-S $OPTARG" ;;
+ esac
+done
+shift "$((OPTIND-1))"
+
+rm -rf temp
+mkdir -p temp
+echo "generating tests.."
+python3 generate.py -c $count $seed
+
+{
+ all_targets="all_targets:"
+ echo "all: all_targets"
+ echo " @echo"
+ for i in $( ls temp/*.ys | sed 's,[^0-9],,g; s,^0*\(.\),\1,g;' ); do
+ idx=$( printf "%05d" $i )
+ echo "temp/uut_${idx}.log: temp/uut_${idx}.ys temp/uut_${idx}.v"
+ echo " @echo -n '[$i]'"
+ echo " @../../yosys -ql temp/uut_${idx}.out temp/uut_${idx}.ys"
+ echo " @mv temp/uut_${idx}.out temp/uut_${idx}.log"
+ echo " @grep -q 'SAT proof finished' temp/uut_${idx}.log && echo -n K || echo -n T"
+ all_targets="$all_targets temp/uut_${idx}.log"
+ done
+ echo "$all_targets"
+} > temp/makefile
+
+echo "running tests.."
+${MAKE:-make} -f temp/makefile
+
+exit 0
diff --git a/tests/hana/.gitignore b/tests/hana/.gitignore
new file mode 100644
index 00000000..073f4615
--- /dev/null
+++ b/tests/hana/.gitignore
@@ -0,0 +1,2 @@
+*.log
+*.out
diff --git a/tests/hana/README b/tests/hana/README
new file mode 100644
index 00000000..2081fb10
--- /dev/null
+++ b/tests/hana/README
@@ -0,0 +1,4 @@
+
+These test cases are copied from the hana project:
+https://sourceforge.net/projects/sim-sim/
+
diff --git a/tests/hana/hana_vlib.v b/tests/hana/hana_vlib.v
new file mode 100644
index 00000000..fc82f138
--- /dev/null
+++ b/tests/hana/hana_vlib.v
@@ -0,0 +1,1139 @@
+/*
+Copyright (C) 2009-2010 Parvez Ahmad
+Written by Parvez Ahmad <parvez_ahmad@yahoo.co.uk>.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+
+module BUF (input in, output out);
+
+assign out = in;
+
+endmodule
+
+module TRIBUF(input in, enable, output out);
+
+assign out = enable ? in : 1'bz;
+
+endmodule
+
+module INV(input in, output out);
+
+assign out = ~in;
+
+endmodule
+
+module AND2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out);
+
+assign out = &in;
+
+endmodule
+
+module AND3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out);
+
+assign out = &in;
+
+endmodule
+
+module AND4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out);
+
+assign out = &in;
+
+endmodule
+
+module OR2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out);
+
+assign out = |in;
+
+endmodule
+
+module OR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out);
+
+assign out = |in;
+
+endmodule
+
+module OR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out);
+
+assign out = |in;
+
+endmodule
+
+
+module NAND2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out);
+
+assign out = ~&in;
+
+endmodule
+
+module NAND3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out);
+
+assign out = ~&in;
+
+endmodule
+
+module NAND4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out);
+
+assign out = ~&in;
+
+endmodule
+
+module NOR2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out);
+
+assign out = ~|in;
+
+endmodule
+
+module NOR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out);
+
+assign out = ~|in;
+
+endmodule
+
+module NOR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out);
+
+assign out = ~|in;
+
+endmodule
+
+
+module XOR2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out);
+
+assign out = ^in;
+
+endmodule
+
+module XOR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out);
+
+assign out = ^in;
+
+endmodule
+
+module XOR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out);
+
+assign out = ^in;
+
+endmodule
+
+
+module XNOR2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out);
+
+assign out = ~^in;
+
+endmodule
+
+module XNOR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out);
+
+assign out = ~^in;
+
+endmodule
+
+module XNOR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out);
+
+assign out = ~^in;
+
+endmodule
+
+module DEC1 (input in, enable, output reg [1:0] out);
+
+always @(in or enable)
+ if(!enable)
+ out = 2'b00;
+ else begin
+ case (in)
+ 1'b0 : out = 2'b01;
+ 1'b1 : out = 2'b10;
+ endcase
+ end
+endmodule
+
+module DEC2 (input [1:0] in, input enable, output reg [3:0] out);
+
+always @(in or enable)
+ if(!enable)
+ out = 4'b0000;
+ else begin
+ case (in)
+ 2'b00 : out = 4'b0001;
+ 2'b01 : out = 4'b0010;
+ 2'b10 : out = 4'b0100;
+ 2'b11 : out = 4'b1000;
+ endcase
+ end
+endmodule
+
+module DEC3 (input [2:0] in, input enable, output reg [7:0] out);
+
+always @(in or enable)
+ if(!enable)
+ out = 8'b00000000;
+ else begin
+ case (in)
+ 3'b000 : out = 8'b00000001;
+ 3'b001 : out = 8'b00000010;
+ 3'b010 : out = 8'b00000100;
+ 3'b011 : out = 8'b00001000;
+ 3'b100 : out = 8'b00010000;
+ 3'b101 : out = 8'b00100000;
+ 3'b110 : out = 8'b01000000;
+ 3'b111 : out = 8'b10000000;
+ endcase
+ end
+endmodule
+
+module DEC4 (input [3:0] in, input enable, output reg [15:0] out);
+
+always @(in or enable)
+ if(!enable)
+ out = 16'b0000000000000000;
+ else begin
+ case (in)
+ 4'b0000 : out = 16'b0000000000000001;
+ 4'b0001 : out = 16'b0000000000000010;
+ 4'b0010 : out = 16'b0000000000000100;
+ 4'b0011 : out = 16'b0000000000001000;
+ 4'b0100 : out = 16'b0000000000010000;
+ 4'b0101 : out = 16'b0000000000100000;
+ 4'b0110 : out = 16'b0000000001000000;
+ 4'b0111 : out = 16'b0000000010000000;
+ 4'b1000 : out = 16'b0000000100000000;
+ 4'b1001 : out = 16'b0000001000000000;
+ 4'b1010 : out = 16'b0000010000000000;
+ 4'b1011 : out = 16'b0000100000000000;
+ 4'b1100 : out = 16'b0001000000000000;
+ 4'b1101 : out = 16'b0010000000000000;
+ 4'b1110 : out = 16'b0100000000000000;
+ 4'b1111 : out = 16'b1000000000000000;
+ endcase
+ end
+endmodule
+module DEC5 (input [4:0] in, input enable, output reg [31:0] out);
+
+always @(in or enable)
+ if(!enable)
+ out = 32'b00000000000000000000000000000000;
+ else begin
+ case (in)
+ 5'b00000 : out = 32'b00000000000000000000000000000001;
+ 5'b00001 : out = 32'b00000000000000000000000000000010;
+ 5'b00010 : out = 32'b00000000000000000000000000000100;
+ 5'b00011 : out = 32'b00000000000000000000000000001000;
+ 5'b00100 : out = 32'b00000000000000000000000000010000;
+ 5'b00101 : out = 32'b00000000000000000000000000100000;
+ 5'b00110 : out = 32'b00000000000000000000000001000000;
+ 5'b00111 : out = 32'b00000000000000000000000010000000;
+ 5'b01000 : out = 32'b00000000000000000000000100000000;
+ 5'b01001 : out = 32'b00000000000000000000001000000000;
+ 5'b01010 : out = 32'b00000000000000000000010000000000;
+ 5'b01011 : out = 32'b00000000000000000000100000000000;
+ 5'b01100 : out = 32'b00000000000000000001000000000000;
+ 5'b01101 : out = 32'b00000000000000000010000000000000;
+ 5'b01110 : out = 32'b00000000000000000100000000000000;
+ 5'b01111 : out = 32'b00000000000000001000000000000000;
+ 5'b10000 : out = 32'b00000000000000010000000000000000;
+ 5'b10001 : out = 32'b00000000000000100000000000000000;
+ 5'b10010 : out = 32'b00000000000001000000000000000000;
+ 5'b10011 : out = 32'b00000000000010000000000000000000;
+ 5'b10100 : out = 32'b00000000000100000000000000000000;
+ 5'b10101 : out = 32'b00000000001000000000000000000000;
+ 5'b10110 : out = 32'b00000000010000000000000000000000;
+ 5'b10111 : out = 32'b00000000100000000000000000000000;
+ 5'b11000 : out = 32'b00000001000000000000000000000000;
+ 5'b11001 : out = 32'b00000010000000000000000000000000;
+ 5'b11010 : out = 32'b00000100000000000000000000000000;
+ 5'b11011 : out = 32'b00001000000000000000000000000000;
+ 5'b11100 : out = 32'b00010000000000000000000000000000;
+ 5'b11101 : out = 32'b00100000000000000000000000000000;
+ 5'b11110 : out = 32'b01000000000000000000000000000000;
+ 5'b11111 : out = 32'b10000000000000000000000000000000;
+ endcase
+ end
+endmodule
+
+module DEC6 (input [5:0] in, input enable, output reg [63:0] out);
+
+always @(in or enable)
+ if(!enable)
+ out = 64'b0000000000000000000000000000000000000000000000000000000000000000;
+ else begin
+ case (in)
+ 6'b000000 : out = 64'b0000000000000000000000000000000000000000000000000000000000000001;
+ 6'b000001 : out = 64'b0000000000000000000000000000000000000000000000000000000000000010;
+ 6'b000010 : out = 64'b0000000000000000000000000000000000000000000000000000000000000100;
+ 6'b000011 : out = 64'b0000000000000000000000000000000000000000000000000000000000001000;
+ 6'b000100 : out = 64'b0000000000000000000000000000000000000000000000000000000000010000;
+ 6'b000101 : out = 64'b0000000000000000000000000000000000000000000000000000000000100000;
+ 6'b000110 : out = 64'b0000000000000000000000000000000000000000000000000000000001000000;
+ 6'b000111 : out = 64'b0000000000000000000000000000000000000000000000000000000010000000;
+ 6'b001000 : out = 64'b0000000000000000000000000000000000000000000000000000000100000000;
+ 6'b001001 : out = 64'b0000000000000000000000000000000000000000000000000000001000000000;
+ 6'b001010 : out = 64'b0000000000000000000000000000000000000000000000000000010000000000;
+ 6'b001011 : out = 64'b0000000000000000000000000000000000000000000000000000100000000000;
+ 6'b001100 : out = 64'b0000000000000000000000000000000000000000000000000001000000000000;
+ 6'b001101 : out = 64'b0000000000000000000000000000000000000000000000000010000000000000;
+ 6'b001110 : out = 64'b0000000000000000000000000000000000000000000000000100000000000000;
+ 6'b001111 : out = 64'b0000000000000000000000000000000000000000000000001000000000000000;
+ 6'b010000 : out = 64'b0000000000000000000000000000000000000000000000010000000000000000;
+ 6'b010001 : out = 64'b0000000000000000000000000000000000000000000000100000000000000000;
+ 6'b010010 : out = 64'b0000000000000000000000000000000000000000000001000000000000000000;
+ 6'b010011 : out = 64'b0000000000000000000000000000000000000000000010000000000000000000;
+ 6'b010100 : out = 64'b0000000000000000000000000000000000000000000100000000000000000000;
+ 6'b010101 : out = 64'b0000000000000000000000000000000000000000001000000000000000000000;
+ 6'b010110 : out = 64'b0000000000000000000000000000000000000000010000000000000000000000;
+ 6'b010111 : out = 64'b0000000000000000000000000000000000000000100000000000000000000000;
+ 6'b011000 : out = 64'b0000000000000000000000000000000000000001000000000000000000000000;
+ 6'b011001 : out = 64'b0000000000000000000000000000000000000010000000000000000000000000;
+ 6'b011010 : out = 64'b0000000000000000000000000000000000000100000000000000000000000000;
+ 6'b011011 : out = 64'b0000000000000000000000000000000000001000000000000000000000000000;
+ 6'b011100 : out = 64'b0000000000000000000000000000000000010000000000000000000000000000;
+ 6'b011101 : out = 64'b0000000000000000000000000000000000100000000000000000000000000000;
+ 6'b011110 : out = 64'b0000000000000000000000000000000001000000000000000000000000000000;
+ 6'b011111 : out = 64'b0000000000000000000000000000000010000000000000000000000000000000;
+
+ 6'b100000 : out = 64'b0000000000000000000000000000000100000000000000000000000000000000;
+ 6'b100001 : out = 64'b0000000000000000000000000000001000000000000000000000000000000000;
+ 6'b100010 : out = 64'b0000000000000000000000000000010000000000000000000000000000000000;
+ 6'b100011 : out = 64'b0000000000000000000000000000100000000000000000000000000000000000;
+ 6'b100100 : out = 64'b0000000000000000000000000001000000000000000000000000000000000000;
+ 6'b100101 : out = 64'b0000000000000000000000000010000000000000000000000000000000000000;
+ 6'b100110 : out = 64'b0000000000000000000000000100000000000000000000000000000000000000;
+ 6'b100111 : out = 64'b0000000000000000000000001000000000000000000000000000000000000000;
+ 6'b101000 : out = 64'b0000000000000000000000010000000000000000000000000000000000000000;
+ 6'b101001 : out = 64'b0000000000000000000000100000000000000000000000000000000000000000;
+ 6'b101010 : out = 64'b0000000000000000000001000000000000000000000000000000000000000000;
+ 6'b101011 : out = 64'b0000000000000000000010000000000000000000000000000000000000000000;
+ 6'b101100 : out = 64'b0000000000000000000100000000000000000000000000000000000000000000;
+ 6'b101101 : out = 64'b0000000000000000001000000000000000000000000000000000000000000000;
+ 6'b101110 : out = 64'b0000000000000000010000000000000000000000000000000000000000000000;
+ 6'b101111 : out = 64'b0000000000000000100000000000000000000000000000000000000000000000;
+ 6'b110000 : out = 64'b0000000000000001000000000000000000000000000000000000000000000000;
+ 6'b110001 : out = 64'b0000000000000010000000000000000000000000000000000000000000000000;
+ 6'b110010 : out = 64'b0000000000000100000000000000000000000000000000000000000000000000;
+ 6'b110011 : out = 64'b0000000000001000000000000000000000000000000000000000000000000000;
+ 6'b110100 : out = 64'b0000000000010000000000000000000000000000000000000000000000000000;
+ 6'b110101 : out = 64'b0000000000100000000000000000000000000000000000000000000000000000;
+ 6'b110110 : out = 64'b0000000001000000000000000000000000000000000000000000000000000000;
+ 6'b110111 : out = 64'b0000000010000000000000000000000000000000000000000000000000000000;
+ 6'b111000 : out = 64'b0000000100000000000000000000000000000000000000000000000000000000;
+ 6'b111001 : out = 64'b0000001000000000000000000000000000000000000000000000000000000000;
+ 6'b111010 : out = 64'b0000010000000000000000000000000000000000000000000000000000000000;
+ 6'b111011 : out = 64'b0000100000000000000000000000000000000000000000000000000000000000;
+ 6'b111100 : out = 64'b0001000000000000000000000000000000000000000000000000000000000000;
+ 6'b111101 : out = 64'b0010000000000000000000000000000000000000000000000000000000000000;
+ 6'b111110 : out = 64'b0100000000000000000000000000000000000000000000000000000000000000;
+ 6'b111111 : out = 64'b1000000000000000000000000000000000000000000000000000000000000000;
+ endcase
+ end
+endmodule
+
+
+module MUX2(input [1:0] in, input select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ endcase
+endmodule
+
+
+module MUX4(input [3:0] in, input [1:0] select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ 2: out = in[2];
+ 3: out = in[3];
+ endcase
+endmodule
+
+
+module MUX8(input [7:0] in, input [2:0] select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ 2: out = in[2];
+ 3: out = in[3];
+ 4: out = in[4];
+ 5: out = in[5];
+ 6: out = in[6];
+ 7: out = in[7];
+ endcase
+endmodule
+
+module MUX16(input [15:0] in, input [3:0] select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ 2: out = in[2];
+ 3: out = in[3];
+ 4: out = in[4];
+ 5: out = in[5];
+ 6: out = in[6];
+ 7: out = in[7];
+ 8: out = in[8];
+ 9: out = in[9];
+ 10: out = in[10];
+ 11: out = in[11];
+ 12: out = in[12];
+ 13: out = in[13];
+ 14: out = in[14];
+ 15: out = in[15];
+ endcase
+endmodule
+
+module MUX32(input [31:0] in, input [4:0] select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ 2: out = in[2];
+ 3: out = in[3];
+ 4: out = in[4];
+ 5: out = in[5];
+ 6: out = in[6];
+ 7: out = in[7];
+ 8: out = in[8];
+ 9: out = in[9];
+ 10: out = in[10];
+ 11: out = in[11];
+ 12: out = in[12];
+ 13: out = in[13];
+ 14: out = in[14];
+ 15: out = in[15];
+ 16: out = in[16];
+ 17: out = in[17];
+ 18: out = in[18];
+ 19: out = in[19];
+ 20: out = in[20];
+ 21: out = in[21];
+ 22: out = in[22];
+ 23: out = in[23];
+ 24: out = in[24];
+ 25: out = in[25];
+ 26: out = in[26];
+ 27: out = in[27];
+ 28: out = in[28];
+ 29: out = in[29];
+ 30: out = in[30];
+ 31: out = in[31];
+ endcase
+endmodule
+
+module MUX64(input [63:0] in, input [5:0] select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ 2: out = in[2];
+ 3: out = in[3];
+ 4: out = in[4];
+ 5: out = in[5];
+ 6: out = in[6];
+ 7: out = in[7];
+ 8: out = in[8];
+ 9: out = in[9];
+ 10: out = in[10];
+ 11: out = in[11];
+ 12: out = in[12];
+ 13: out = in[13];
+ 14: out = in[14];
+ 15: out = in[15];
+ 16: out = in[16];
+ 17: out = in[17];
+ 18: out = in[18];
+ 19: out = in[19];
+ 20: out = in[20];
+ 21: out = in[21];
+ 22: out = in[22];
+ 23: out = in[23];
+ 24: out = in[24];
+ 25: out = in[25];
+ 26: out = in[26];
+ 27: out = in[27];
+ 28: out = in[28];
+ 29: out = in[29];
+ 30: out = in[30];
+ 31: out = in[31];
+ 32: out = in[32];
+ 33: out = in[33];
+ 34: out = in[34];
+ 35: out = in[35];
+ 36: out = in[36];
+ 37: out = in[37];
+ 38: out = in[38];
+ 39: out = in[39];
+ 40: out = in[40];
+ 41: out = in[41];
+ 42: out = in[42];
+ 43: out = in[43];
+ 44: out = in[44];
+ 45: out = in[45];
+ 46: out = in[46];
+ 47: out = in[47];
+ 48: out = in[48];
+ 49: out = in[49];
+ 50: out = in[50];
+ 51: out = in[51];
+ 52: out = in[52];
+ 53: out = in[53];
+ 54: out = in[54];
+ 55: out = in[55];
+ 56: out = in[56];
+ 57: out = in[57];
+ 58: out = in[58];
+ 59: out = in[59];
+ 60: out = in[60];
+ 61: out = in[61];
+ 62: out = in[62];
+ 63: out = in[63];
+ endcase
+endmodule
+
+module ADD1(input in1, in2, cin, output out, cout);
+
+assign {cout, out} = in1 + in2 + cin;
+
+endmodule
+
+module ADD2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 + in2 + cin;
+
+endmodule
+
+module ADD4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 + in2 + cin;
+
+endmodule
+
+module ADD8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 + in2 + cin;
+
+endmodule
+
+module ADD16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 + in2 + cin;
+
+endmodule
+
+module ADD32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 + in2 + cin;
+
+endmodule
+module ADD64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 + in2 + cin;
+
+endmodule
+
+module SUB1(input in1, in2, cin, output out, cout);
+
+assign {cout, out} = in1 - in2 - cin;
+
+endmodule
+
+module SUB2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 - in2 - cin;
+
+endmodule
+
+module SUB4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 - in2 - cin;
+
+endmodule
+
+module SUB8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 - in2 - cin;
+
+endmodule
+
+module SUB16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 - in2 - cin;
+
+endmodule
+
+module SUB32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 - in2 - cin;
+
+endmodule
+module SUB64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2,
+ input cin, output [SIZE-1:0] out, output cout);
+
+assign {cout, out} = in1 - in2 - cin;
+
+endmodule
+
+module MUL1 #(parameter SIZE = 1)(input in1, in2, output [2*SIZE-1:0] out);
+
+assign out = in1*in2;
+
+endmodule
+
+module MUL2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out);
+
+assign out = in1*in2;
+
+endmodule
+
+module MUL4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out);
+
+assign out = in1*in2;
+
+endmodule
+
+module MUL8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out);
+
+assign out = in1*in2;
+
+endmodule
+
+module MUL16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out);
+
+assign out = in1*in2;
+
+endmodule
+
+module MUL32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out);
+
+assign out = in1*in2;
+
+endmodule
+
+module MUL64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out);
+
+assign out = in1*in2;
+
+endmodule
+
+module DIV1 #(parameter SIZE = 1)(input in1, in2, output out, rem);
+
+assign out = in1/in2;
+assign rem = in1%in2;
+
+endmodule
+
+module DIV2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2,
+ output [SIZE-1:0] out, rem);
+
+assign out = in1/in2;
+assign rem = in1%in2;
+
+endmodule
+
+module DIV4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2,
+ output [SIZE-1:0] out, rem);
+
+assign out = in1/in2;
+assign rem = in1%in2;
+
+endmodule
+
+module DIV8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2,
+ output [SIZE-1:0] out, rem);
+
+assign out = in1/in2;
+assign rem = in1%in2;
+
+endmodule
+
+module DIV16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2,
+ output [SIZE-1:0] out, rem);
+
+assign out = in1/in2;
+assign rem = in1%in2;
+
+endmodule
+
+module DIV32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2,
+ output [SIZE-1:0] out, rem);
+
+assign out = in1/in2;
+assign rem = in1%in2;
+
+endmodule
+
+module DIV64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2,
+ output [SIZE-1:0] out, rem);
+
+assign out = in1/in2;
+assign rem = in1%in2;
+
+endmodule
+
+module FF (input d, clk, output reg q);
+always @( posedge clk)
+ q <= d;
+endmodule
+
+
+module RFF(input d, clk, reset, output reg q);
+always @(posedge clk or posedge reset)
+ if(reset)
+ q <= 0;
+ else
+ q <= d;
+endmodule
+
+module SFF(input d, clk, set, output reg q);
+always @(posedge clk or posedge set)
+ if(set)
+ q <= 1;
+ else
+ q <= d;
+endmodule
+
+module RSFF(input d, clk, set, reset, output reg q);
+always @(posedge clk or posedge reset or posedge set)
+ if(reset)
+ q <= 0;
+ else if(set)
+ q <= 1;
+ else
+ q <= d;
+endmodule
+
+module SRFF(input d, clk, set, reset, output reg q);
+always @(posedge clk or posedge set or posedge reset)
+ if(set)
+ q <= 1;
+ else if(reset)
+ q <= 0;
+ else
+ q <= d;
+endmodule
+
+module LATCH(input d, enable, output reg q);
+always @( d or enable)
+ if(enable)
+ q <= d;
+endmodule
+
+module RLATCH(input d, reset, enable, output reg q);
+always @( d or enable or reset)
+ if(enable)
+ if(reset)
+ q <= 0;
+ else
+ q <= d;
+endmodule
+
+module LSHIFT1 #(parameter SIZE = 1)(input in, shift, val, output reg out);
+
+always @ (in, shift, val) begin
+ if(shift)
+ out = val;
+ else
+ out = in;
+end
+
+endmodule
+
+
+module LSHIFT2 #(parameter SIZE = 2)(input [SIZE-1:0] in,
+ input [SIZE-1:0] shift, input val,
+ output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in << shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift));
+end
+endmodule
+
+module LSHIFT4 #(parameter SIZE = 4)(input [SIZE-1:0] in,
+ input [2:0] shift, input val, output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in << shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift));
+end
+endmodule
+
+
+module LSHIFT8 #(parameter SIZE = 8)(input [SIZE-1:0] in,
+ input [3:0] shift, input val, output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in << shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift));
+end
+endmodule
+
+module LSHIFT16 #(parameter SIZE = 16)(input [SIZE-1:0] in,
+ input [4:0] shift, input val, output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in << shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift));
+end
+endmodule
+
+module LSHIFT32 #(parameter SIZE = 32)(input [SIZE-1:0] in,
+ input [5:0] shift, input val, output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in << shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift));
+end
+endmodule
+
+module LSHIFT64 #(parameter SIZE = 64)(input [SIZE-1:0] in,
+ input [6:0] shift, input val, output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in << shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift));
+end
+endmodule
+
+module RSHIFT1 #(parameter SIZE = 1)(input in, shift, val, output reg out);
+
+always @ (in, shift, val) begin
+ if(shift)
+ out = val;
+ else
+ out = in;
+end
+
+endmodule
+
+module RSHIFT2 #(parameter SIZE = 2)(input [SIZE-1:0] in,
+ input [SIZE-1:0] shift, input val,
+ output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in >> shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift));
+end
+
+endmodule
+
+
+module RSHIFT4 #(parameter SIZE = 4)(input [SIZE-1:0] in,
+ input [2:0] shift, input val,
+ output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in >> shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift));
+end
+endmodule
+
+module RSHIFT8 #(parameter SIZE = 8)(input [SIZE-1:0] in,
+ input [3:0] shift, input val,
+ output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in >> shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift));
+end
+
+endmodule
+
+module RSHIFT16 #(parameter SIZE = 16)(input [SIZE-1:0] in,
+ input [4:0] shift, input val,
+ output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in >> shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift));
+end
+endmodule
+
+
+module RSHIFT32 #(parameter SIZE = 32)(input [SIZE-1:0] in,
+ input [5:0] shift, input val,
+ output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in >> shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift));
+end
+endmodule
+
+module RSHIFT64 #(parameter SIZE = 64)(input [SIZE-1:0] in,
+ input [6:0] shift, input val,
+ output reg [SIZE-1:0] out);
+
+always @(in or shift or val) begin
+ out = in >> shift;
+ if(val)
+ out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift));
+end
+endmodule
+
+module CMP1 #(parameter SIZE = 1) (input in1, in2,
+ output reg equal, unequal, greater, lesser);
+
+always @ (in1 or in2) begin
+ if(in1 == in2) begin
+ equal = 1;
+ unequal = 0;
+ greater = 0;
+ lesser = 0;
+ end
+ else begin
+ equal = 0;
+ unequal = 1;
+
+ if(in1 < in2) begin
+ greater = 0;
+ lesser = 1;
+ end
+ else begin
+ greater = 1;
+ lesser = 0;
+ end
+ end
+end
+endmodule
+
+
+module CMP2 #(parameter SIZE = 2) (input [SIZE-1:0] in1, in2,
+ output reg equal, unequal, greater, lesser);
+
+always @ (in1 or in2) begin
+ if(in1 == in2) begin
+ equal = 1;
+ unequal = 0;
+ greater = 0;
+ lesser = 0;
+ end
+ else begin
+ equal = 0;
+ unequal = 1;
+
+ if(in1 < in2) begin
+ greater = 0;
+ lesser = 1;
+ end
+ else begin
+ greater = 1;
+ lesser = 0;
+ end
+ end
+end
+endmodule
+
+module CMP4 #(parameter SIZE = 4) (input [SIZE-1:0] in1, in2,
+ output reg equal, unequal, greater, lesser);
+
+always @ (in1 or in2) begin
+ if(in1 == in2) begin
+ equal = 1;
+ unequal = 0;
+ greater = 0;
+ lesser = 0;
+ end
+ else begin
+ equal = 0;
+ unequal = 1;
+
+ if(in1 < in2) begin
+ greater = 0;
+ lesser = 1;
+ end
+ else begin
+ greater = 1;
+ lesser = 0;
+ end
+ end
+end
+endmodule
+
+module CMP8 #(parameter SIZE = 8) (input [SIZE-1:0] in1, in2,
+ output reg equal, unequal, greater, lesser);
+
+always @ (in1 or in2) begin
+ if(in1 == in2) begin
+ equal = 1;
+ unequal = 0;
+ greater = 0;
+ lesser = 0;
+ end
+ else begin
+ equal = 0;
+ unequal = 1;
+
+ if(in1 < in2) begin
+ greater = 0;
+ lesser = 1;
+ end
+ else begin
+ greater = 1;
+ lesser = 0;
+ end
+ end
+end
+endmodule
+
+module CMP16 #(parameter SIZE = 16) (input [SIZE-1:0] in1, in2,
+ output reg equal, unequal, greater, lesser);
+
+always @ (in1 or in2) begin
+ if(in1 == in2) begin
+ equal = 1;
+ unequal = 0;
+ greater = 0;
+ lesser = 0;
+ end
+ else begin
+ equal = 0;
+ unequal = 1;
+
+ if(in1 < in2) begin
+ greater = 0;
+ lesser = 1;
+ end
+ else begin
+ greater = 1;
+ lesser = 0;
+ end
+ end
+end
+endmodule
+
+module CMP32 #(parameter SIZE = 32) (input [SIZE-1:0] in1, in2,
+ output reg equal, unequal, greater, lesser);
+
+always @ (in1 or in2) begin
+ if(in1 == in2) begin
+ equal = 1;
+ unequal = 0;
+ greater = 0;
+ lesser = 0;
+ end
+ else begin
+ equal = 0;
+ unequal = 1;
+
+ if(in1 < in2) begin
+ greater = 0;
+ lesser = 1;
+ end
+ else begin
+ greater = 1;
+ lesser = 0;
+ end
+ end
+end
+endmodule
+
+module CMP64 #(parameter SIZE = 64) (input [SIZE-1:0] in1, in2,
+ output reg equal, unequal, greater, lesser);
+
+always @ (in1 or in2) begin
+ if(in1 == in2) begin
+ equal = 1;
+ unequal = 0;
+ greater = 0;
+ lesser = 0;
+ end
+ else begin
+ equal = 0;
+ unequal = 1;
+
+ if(in1 < in2) begin
+ greater = 0;
+ lesser = 1;
+ end
+ else begin
+ greater = 1;
+ lesser = 0;
+ end
+ end
+end
+endmodule
+
+module VCC (output supply1 out);
+endmodule
+
+module GND (output supply0 out);
+endmodule
+
+
+module INC1 #(parameter SIZE = 1) (input in, output [SIZE:0] out);
+
+assign out = in + 1;
+
+endmodule
+
+module INC2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output [SIZE:0] out);
+
+assign out = in + 1;
+
+endmodule
+
+module INC4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output [SIZE:0] out);
+assign out = in + 1;
+
+endmodule
+
+module INC8 #(parameter SIZE = 8) (input [SIZE-1:0] in, output [SIZE:0] out);
+assign out = in + 1;
+
+endmodule
+
+module INC16 #(parameter SIZE = 16) (input [SIZE-1:0] in, output [SIZE:0] out);
+assign out = in + 1;
+
+endmodule
+
+module INC32 #(parameter SIZE = 32) (input [SIZE-1:0] in, output [SIZE:0] out);
+assign out = in + 1;
+
+endmodule
+module INC64 #(parameter SIZE = 64) (input [SIZE-1:0] in, output [SIZE:0] out);
+assign out = in + 1;
+
+endmodule
+
diff --git a/tests/hana/run-test.sh b/tests/hana/run-test.sh
new file mode 100755
index 00000000..878c80b3
--- /dev/null
+++ b/tests/hana/run-test.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+OPTIND=1
+seed="" # default to no seed specified
+while getopts "S:" opt
+do
+ case "$opt" in
+ S) arg="${OPTARG#"${OPTARG%%[![:space:]]*}"}" # remove leading space
+ seed="SEED=$arg" ;;
+ esac
+done
+shift "$((OPTIND-1))"
+
+exec ${MAKE:-make} -f ../tools/autotest.mk $seed EXTRA_FLAGS="-l hana_vlib.v -n 300 -e" test_*.v
diff --git a/tests/hana/test_intermout.v b/tests/hana/test_intermout.v
new file mode 100644
index 00000000..88b91ee4
--- /dev/null
+++ b/tests/hana/test_intermout.v
@@ -0,0 +1,418 @@
+
+// test_intermout_always_comb_1_test.v
+module f1_test(a, b, c, d, z);
+input a, b, c, d;
+output z;
+reg z, temp1, temp2;
+
+always @(a or b or c or d)
+begin
+ temp1 = a ^ b;
+ temp2 = c ^ d;
+ z = temp1 ^ temp2;
+end
+
+endmodule
+
+// test_intermout_always_comb_3_test.v
+module f2_test (in1, in2, out);
+input in1, in2;
+output reg out;
+
+always @ ( in1 or in2)
+ if(in1 > in2)
+ out = in1;
+ else
+ out = in2;
+endmodule
+
+// test_intermout_always_comb_4_test.v
+module f3_test(a, b, c);
+input b, c;
+output reg a;
+
+always @(b or c) begin
+a = b;
+a = c;
+end
+endmodule
+
+// test_intermout_always_comb_5_test.v
+module f4_test(ctrl, in1, in2, out);
+input ctrl;
+input in1, in2;
+output reg out;
+
+always @ (ctrl or in1 or in2)
+ if(ctrl)
+ out = in1 & in2;
+ else
+ out = in1 | in2;
+endmodule
+
+// test_intermout_always_ff_3_test.v
+module f5_NonBlockingEx(clk, merge, er, xmit, fddi, claim);
+input clk, merge, er, xmit, fddi;
+output reg claim;
+reg fcr;
+
+always @(posedge clk)
+begin
+ fcr = er | xmit;
+
+ if(merge)
+ claim = fcr & fddi;
+ else
+ claim = fddi;
+end
+endmodule
+
+// test_intermout_always_ff_4_test.v
+module f6_FlipFlop(clk, cs, ns);
+input clk;
+input [31:0] cs;
+output [31:0] ns;
+integer is;
+
+always @(posedge clk)
+ is <= cs;
+
+assign ns = is;
+endmodule
+
+// test_intermout_always_ff_5_test.v
+module f7_FlipFlop(clock, cs, ns);
+input clock;
+input [3:0] cs;
+output reg [3:0] ns;
+reg [3:0] temp;
+
+always @(posedge clock)
+begin
+ temp = cs;
+ ns = temp;
+end
+
+endmodule
+
+// test_intermout_always_ff_6_test.v
+module f8_inc(clock, counter);
+
+input clock;
+output reg [3:0] counter;
+always @(posedge clock)
+ counter <= counter + 1;
+endmodule
+
+// test_intermout_always_ff_8_test.v
+module f9_NegEdgeClock(q, d, clk, reset);
+input d, clk, reset;
+output reg q;
+
+always @(negedge clk or negedge reset)
+ if(!reset)
+ q <= 1'b0;
+ else
+ q <= d;
+
+endmodule
+
+// test_intermout_always_ff_9_test.v
+module f10_MyCounter (clock, preset, updown, presetdata, counter);
+input clock, preset, updown;
+input [1: 0] presetdata;
+output reg [1:0] counter;
+
+always @(posedge clock)
+ if(preset)
+ counter <= presetdata;
+ else
+ if(updown)
+ counter <= counter + 1;
+ else
+ counter <= counter - 1;
+endmodule
+
+// test_intermout_always_latch_1_test.v
+module f11_test(en, in, out);
+input en;
+input [1:0] in;
+output reg [2:0] out;
+
+always @ (en or in)
+ if(en)
+ out = in + 1;
+endmodule
+
+// test_intermout_bufrm_1_test.v
+module f12_test(input in, output out);
+//no buffer removal
+assign out = in;
+endmodule
+
+// test_intermout_bufrm_2_test.v
+module f13_test(input in, output out);
+//intermediate buffers should be removed
+wire w1, w2;
+assign w1 = in;
+assign w2 = w1;
+assign out = w2;
+endmodule
+
+// test_intermout_bufrm_6_test.v
+module f14_test(in, out);
+input in;
+output out;
+
+wire w1, w2, w3, w4;
+assign w1 = in;
+assign w2 = w1;
+assign w4 = w3;
+assign out = w4;
+f14_mybuf _f14_mybuf(w2, w3);
+endmodule
+
+module f14_mybuf(in, out);
+input in;
+output out;
+wire w1, w2, w3, w4;
+
+assign w1 = in;
+assign w2 = w1;
+assign out = w2;
+endmodule
+
+
+// test_intermout_bufrm_7_test.v
+module f15_test(in1, in2, out);
+input in1, in2;
+output out;
+// Y with cluster of f15_mybuf instances at the junction
+
+wire w1, w2, w3, w4, w5, w6, w7, w8, w9, w10;
+assign w1 = in1;
+assign w2 = w1;
+assign w5 = in2;
+assign w6 = w5;
+assign w10 = w9;
+assign out = w10;
+
+f15_mybuf _f15_mybuf0(w2, w3);
+f15_mybuf _f15_mybuf1(w3, w4);
+
+f15_mybuf _f15_mybuf2(w6, w7);
+f15_mybuf _f15_mybuf3(w7, w4);
+
+f15_mybuf _f15_mybuf4(w4, w8);
+f15_mybuf _f15_mybuf5(w8, w9);
+endmodule
+
+module f15_mybuf(in, out);
+input in;
+output out;
+wire w1, w2, w3, w4;
+
+assign w1 = in;
+assign w2 = w1;
+assign out = w2;
+endmodule
+
+
+// test_intermout_exprs_add_test.v
+module f16_test(out, in1, in2, vin1, vin2, vout1);
+output out;
+input in1, in2;
+input [1:0] vin1;
+input [2:0] vin2;
+output [3:0] vout1;
+
+assign out = in1 + in2;
+assign vout1 = vin1 + vin2;
+endmodule
+
+// test_intermout_exprs_binlogic_test.v
+module f17_test(in1, in2, vin1, vin2, out, vout, vin3, vin4, vout1 );
+input in1, in2;
+input [1:0] vin1;
+input [3:0] vin2;
+input [1:0] vin3;
+input [3:0] vin4;
+output vout, vout1;
+output out;
+
+assign out = in1 && in2;
+assign vout = vin1 && vin2;
+assign vout1 = vin3 || vin4;
+endmodule
+
+// test_intermout_exprs_bitwiseneg_test.v
+module f18_test(output out, input in, output [1:0] vout, input [1:0] vin);
+
+assign out = ~in;
+assign vout = ~vin;
+endmodule
+
+// test_intermout_exprs_buffer_test.v
+module f19_buffer(in, out, vin, vout);
+input in;
+output out;
+input [1:0] vin;
+output [1:0] vout;
+
+assign out = in;
+assign vout = vin;
+endmodule
+
+// test_intermout_exprs_condexpr_mux_test.v
+module f20_test(in1, in2, out, vin1, vin2, vin3, vin4, vout1, vout2, en1, ven1, ven2);
+input in1, in2, en1, ven1;
+input [1:0] ven2;
+output out;
+input [1:0] vin1, vin2, vin3, vin4;
+output [1:0] vout1, vout2;
+
+assign out = en1 ? in1 : in2;
+assign vout1 = ven1 ? vin1 : vin2;
+assign vout2 = ven2 ? vin3 : vin4;
+endmodule
+
+// test_intermout_exprs_condexpr_tribuf_test.v
+module f21_test(in, out, en, vin1, vout1, en1);
+input in, en, en1;
+output out;
+input [1:0] vin1;
+output [1:0] vout1;
+
+assign out = en ? in : 1'bz;
+assign vout1 = en1 ? vin1 : 2'bzz;
+endmodule
+
+// test_intermout_exprs_constshift_test.v
+module f22_test(in, out, vin, vout, vin1, vout1, vin2, vout2);
+
+input in;
+input [3:0] vin, vin1, vin2;
+output [3:0] vout, vout1, vout2;
+output out;
+
+assign out = in << 1;
+assign vout = vin << 2;
+assign vout1 = vin1 >> 2;
+assign vout2 = vin2 >>> 2;
+endmodule
+
+// test_intermout_exprs_const_test.v
+module f23_test (out, vout);
+output out;
+output [7:0] vout;
+
+assign out = 1'b1;
+assign vout = 9;
+endmodule
+
+// test_intermout_exprs_div_test.v
+module f24_test(out, in1, in2, vin1, vin2, vout1);
+output out;
+input in1, in2;
+input [1:0] vin1;
+input [2:0] vin2;
+output [3:0] vout1;
+
+assign out = in1 / in2;
+assign vout1 = vin1 / vin2;
+endmodule
+
+// test_intermout_exprs_logicneg_test.v
+module f25_test(out, vout, in, vin);
+output out, vout;
+input in;
+input [3:0] vin;
+assign out = !in;
+assign vout = !vin;
+endmodule
+
+// test_intermout_exprs_mod_test.v
+module f26_test(out, in1, in2, vin1, vin2, vout1);
+output out;
+input in1, in2;
+input [1:0] vin1;
+input [2:0] vin2;
+output [3:0] vout1;
+
+assign out = in1 % in2;
+assign vout1 = vin1 % vin2;
+endmodule
+
+// test_intermout_exprs_mul_test.v
+module f27_test(out, in1, in2, vin1, vin2, vout1);
+output out;
+input in1, in2;
+input [1:0] vin1;
+input [2:0] vin2;
+output [3:0] vout1;
+
+assign out = in1 * in2;
+assign vout1 = vin1 * vin2;
+endmodule
+
+// test_intermout_exprs_redand_test.v
+module f28_test(output out, input [1:0] vin, output out1, input [3:0] vin1);
+
+assign out = &vin;
+assign out1 = &vin1;
+endmodule
+
+// test_intermout_exprs_redop_test.v
+module f29_Reduction (A1, A2, A3, A4, A5, A6, Y1, Y2, Y3, Y4, Y5, Y6);
+input [1:0] A1;
+input [1:0] A2;
+input [1:0] A3;
+input [1:0] A4;
+input [1:0] A5;
+input [1:0] A6;
+output Y1, Y2, Y3, Y4, Y5, Y6;
+//reg Y1, Y2, Y3, Y4, Y5, Y6;
+assign Y1=&A1; //reduction AND
+assign Y2=|A2; //reduction OR
+assign Y3=~&A3; //reduction NAND
+assign Y4=~|A4; //reduction NOR
+assign Y5=^A5; //reduction XOR
+assign Y6=~^A6; //reduction XNOR
+endmodule
+
+// test_intermout_exprs_sub_test.v
+module f30_test(out, in1, in2, vin1, vin2, vout1);
+output out;
+input in1, in2;
+input [1:0] vin1;
+input [2:0] vin2;
+output [3:0] vout1;
+
+assign out = in1 - in2;
+assign vout1 = vin1 - vin2;
+endmodule
+
+// test_intermout_exprs_unaryminus_test.v
+module f31_test(output out, input in, output [31:0] vout, input [31:0] vin);
+
+assign out = -in;
+assign vout = -vin;
+endmodule
+
+// test_intermout_exprs_unaryplus_test.v
+module f32_test(output out, input in);
+
+assign out = +in;
+endmodule
+
+// test_intermout_exprs_varshift_test.v
+module f33_test(vin0, vout0);
+input [2:0] vin0;
+output reg [7:0] vout0;
+
+wire [7:0] myreg0, myreg1, myreg2;
+integer i;
+assign myreg0 = vout0 << vin0;
+
+assign myreg1 = myreg2 >> i;
+endmodule
diff --git a/tests/hana/test_parse2synthtrans.v b/tests/hana/test_parse2synthtrans.v
new file mode 100644
index 00000000..a1c0bfdb
--- /dev/null
+++ b/tests/hana/test_parse2synthtrans.v
@@ -0,0 +1,117 @@
+
+// test_parse2synthtrans_behavopt_1_test.v
+module f1_test(in, out, clk, reset);
+input in, reset;
+output reg out;
+input clk;
+reg signed [3:0] a;
+reg signed [3:0] b;
+reg signed [3:0] c;
+reg [5:0] d;
+reg [5:0] e;
+
+always @(clk or reset) begin
+ a = -4;
+ b = 2;
+ c = a + b;
+ d = a + b + c;
+ d = d*d;
+ if(b)
+ e = d*d;
+ else
+ e = d + d;
+end
+endmodule
+
+// test_parse2synthtrans_case_1_test.v
+module f2_demultiplexer1_to_4 (out0, out1, out2, out3, in, s1, s0);
+output out0, out1, out2, out3;
+reg out0, out1, out2, out3;
+input in;
+input s1, s0;
+reg [3:0] encoding;
+reg [1:0] state;
+ always @(encoding) begin
+ case (encoding)
+ 4'bxx11: state = 1;
+ 4'bx0xx: state = 3;
+ 4'b11xx: state = 4;
+ 4'bx1xx: state = 2;
+ 4'bxx1x: state = 1;
+ 4'bxxx1: state = 0;
+ default: state = 0;
+ endcase
+ end
+
+ always @(encoding) begin
+ case (encoding)
+ 4'b0000: state = 1;
+ default: state = 0;
+ endcase
+ end
+endmodule
+
+// test_parse2synthtrans_contassign_1_test.v
+module f3_test(in, out);
+
+input wire in;
+output out;
+assign out = (in+in);
+assign out = 74;
+endmodule
+
+// test_parse2synthtrans_module_basic0_test.v
+module f4_test;
+endmodule
+
+// test_parse2synthtrans_operators_1_test.v
+module f5_test(in, out);
+input in;
+output out;
+parameter p1 = 10;
+parameter p2 = 5;
+
+assign out = +p1;
+assign out = -p2;
+assign out = p1 + p2;
+assign out = p1 - p2;
+endmodule
+
+// test_parse2synthtrans_param_1_test.v
+module f6_test(in, out);
+input in;
+output out;
+parameter p = 10;
+
+assign out = p;
+endmodule
+
+// test_parse2synthtrans_port_scalar_1_test.v
+module f7_test(in, out, io);
+inout io;
+output out;
+input in;
+
+endmodule
+
+// test_parse2synthtrans_port_vector_1_test.v
+module f8_test(in1, in2, out1, out2, io1, io2);
+inout [1:0] io1;
+inout [0:1] io2;
+output [1:0] out1;
+output [0:1] out2;
+input [1:0] in1;
+input [0:1] in2;
+
+endmodule
+
+// test_parse2synthtrans_v2k_comb_logic_sens_list_test.v
+module f9_test(q, d, clk, reset);
+output reg q;
+input d, clk, reset;
+
+always @ (posedge clk, negedge reset)
+ if(!reset) q <= 0;
+ else q <= d;
+
+endmodule
diff --git a/tests/hana/test_parser.v b/tests/hana/test_parser.v
new file mode 100644
index 00000000..c7305356
--- /dev/null
+++ b/tests/hana/test_parser.v
@@ -0,0 +1,87 @@
+
+// test_parser_constructs_module_basic1_test.v
+module f1_test;
+endmodule
+
+// test_parser_constructs_param_basic0_test.v
+module f2_test #( parameter v2kparam = 5)
+(in, out, io, vin, vout, vio);
+input in;
+output out;
+inout io;
+input [3:0] vin;
+output [v2kparam:0] vout;
+inout [0:3] vio;
+parameter myparam = 10;
+endmodule
+
+// test_parser_constructs_port_basic0_test.v
+module f3_test(in, out, io, vin, vout, vio);
+input in;
+output out;
+inout io;
+input [3:0] vin;
+output [3:0] vout;
+inout [0:3] vio;
+endmodule
+
+// test_parser_directives_define_simpledef_test.v
+`define parvez ahmad
+`define WIRE wire
+`define TEN 10
+
+module f4_`parvez();
+parameter param = `TEN;
+`WIRE w;
+assign w = `TEN;
+endmodule
+
+// test_parser_misc_operators_test.v
+module f5_test(out, i0, i1, i2, i3, s1, s0);
+output out;
+input i0, i1, i2, i3;
+input s1, s0;
+
+assign out = (~s1 & s0 & i0) |
+ (~s1 & s0 & i1) |
+ (s1 & ~s0 & i2) |
+ (s1 & s0 & i3);
+
+endmodule
+
+module f5_ternaryop(out, i0, i1, i2, i3, s1, s0);
+output out;
+input i0, i1, i2, i3;
+input s1, s0;
+
+assign out = s1 ? (s0 ? i3 : i2) : (s0 ? i1 : i0);
+
+endmodule
+
+module f5_fulladd4(sum, c_out, a, b, c_in);
+output [3:0] sum;
+output c_out;
+input [3:0] a, b;
+input c_in;
+
+assign {c_out, sum} = a + b + c_in;
+endmodule
+
+// test_parser_v2k_comb_port_data_type_test.v
+module f6_adder(sum , co, a, b, ci);
+output reg [31:0] sum;
+output reg co;
+input wire [31:0] a, b;
+input wire ci;
+endmodule
+
+// test_parser_v2k_comma_sep_sens_list_test.v
+module f7_test(q, d, clk, reset);
+output reg q;
+input d, clk, reset;
+
+always @ (posedge clk, negedge reset)
+ if(!reset) q <= 0;
+ else q <= d;
+
+endmodule
diff --git a/tests/hana/test_simulation_always.v b/tests/hana/test_simulation_always.v
new file mode 100644
index 00000000..3ee75313
--- /dev/null
+++ b/tests/hana/test_simulation_always.v
@@ -0,0 +1,135 @@
+
+// test_simulation_always_15_test.v
+module f1_test(input [1:0] in, output reg [1:0] out);
+
+always @(in)
+ out = in;
+endmodule
+
+// test_simulation_always_17_test.v
+module f2_test(a, b, c, d, z);
+input a, b, c, d;
+output z;
+reg z, temp1, temp2;
+
+always @(a or b or c or d)
+begin
+ temp1 = a ^ b;
+ temp2 = c ^ d;
+ z = temp1 ^ temp2;
+end
+
+endmodule
+
+// test_simulation_always_18_test.v
+module f3_test (in1, in2, out);
+input in1, in2;
+output reg out;
+
+always @ ( in1 or in2)
+ if(in1 > in2)
+ out = in1;
+ else
+ out = in2;
+endmodule
+
+// test_simulation_always_19_test.v
+module f4_test(ctrl, in1, in2, out);
+input ctrl;
+input in1, in2;
+output reg out;
+
+always @ (ctrl or in1 or in2)
+ if(ctrl)
+ out = in1 & in2;
+ else
+ out = in1 | in2;
+endmodule
+
+// test_simulation_always_1_test.v
+module f5_test(input in, output reg out);
+
+always @(in)
+ out = in;
+endmodule
+
+// test_simulation_always_20_test.v
+module f6_NonBlockingEx(clk, merge, er, xmit, fddi, claim);
+input clk, merge, er, xmit, fddi;
+output reg claim;
+reg fcr;
+
+always @(posedge clk)
+begin
+ fcr <= er | xmit;
+
+ if(merge)
+ claim <= fcr & fddi;
+ else
+ claim <= fddi;
+end
+endmodule
+
+// test_simulation_always_21_test.v
+module f7_FlipFlop(clk, cs, ns);
+input clk;
+input [7:0] cs;
+output [7:0] ns;
+integer is;
+
+always @(posedge clk)
+ is <= cs;
+
+assign ns = is;
+endmodule
+
+// test_simulation_always_22_test.v
+module f8_inc(clock, counter);
+
+input clock;
+output reg [7:0] counter;
+always @(posedge clock)
+ counter <= counter + 1;
+endmodule
+
+// test_simulation_always_23_test.v
+module f9_MyCounter (clock, preset, updown, presetdata, counter);
+input clock, preset, updown;
+input [1: 0] presetdata;
+output reg [1:0] counter;
+
+always @(posedge clock)
+ if(preset)
+ counter <= presetdata;
+ else
+ if(updown)
+ counter <= counter + 1;
+ else
+ counter <= counter - 1;
+endmodule
+
+// test_simulation_always_27_test.v
+module f10_FlipFlop(clock, cs, ns);
+input clock;
+input cs;
+output reg ns;
+reg temp;
+
+always @(posedge clock)
+begin
+ temp <= cs;
+ ns <= temp;
+end
+
+endmodule
+
+// test_simulation_always_29_test.v
+module f11_test(input in, output reg [1:0] out);
+
+ always @(in)
+ begin
+ out = in;
+ out = out + in;
+ end
+
+endmodule
diff --git a/tests/hana/test_simulation_and.v b/tests/hana/test_simulation_and.v
new file mode 100644
index 00000000..480e733d
--- /dev/null
+++ b/tests/hana/test_simulation_and.v
@@ -0,0 +1,35 @@
+
+// test_simulation_and_1_test.v
+module f1_test(input [1:0] in, output out);
+assign out = in[0] & in[1];
+endmodule
+
+// test_simulation_and_2_test.v
+module f2_test(input [1:0] in, output out);
+assign out = in[0] && in[1];
+endmodule
+
+// test_simulation_and_3_test.v
+module f3_test(input [2:0] in, output out);
+assign out = in[0] & in[1] & in[2];
+endmodule
+
+// test_simulation_and_4_test.v
+module f4_test(input [2:0] in, output out);
+assign out = in[0] && in[1] && in[2];
+endmodule
+
+// test_simulation_and_5_test.v
+module f5_test(input [3:0] in, output out);
+assign out = in[0] & in[1] & in[2] & in[3];
+endmodule
+
+// test_simulation_and_6_test.v
+module f6_test(input [3:0] in, output out);
+assign out = in[0] && in[1] && in[2] && in[3];
+endmodule
+
+// test_simulation_and_7_test.v
+module f7_test(input [3:0] in, output out);
+and myand(out, in[0], in[1], in[2], in[3]);
+endmodule
diff --git a/tests/hana/test_simulation_buffer.v b/tests/hana/test_simulation_buffer.v
new file mode 100644
index 00000000..d674b05c
--- /dev/null
+++ b/tests/hana/test_simulation_buffer.v
@@ -0,0 +1,17 @@
+
+// test_simulation_buffer_1_test.v
+module f1_test(input in, output out);
+assign out = in;
+endmodule
+
+// test_simulation_buffer_2_test.v
+module f2_test(input [1:0] in, output [1:0] out);
+assign out[0] = in[0];
+assign out[1] = in[1];
+endmodule
+
+// test_simulation_buffer_3_test.v
+module f3_test(input in, output [1:0] out);
+assign out[0] = in;
+assign out[1] = in;
+endmodule
diff --git a/tests/hana/test_simulation_decoder.v b/tests/hana/test_simulation_decoder.v
new file mode 100644
index 00000000..ef9045aa
--- /dev/null
+++ b/tests/hana/test_simulation_decoder.v
@@ -0,0 +1,219 @@
+
+// test_simulation_decoder_2_test.v
+module f1_test (input [1:0] in, input enable, output reg out);
+
+always @(in or enable)
+ if(!enable)
+ out = 4'b0000;
+ else begin
+ case (in)
+ 2'b00 : out = 0 ;
+ 2'b01 : out = 1;
+ 2'b10 : out = 0;
+ 2'b11 : out = 1;
+ endcase
+ end
+endmodule
+
+// test_simulation_decoder_3_test.v
+module f2_test (input [1:0] in, input enable, output reg [2:0] out);
+
+always @(in or enable)
+ if(!enable)
+ out = 3'b000;
+ else begin
+ case (in)
+ 2'b00 : out = 3'b001 ;
+ 2'b01 : out = 3'b010;
+ 2'b10 : out = 3'b010;
+ 2'b11 : out = 3'b100;
+ endcase
+ end
+endmodule
+
+// test_simulation_decoder_4_test.v
+module f3_test (input [2:0] in, output reg [7:0] out);
+
+always @(in )
+ case (in)
+ 3'b000 : out = 8'b00000001;
+ 3'b001 : out = 8'b00000010;
+ 3'b010 : out = 8'b00000100;
+ 3'b011 : out = 8'b00001000;
+ 3'b100 : out = 8'b00010000;
+ 3'b101 : out = 8'b00100000;
+ 3'b110 : out = 8'b01000000;
+ 3'b111 : out = 8'b10000000;
+ endcase
+endmodule
+
+// test_simulation_decoder_5_test.v
+module f4_test (input [2:0] in, input enable, output reg [7:0] out);
+
+always @(in or enable )
+ if(!enable)
+ out = 8'b00000000;
+ else
+ case (in)
+ 3'b000 : out = 8'b00000001;
+ 3'b001 : out = 8'b00000010;
+ 3'b010 : out = 8'b00000100;
+ 3'b011 : out = 8'b00001000;
+ 3'b100 : out = 8'b00010000;
+ 3'b101 : out = 8'b00100000;
+ 3'b110 : out = 8'b01000000;
+ 3'b111 : out = 8'b10000000;
+ endcase
+endmodule
+
+// test_simulation_decoder_6_test.v
+module f5_test (input [3:0] in, input enable, output reg [15:0] out);
+
+always @(in or enable)
+ if(!enable)
+ out = 16'b0000000000000000;
+ else begin
+ case (in)
+ 4'b0000 : out = 16'b0000000000000001;
+ 4'b0001 : out = 16'b0000000000000010;
+ 4'b0010 : out = 16'b0000000000000100;
+ 4'b0011 : out = 16'b0000000000001000;
+ 4'b0100 : out = 16'b0000000000010000;
+ 4'b0101 : out = 16'b0000000000100000;
+ 4'b0110 : out = 16'b0000000001000000;
+ 4'b0111 : out = 16'b0000000010000000;
+ 4'b1000 : out = 16'b0000000100000000;
+ 4'b1001 : out = 16'b0000001000000000;
+ 4'b1010 : out = 16'b0000010000000000;
+ 4'b1011 : out = 16'b0000100000000000;
+ 4'b1100 : out = 16'b0001000000000000;
+ 4'b1101 : out = 16'b0010000000000000;
+ 4'b1110 : out = 16'b0100000000000000;
+ 4'b1111 : out = 16'b1000000000000000;
+ endcase
+ end
+endmodule
+
+
+// test_simulation_decoder_7_test.v
+module f6_test (input [4:0] in, input enable, output reg [31:0] out);
+
+always @(in or enable)
+ if(!enable)
+ out = 32'b00000000000000000000000000000000;
+ else begin
+ case (in)
+ 5'b00000 : out = 32'b00000000000000000000000000000001;
+ 5'b00001 : out = 32'b00000000000000000000000000000010;
+ 5'b00010 : out = 32'b00000000000000000000000000000100;
+ 5'b00011 : out = 32'b00000000000000000000000000001000;
+ 5'b00100 : out = 32'b00000000000000000000000000010000;
+ 5'b00101 : out = 32'b00000000000000000000000000100000;
+ 5'b00110 : out = 32'b00000000000000000000000001000000;
+ 5'b00111 : out = 32'b00000000000000000000000010000000;
+ 5'b01000 : out = 32'b00000000000000000000000100000000;
+ 5'b01001 : out = 32'b00000000000000000000001000000000;
+ 5'b01010 : out = 32'b00000000000000000000010000000000;
+ 5'b01011 : out = 32'b00000000000000000000100000000000;
+ 5'b01100 : out = 32'b00000000000000000001000000000000;
+ 5'b01101 : out = 32'b00000000000000000010000000000000;
+ 5'b01110 : out = 32'b00000000000000000100000000000000;
+ 5'b01111 : out = 32'b00000000000000001000000000000000;
+ 5'b10000 : out = 32'b00000000000000010000000000000000;
+ 5'b10001 : out = 32'b00000000000000100000000000000000;
+ 5'b10010 : out = 32'b00000000000001000000000000000000;
+ 5'b10011 : out = 32'b00000000000010000000000000000000;
+ 5'b10100 : out = 32'b00000000000100000000000000000000;
+ 5'b10101 : out = 32'b00000000001000000000000000000000;
+ 5'b10110 : out = 32'b00000000010000000000000000000000;
+ 5'b10111 : out = 32'b00000000100000000000000000000000;
+ 5'b11000 : out = 32'b00000001000000000000000000000000;
+ 5'b11001 : out = 32'b00000010000000000000000000000000;
+ 5'b11010 : out = 32'b00000100000000000000000000000000;
+ 5'b11011 : out = 32'b00001000000000000000000000000000;
+ 5'b11100 : out = 32'b00010000000000000000000000000000;
+ 5'b11101 : out = 32'b00100000000000000000000000000000;
+ 5'b11110 : out = 32'b01000000000000000000000000000000;
+ 5'b11111 : out = 32'b10000000000000000000000000000000;
+ endcase
+ end
+endmodule
+
+
+// test_simulation_decoder_8_test.v
+module f7_test (input [5:0] in, input enable, output reg [63:0] out);
+
+always @(in or enable)
+ if(!enable)
+ out = 64'b0000000000000000000000000000000000000000000000000000000000000000;
+ else begin
+ case (in)
+ 6'b000000 : out = 64'b0000000000000000000000000000000000000000000000000000000000000001;
+ 6'b000001 : out = 64'b0000000000000000000000000000000000000000000000000000000000000010;
+ 6'b000010 : out = 64'b0000000000000000000000000000000000000000000000000000000000000100;
+ 6'b000011 : out = 64'b0000000000000000000000000000000000000000000000000000000000001000;
+ 6'b000100 : out = 64'b0000000000000000000000000000000000000000000000000000000000010000;
+ 6'b000101 : out = 64'b0000000000000000000000000000000000000000000000000000000000100000;
+ 6'b000110 : out = 64'b0000000000000000000000000000000000000000000000000000000001000000;
+ 6'b000111 : out = 64'b0000000000000000000000000000000000000000000000000000000010000000;
+ 6'b001000 : out = 64'b0000000000000000000000000000000000000000000000000000000100000000;
+ 6'b001001 : out = 64'b0000000000000000000000000000000000000000000000000000001000000000;
+ 6'b001010 : out = 64'b0000000000000000000000000000000000000000000000000000010000000000;
+ 6'b001011 : out = 64'b0000000000000000000000000000000000000000000000000000100000000000;
+ 6'b001100 : out = 64'b0000000000000000000000000000000000000000000000000001000000000000;
+ 6'b001101 : out = 64'b0000000000000000000000000000000000000000000000000010000000000000;
+ 6'b001110 : out = 64'b0000000000000000000000000000000000000000000000000100000000000000;
+ 6'b001111 : out = 64'b0000000000000000000000000000000000000000000000001000000000000000;
+ 6'b010000 : out = 64'b0000000000000000000000000000000000000000000000010000000000000000;
+ 6'b010001 : out = 64'b0000000000000000000000000000000000000000000000100000000000000000;
+ 6'b010010 : out = 64'b0000000000000000000000000000000000000000000001000000000000000000;
+ 6'b010011 : out = 64'b0000000000000000000000000000000000000000000010000000000000000000;
+ 6'b010100 : out = 64'b0000000000000000000000000000000000000000000100000000000000000000;
+ 6'b010101 : out = 64'b0000000000000000000000000000000000000000001000000000000000000000;
+ 6'b010110 : out = 64'b0000000000000000000000000000000000000000010000000000000000000000;
+ 6'b010111 : out = 64'b0000000000000000000000000000000000000000100000000000000000000000;
+ 6'b011000 : out = 64'b0000000000000000000000000000000000000001000000000000000000000000;
+ 6'b011001 : out = 64'b0000000000000000000000000000000000000010000000000000000000000000;
+ 6'b011010 : out = 64'b0000000000000000000000000000000000000100000000000000000000000000;
+ 6'b011011 : out = 64'b0000000000000000000000000000000000001000000000000000000000000000;
+ 6'b011100 : out = 64'b0000000000000000000000000000000000010000000000000000000000000000;
+ 6'b011101 : out = 64'b0000000000000000000000000000000000100000000000000000000000000000;
+ 6'b011110 : out = 64'b0000000000000000000000000000000001000000000000000000000000000000;
+ 6'b011111 : out = 64'b0000000000000000000000000000000010000000000000000000000000000000;
+
+ 6'b100000 : out = 64'b0000000000000000000000000000000100000000000000000000000000000000;
+ 6'b100001 : out = 64'b0000000000000000000000000000001000000000000000000000000000000000;
+ 6'b100010 : out = 64'b0000000000000000000000000000010000000000000000000000000000000000;
+ 6'b100011 : out = 64'b0000000000000000000000000000100000000000000000000000000000000000;
+ 6'b100100 : out = 64'b0000000000000000000000000001000000000000000000000000000000000000;
+ 6'b100101 : out = 64'b0000000000000000000000000010000000000000000000000000000000000000;
+ 6'b100110 : out = 64'b0000000000000000000000000100000000000000000000000000000000000000;
+ 6'b100111 : out = 64'b0000000000000000000000001000000000000000000000000000000000000000;
+ 6'b101000 : out = 64'b0000000000000000000000010000000000000000000000000000000000000000;
+ 6'b101001 : out = 64'b0000000000000000000000100000000000000000000000000000000000000000;
+ 6'b101010 : out = 64'b0000000000000000000001000000000000000000000000000000000000000000;
+ 6'b101011 : out = 64'b0000000000000000000010000000000000000000000000000000000000000000;
+ 6'b101100 : out = 64'b0000000000000000000100000000000000000000000000000000000000000000;
+ 6'b101101 : out = 64'b0000000000000000001000000000000000000000000000000000000000000000;
+ 6'b101110 : out = 64'b0000000000000000010000000000000000000000000000000000000000000000;
+ 6'b101111 : out = 64'b0000000000000000100000000000000000000000000000000000000000000000;
+ 6'b110000 : out = 64'b0000000000000001000000000000000000000000000000000000000000000000;
+ 6'b110001 : out = 64'b0000000000000010000000000000000000000000000000000000000000000000;
+ 6'b110010 : out = 64'b0000000000000100000000000000000000000000000000000000000000000000;
+ 6'b110011 : out = 64'b0000000000001000000000000000000000000000000000000000000000000000;
+ 6'b110100 : out = 64'b0000000000010000000000000000000000000000000000000000000000000000;
+ 6'b110101 : out = 64'b0000000000100000000000000000000000000000000000000000000000000000;
+ 6'b110110 : out = 64'b0000000001000000000000000000000000000000000000000000000000000000;
+ 6'b110111 : out = 64'b0000000010000000000000000000000000000000000000000000000000000000;
+ 6'b111000 : out = 64'b0000000100000000000000000000000000000000000000000000000000000000;
+ 6'b111001 : out = 64'b0000001000000000000000000000000000000000000000000000000000000000;
+ 6'b111010 : out = 64'b0000010000000000000000000000000000000000000000000000000000000000;
+ 6'b111011 : out = 64'b0000100000000000000000000000000000000000000000000000000000000000;
+ 6'b111100 : out = 64'b0001000000000000000000000000000000000000000000000000000000000000;
+ 6'b111101 : out = 64'b0010000000000000000000000000000000000000000000000000000000000000;
+ 6'b111110 : out = 64'b0100000000000000000000000000000000000000000000000000000000000000;
+ 6'b111111 : out = 64'b1000000000000000000000000000000000000000000000000000000000000000;
+ endcase
+ end
+endmodule
+
diff --git a/tests/hana/test_simulation_inc.v b/tests/hana/test_simulation_inc.v
new file mode 100644
index 00000000..f8f54870
--- /dev/null
+++ b/tests/hana/test_simulation_inc.v
@@ -0,0 +1,42 @@
+
+// test_simulation_inc_16_test.v
+module f1_test(input [15:0] in, output [15:0] out);
+
+assign out = -in;
+
+endmodule
+
+// test_simulation_inc_1_test.v
+module f2_test(input in, output out);
+
+assign out = -in;
+
+endmodule
+
+// test_simulation_inc_2_test.v
+module f3_test(input [1:0] in, output [1:0] out);
+
+assign out = -in;
+
+endmodule
+
+// test_simulation_inc_32_test.v
+module f4_test(input [31:0] in, output [31:0] out);
+
+assign out = -in;
+
+endmodule
+
+// test_simulation_inc_4_test.v
+module f5_test(input [3:0] in, output [3:0] out);
+
+assign out = -in;
+
+endmodule
+
+// test_simulation_inc_8_test.v
+module f6_test(input [7:0] in, output [7:0] out);
+
+assign out = -in;
+
+endmodule
diff --git a/tests/hana/test_simulation_mux.v b/tests/hana/test_simulation_mux.v
new file mode 100644
index 00000000..085387ef
--- /dev/null
+++ b/tests/hana/test_simulation_mux.v
@@ -0,0 +1,176 @@
+
+// test_simulation_mux_16_test.v
+module f1_test(input [15:0] in, input [3:0] select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ 2: out = in[2];
+ 3: out = in[3];
+ 4: out = in[4];
+ 5: out = in[5];
+ 6: out = in[6];
+ 7: out = in[7];
+ 8: out = in[8];
+ 9: out = in[9];
+ 10: out = in[10];
+ 11: out = in[11];
+ 12: out = in[12];
+ 13: out = in[13];
+ 14: out = in[14];
+ 15: out = in[15];
+ endcase
+endmodule
+
+// test_simulation_mux_2_test.v
+module f2_test(input [1:0] in, input select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ endcase
+endmodule
+
+// test_simulation_mux_32_test.v
+module f3_test(input [31:0] in, input [4:0] select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ 2: out = in[2];
+ 3: out = in[3];
+ 4: out = in[4];
+ 5: out = in[5];
+ 6: out = in[6];
+ 7: out = in[7];
+ 8: out = in[8];
+ 9: out = in[9];
+ 10: out = in[10];
+ 11: out = in[11];
+ 12: out = in[12];
+ 13: out = in[13];
+ 14: out = in[14];
+ 15: out = in[15];
+ 16: out = in[16];
+ 17: out = in[17];
+ 18: out = in[18];
+ 19: out = in[19];
+ 20: out = in[20];
+ 21: out = in[21];
+ 22: out = in[22];
+ 23: out = in[23];
+ 24: out = in[24];
+ 25: out = in[25];
+ 26: out = in[26];
+ 27: out = in[27];
+ 28: out = in[28];
+ 29: out = in[29];
+ 30: out = in[30];
+ 31: out = in[31];
+ endcase
+endmodule
+
+
+// test_simulation_mux_4_test.v
+module f4_test(input [3:0] in, input [1:0] select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ 2: out = in[2];
+ 3: out = in[3];
+ endcase
+endmodule
+
+// test_simulation_mux_64_test.v
+module f5_test(input [63:0] in, input [5:0] select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ 2: out = in[2];
+ 3: out = in[3];
+ 4: out = in[4];
+ 5: out = in[5];
+ 6: out = in[6];
+ 7: out = in[7];
+ 8: out = in[8];
+ 9: out = in[9];
+ 10: out = in[10];
+ 11: out = in[11];
+ 12: out = in[12];
+ 13: out = in[13];
+ 14: out = in[14];
+ 15: out = in[15];
+ 16: out = in[16];
+ 17: out = in[17];
+ 18: out = in[18];
+ 19: out = in[19];
+ 20: out = in[20];
+ 21: out = in[21];
+ 22: out = in[22];
+ 23: out = in[23];
+ 24: out = in[24];
+ 25: out = in[25];
+ 26: out = in[26];
+ 27: out = in[27];
+ 28: out = in[28];
+ 29: out = in[29];
+ 30: out = in[30];
+ 31: out = in[31];
+ 32: out = in[32];
+ 33: out = in[33];
+ 34: out = in[34];
+ 35: out = in[35];
+ 36: out = in[36];
+ 37: out = in[37];
+ 38: out = in[38];
+ 39: out = in[39];
+ 40: out = in[40];
+ 41: out = in[41];
+ 42: out = in[42];
+ 43: out = in[43];
+ 44: out = in[44];
+ 45: out = in[45];
+ 46: out = in[46];
+ 47: out = in[47];
+ 48: out = in[48];
+ 49: out = in[49];
+ 50: out = in[50];
+ 51: out = in[51];
+ 52: out = in[52];
+ 53: out = in[53];
+ 54: out = in[54];
+ 55: out = in[55];
+ 56: out = in[56];
+ 57: out = in[57];
+ 58: out = in[58];
+ 59: out = in[59];
+ 60: out = in[60];
+ 61: out = in[61];
+ 62: out = in[62];
+ 63: out = in[63];
+ endcase
+endmodule
+
+
+// test_simulation_mux_8_test.v
+module f6_test(input [7:0] in, input [2:0] select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ 2: out = in[2];
+ 3: out = in[3];
+ 4: out = in[4];
+ 5: out = in[5];
+ 6: out = in[6];
+ 7: out = in[7];
+ endcase
+endmodule
diff --git a/tests/hana/test_simulation_nand.v b/tests/hana/test_simulation_nand.v
new file mode 100644
index 00000000..5e6e0f1f
--- /dev/null
+++ b/tests/hana/test_simulation_nand.v
@@ -0,0 +1,25 @@
+
+// test_simulation_nand_1_test.v
+module f1_test(input [1:0] in, output out);
+assign out = ~(in[0] & in[1]);
+endmodule
+
+// test_simulation_nand_3_test.v
+module f2_test(input [2:0] in, output out);
+assign out = !(in[0] & in[1] & in[2]);
+endmodule
+
+// test_simulation_nand_4_test.v
+module f3_test(input [2:0] in, output out);
+assign out = ~(in[0] && in[1] && in[2]);
+endmodule
+
+// test_simulation_nand_5_test.v
+module f4_test(input [3:0] in, output out);
+assign out = !(in[0] & in[1] & in[2] & in[3]);
+endmodule
+
+// test_simulation_nand_6_test.v
+module f5_test(input [3:0] in, output out);
+assign out = !(in[0] && in[1] && in[2] && in[3]);
+endmodule
diff --git a/tests/hana/test_simulation_nor.v b/tests/hana/test_simulation_nor.v
new file mode 100644
index 00000000..d7d2bc0e
--- /dev/null
+++ b/tests/hana/test_simulation_nor.v
@@ -0,0 +1,20 @@
+
+// test_simulation_nor_1_test.v
+module f1_test(input [1:0] in, output out);
+assign out = ~(in[0] | in[1]);
+endmodule
+
+// test_simulation_nor_2_test.v
+module f2_test(input [2:0] in, output out);
+assign out = ~(in[0] | in[1] | in[2]);
+endmodule
+
+// test_simulation_nor_3_test.v
+module f3_test(input [3:0] in, output out);
+assign out = ~(in[0] | in[1] | in[2] | in[3]);
+endmodule
+
+// test_simulation_nor_4_test.v
+module f4_test(input [3:0] in, output out);
+nor mynor(out, in[0], in[1], in[2], in[3]);
+endmodule
diff --git a/tests/hana/test_simulation_or.v b/tests/hana/test_simulation_or.v
new file mode 100644
index 00000000..9217db80
--- /dev/null
+++ b/tests/hana/test_simulation_or.v
@@ -0,0 +1,30 @@
+
+// test_simulation_or_1_test.v
+module f1_test(input [1:0] in, output out);
+assign out = in[0] | in[1];
+endmodule
+
+// test_simulation_or_2_test.v
+module f2_test(input [1:0] in, output out);
+assign out = in[0] || in[1];
+endmodule
+
+// test_simulation_or_3_test.v
+module f3_test(input [2:0] in, output out);
+assign out = in[0] | in[1] | in[2];
+endmodule
+
+// test_simulation_or_4_test.v
+module f4_test(input [2:0] in, output out);
+assign out = in[0] || in[1] || in[2];
+endmodule
+
+// test_simulation_or_5_test.v
+module f5_test(input [3:0] in, output out);
+assign out = in[0] | in[1] | in[2] | in[3];
+endmodule
+
+// test_simulation_or_6_test.v
+module f6_test(input [3:0] in, output out);
+assign out = in[0] || in[1] || in[2] || in[3];
+endmodule
diff --git a/tests/hana/test_simulation_seq.v b/tests/hana/test_simulation_seq.v
new file mode 100644
index 00000000..eba4e88e
--- /dev/null
+++ b/tests/hana/test_simulation_seq.v
@@ -0,0 +1,12 @@
+
+// test_simulation_seq_ff_1_test.v
+module f1_test(input in, input clk, output reg out);
+always @(posedge clk)
+ out <= in;
+endmodule
+
+// test_simulation_seq_ff_2_test.v
+module f2_test(input in, input clk, output reg out);
+always @(negedge clk)
+ out <= in;
+endmodule
diff --git a/tests/hana/test_simulation_shifter.v b/tests/hana/test_simulation_shifter.v
new file mode 100644
index 00000000..8864fb0e
--- /dev/null
+++ b/tests/hana/test_simulation_shifter.v
@@ -0,0 +1,60 @@
+
+// test_simulation_shifter_left_16_test.v
+module f1_test(input [15:0] IN, input [4:0] SHIFT, output [15:0] OUT);
+
+assign OUT = IN << SHIFT;
+endmodule
+
+// test_simulation_shifter_left_32_test.v
+module f2_test(input [31:0] IN, input [5:0] SHIFT, output [31:0] OUT);
+
+assign OUT = IN << SHIFT;
+endmodule
+
+// test_simulation_shifter_left_4_test.v
+module f3_test(input [3:0] IN, input [2:0] SHIFT, output [3:0] OUT);
+
+assign OUT = IN << SHIFT;
+endmodule
+
+// test_simulation_shifter_left_64_test.v
+module f4_test(input [63:0] IN, input [6:0] SHIFT, output [63:0] OUT);
+
+assign OUT = IN << SHIFT;
+endmodule
+
+// test_simulation_shifter_left_8_test.v
+module f5_test(input [7:0] IN, input [3:0] SHIFT, output [7:0] OUT);
+
+assign OUT = IN << SHIFT;
+endmodule
+
+// test_simulation_shifter_right_16_test.v
+module f6_test(input [15:0] IN, input [4:0] SHIFT, output [15:0] OUT);
+
+assign OUT = IN >> SHIFT;
+endmodule
+
+// test_simulation_shifter_right_32_test.v
+module f7_test(input [31:0] IN, input [5:0] SHIFT, output [31:0] OUT);
+
+assign OUT = IN >> SHIFT;
+endmodule
+
+// test_simulation_shifter_right_4_test.v
+module f8_test(input [3:0] IN, input [2:0] SHIFT, output [3:0] OUT);
+
+assign OUT = IN >> SHIFT;
+endmodule
+
+// test_simulation_shifter_right_64_test.v
+module f9_test(input [63:0] IN, input [6:0] SHIFT, output [63:0] OUT);
+
+assign OUT = IN >> SHIFT;
+endmodule
+
+// test_simulation_shifter_right_8_test.v
+module f10_test(input [7:0] IN, input [3:0] SHIFT, output [7:0] OUT);
+
+assign OUT = IN >> SHIFT;
+endmodule
diff --git a/tests/hana/test_simulation_sop.v b/tests/hana/test_simulation_sop.v
new file mode 100644
index 00000000..79870cf0
--- /dev/null
+++ b/tests/hana/test_simulation_sop.v
@@ -0,0 +1,65 @@
+
+// test_simulation_sop_basic_10_test.v
+module f1_test(input [1:0] in, input select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ endcase
+endmodule
+
+// test_simulation_sop_basic_11_test.v
+module f2_test(input [3:0] in, input [1:0] select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ 2: out = in[2];
+ 3: out = in[3];
+ endcase
+endmodule
+
+// test_simulation_sop_basic_12_test.v
+module f3_test(input [7:0] in, input [2:0] select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ 2: out = in[2];
+ 3: out = in[3];
+ 4: out = in[4];
+ 5: out = in[5];
+ 6: out = in[6];
+ 7: out = in[7];
+ endcase
+endmodule
+
+// test_simulation_sop_basic_18_test.v
+module f4_test(input [7:0] in, output out);
+
+assign out = ~^in;
+
+endmodule
+
+// test_simulation_sop_basic_3_test.v
+module f5_test(input in, output out);
+assign out = ~in;
+endmodule
+
+// test_simulation_sop_basic_7_test.v
+module f6_test(input in, output out);
+assign out = in;
+endmodule
+
+// test_simulation_sop_basic_8_test.v
+module f7_test(output out);
+assign out = 1'b0;
+endmodule
+
+// test_simulation_sop_basic_9_test.v
+module f8_test(input in, output out);
+assign out = ~in;
+endmodule
diff --git a/tests/hana/test_simulation_techmap.v b/tests/hana/test_simulation_techmap.v
new file mode 100644
index 00000000..88e24d0e
--- /dev/null
+++ b/tests/hana/test_simulation_techmap.v
@@ -0,0 +1,172 @@
+
+// test_simulation_techmap_buf_test.v
+module f1_test(input in, output out);
+assign out = in;
+endmodule
+
+// test_simulation_techmap_inv_test.v
+module f2_test(input in, output out);
+assign out = ~in;
+endmodule
+
+// test_simulation_techmap_mux_0_test.v
+module f3_test(input [1:0] in, input select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ endcase
+endmodule
+
+// test_simulation_techmap_mux_128_test.v
+module f4_test(input [127:0] in, input [6:0] select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ 2: out = in[2];
+ 3: out = in[3];
+ 4: out = in[4];
+ 5: out = in[5];
+ 6: out = in[6];
+ 7: out = in[7];
+ 8: out = in[8];
+ 9: out = in[9];
+ 10: out = in[10];
+ 11: out = in[11];
+ 12: out = in[12];
+ 13: out = in[13];
+ 14: out = in[14];
+ 15: out = in[15];
+ 16: out = in[16];
+ 17: out = in[17];
+ 18: out = in[18];
+ 19: out = in[19];
+ 20: out = in[20];
+ 21: out = in[21];
+ 22: out = in[22];
+ 23: out = in[23];
+ 24: out = in[24];
+ 25: out = in[25];
+ 26: out = in[26];
+ 27: out = in[27];
+ 28: out = in[28];
+ 29: out = in[29];
+ 30: out = in[30];
+ 31: out = in[31];
+ 32: out = in[32];
+ 33: out = in[33];
+ 34: out = in[34];
+ 35: out = in[35];
+ 36: out = in[36];
+ 37: out = in[37];
+ 38: out = in[38];
+ 39: out = in[39];
+ 40: out = in[40];
+ 41: out = in[41];
+ 42: out = in[42];
+ 43: out = in[43];
+ 44: out = in[44];
+ 45: out = in[45];
+ 46: out = in[46];
+ 47: out = in[47];
+ 48: out = in[48];
+ 49: out = in[49];
+ 50: out = in[50];
+ 51: out = in[51];
+ 52: out = in[52];
+ 53: out = in[53];
+ 54: out = in[54];
+ 55: out = in[55];
+ 56: out = in[56];
+ 57: out = in[57];
+ 58: out = in[58];
+ 59: out = in[59];
+ 60: out = in[60];
+ 61: out = in[61];
+ 62: out = in[62];
+ 63: out = in[63];
+ 64: out = in[64];
+ 65: out = in[65];
+ 66: out = in[66];
+ 67: out = in[67];
+ 68: out = in[68];
+ 69: out = in[69];
+ 70: out = in[70];
+ 71: out = in[71];
+ 72: out = in[72];
+ 73: out = in[73];
+ 74: out = in[74];
+ 75: out = in[75];
+ 76: out = in[76];
+ 77: out = in[77];
+ 78: out = in[78];
+ 79: out = in[79];
+ 80: out = in[80];
+ 81: out = in[81];
+ 82: out = in[82];
+ 83: out = in[83];
+ 84: out = in[84];
+ 85: out = in[85];
+ 86: out = in[86];
+ 87: out = in[87];
+ 88: out = in[88];
+ 89: out = in[89];
+ 90: out = in[90];
+ 91: out = in[91];
+ 92: out = in[92];
+ 93: out = in[93];
+ 94: out = in[94];
+ 95: out = in[95];
+ 96: out = in[96];
+ 97: out = in[97];
+ 98: out = in[98];
+ 99: out = in[99];
+ 100: out = in[100];
+ 101: out = in[101];
+ 102: out = in[102];
+ 103: out = in[103];
+ 104: out = in[104];
+ 105: out = in[105];
+ 106: out = in[106];
+ 107: out = in[107];
+ 108: out = in[108];
+ 109: out = in[109];
+ 110: out = in[110];
+ 111: out = in[111];
+ 112: out = in[112];
+ 113: out = in[113];
+ 114: out = in[114];
+ 115: out = in[115];
+ 116: out = in[116];
+ 117: out = in[117];
+ 118: out = in[118];
+ 119: out = in[119];
+ 120: out = in[120];
+ 121: out = in[121];
+ 122: out = in[122];
+ 123: out = in[123];
+ 124: out = in[124];
+ 125: out = in[125];
+ 126: out = in[126];
+ 127: out = in[127];
+ endcase
+endmodule
+
+// test_simulation_techmap_mux_8_test.v
+module f5_test(input [7:0] in, input [2:0] select, output reg out);
+
+always @( in or select)
+ case (select)
+ 0: out = in[0];
+ 1: out = in[1];
+ 2: out = in[2];
+ 3: out = in[3];
+ 4: out = in[4];
+ 5: out = in[5];
+ 6: out = in[6];
+ 7: out = in[7];
+ endcase
+endmodule
diff --git a/tests/hana/test_simulation_techmap_tech.v b/tests/hana/test_simulation_techmap_tech.v
new file mode 100644
index 00000000..60aeca5c
--- /dev/null
+++ b/tests/hana/test_simulation_techmap_tech.v
@@ -0,0 +1,143 @@
+
+// test_simulation_techmap_and_19_tech.v
+module f1_TECH_AND18(input [17:0] in, output out);
+assign out = &in;
+endmodule
+
+module f1_TECH_AND4(input [3:0] in, output out);
+assign out = &in;
+endmodule
+
+// test_simulation_techmap_and_5_tech.v
+module f2_TECH_AND5(input [4:0] in, output out);
+assign out = &in;
+endmodule
+
+// test_simulation_techmap_nand_19_tech.v
+module f3_TECH_NAND18(input [17:0] in, output out);
+assign out = ~(&in);
+endmodule
+
+module f3_TECH_NAND4(input [3:0] in, output out);
+assign out = ~(&in);
+endmodule
+
+module f3_TECH_NAND2(input [1:0] in, output out);
+assign out = ~(&in);
+endmodule
+
+// test_simulation_techmap_nand_2_tech.v
+module f4_TECH_NAND18(input [17:0] in, output out);
+assign out = ~(&in);
+endmodule
+
+module f4_TECH_NAND4(input [3:0] in, output out);
+assign out = ~(&in);
+endmodule
+
+module f4_TECH_NAND2(input [1:0] in, output out);
+assign out = ~(&in);
+endmodule
+
+// test_simulation_techmap_nand_5_tech.v
+module f5_TECH_NAND18(input [17:0] in, output out);
+assign out = ~(&in);
+endmodule
+
+module f5_TECH_NAND4(input [3:0] in, output out);
+assign out = ~(&in);
+endmodule
+
+module f5_TECH_NAND2(input [1:0] in, output out);
+assign out = ~(&in);
+endmodule
+
+// test_simulation_techmap_nor_19_tech.v
+module f6_TECH_NOR18(input [17:0] in, output out);
+assign out = ~(|in);
+endmodule
+
+module f6_TECH_NOR4(input [3:0] in, output out);
+assign out = ~(|in);
+endmodule
+
+module f6_TECH_NOR2(input [1:0] in, output out);
+assign out = ~(|in);
+endmodule
+
+// test_simulation_techmap_nor_2_tech.v
+module f7_TECH_NOR18(input [17:0] in, output out);
+assign out = ~(|in);
+endmodule
+
+module f7_TECH_NOR4(input [3:0] in, output out);
+assign out = ~(|in);
+endmodule
+
+module f7_TECH_NOR2(input [1:0] in, output out);
+assign out = ~(|in);
+endmodule
+
+// test_simulation_techmap_nor_5_tech.v
+module f8_TECH_NOR18(input [17:0] in, output out);
+assign out = ~(|in);
+endmodule
+
+module f8_TECH_NOR4(input [3:0] in, output out);
+assign out = ~(|in);
+endmodule
+
+module f8_TECH_NOR2(input [1:0] in, output out);
+assign out = ~(|in);
+endmodule
+
+// test_simulation_techmap_or_19_tech.v
+module f9_TECH_OR18(input [17:0] in, output out);
+assign out = |in;
+endmodule
+
+module f9_TECH_OR4(input [3:0] in, output out);
+assign out = |in;
+endmodule
+
+// test_simulation_techmap_or_5_tech.v
+module f10_TECH_OR5(input [4:0] in, output out);
+assign out = |in;
+endmodule
+
+// test_simulation_techmap_xnor_2_tech.v
+module f11_TECH_XOR5(input [4:0] in, output out);
+assign out = in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[4];
+endmodule
+module f11_TECH_XOR2(input [1:0] in, output out);
+assign out = in[0] ^ in[1];
+endmodule
+
+// test_simulation_techmap_xnor_5_tech.v
+module f12_TECH_XOR5(input [4:0] in, output out);
+assign out = in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[4];
+endmodule
+module f12_TECH_XOR2(input [1:0] in, output out);
+assign out = in[0] ^ in[1];
+endmodule
+
+// test_simulation_techmap_xor_19_tech.v
+module f13_TECH_XOR2(input [1:0] in, output out);
+assign out = in[0] ^ in[1];
+endmodule
+
+// test_simulation_techmap_xor_2_tech.v
+module f14_TECH_XOR5(input [4:0] in, output out);
+assign out = in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[4];
+endmodule
+module f14_TECH_XOR2(input [1:0] in, output out);
+assign out = in[0] ^ in[1];
+endmodule
+
+// test_simulation_techmap_xor_5_tech.v
+module f15_TECH_XOR5(input [4:0] in, output out);
+assign out = in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[4];
+endmodule
+module f15_TECH_XOR2(input [1:0] in, output out);
+assign out = in[0] ^ in[1];
+endmodule
diff --git a/tests/hana/test_simulation_vlib.v b/tests/hana/test_simulation_vlib.v
new file mode 100644
index 00000000..7d3af09c
--- /dev/null
+++ b/tests/hana/test_simulation_vlib.v
@@ -0,0 +1,65 @@
+// test_simulation_mod_1_xx.v
+module f1_test(in1, in2, out);
+input in1;
+input in2;
+output out;
+
+wire synth_net_0;
+wire synth_net_1;
+BUF synth_BUF_0(.in(synth_net_1), .out(out
+ ));
+DIV1 synth_DIV(.in1(in1), .in2(in2), .rem(synth_net_0), .out(synth_net_1
+ ));
+endmodule
+
+// test_simulation_always_31_tt.v
+module f2_test(clk, cond, data);
+input cond;
+input clk;
+output data;
+
+wire synth_net;
+wire synth_net_0;
+wire synth_net_1;
+wire synth_net_2;
+
+wire synth_net_3;
+wire synth_net_4;
+wire synth_net_5;
+wire synth_net_6;
+
+wire synth_net_7;
+wire synth_net_8;
+wire synth_net_9;
+wire synth_net_10;
+
+wire synth_net_11;
+wire tmp;
+AND2 synth_AND(.in({synth_net_0, synth_net_1}), .
+ out(synth_net_2));
+AND2 synth_AND_0(.in({synth_net_3, synth_net_4}), .out(
+ synth_net_5));
+AND2 synth_AND_1(.in({synth_net_6, synth_net_7}), .out(
+ synth_net_8));
+AND2 synth_AND_2(.in({synth_net_9, synth_net_10}), .out(
+ synth_net_11));
+BUF synth_BUF(.in(synth_net), .out(synth_net_0));
+BUF
+ synth_BUF_0(.in(data), .out(synth_net_3));
+BUF synth_BUF_1(.in(synth_net_8)
+ , .out(tmp));
+BUF synth_BUF_2(.in(tmp), .out(synth_net_9));
+MUX2 synth_MUX(.
+ in({synth_net_2, synth_net_5}), .select(cond), .out(synth_net_6));
+MUX2
+ synth_MUX_0(.in({synth_net_1, synth_net_4}), .select(cond), .out(synth_net_7
+ ));
+FF synth_FF(.d(synth_net_11), .clk(clk), .q(data));
+VCC synth_VCC(.out(
+ synth_net));
+VCC synth_VCC_0(.out(synth_net_1));
+VCC synth_VCC_1(.out(
+ synth_net_4));
+VCC synth_VCC_2(.out(synth_net_10));
+endmodule
+
diff --git a/tests/hana/test_simulation_xnor.v b/tests/hana/test_simulation_xnor.v
new file mode 100644
index 00000000..7286d134
--- /dev/null
+++ b/tests/hana/test_simulation_xnor.v
@@ -0,0 +1,20 @@
+
+// test_simulation_xnor_1_test.v
+module f1_test(input [1:0] in, output out);
+assign out = ~(in[0] ^ in[1]);
+endmodule
+
+// test_simulation_xnor_2_test.v
+module f2_test(input [2:0] in, output out);
+assign out = ~(in[0] ^ in[1] ^ in[2]);
+endmodule
+
+// test_simulation_xnor_3_test.v
+module f3_test(input [3:0] in, output out);
+assign out = ~(in[0] ^ in[1] ^ in[2] ^ in[3]);
+endmodule
+
+// test_simulation_xnor_4_test.v
+module f4_test(input [3:0] in, output out);
+xnor myxnor(out, in[0], in[1], in[2], in[3]);
+endmodule
diff --git a/tests/hana/test_simulation_xor.v b/tests/hana/test_simulation_xor.v
new file mode 100644
index 00000000..e181dd83
--- /dev/null
+++ b/tests/hana/test_simulation_xor.v
@@ -0,0 +1,20 @@
+
+// test_simulation_xor_1_test.v
+module f1_test(input [1:0] in, output out);
+assign out = (in[0] ^ in[1]);
+endmodule
+
+// test_simulation_xor_2_test.v
+module f2_test(input [2:0] in, output out);
+assign out = (in[0] ^ in[1] ^ in[2]);
+endmodule
+
+// test_simulation_xor_3_test.v
+module f3_test(input [3:0] in, output out);
+assign out = (in[0] ^ in[1] ^ in[2] ^ in[3]);
+endmodule
+
+// test_simulation_xor_4_test.v
+module f4_test(input [3:0] in, output out);
+xor myxor(out, in[0], in[1], in[2], in[3]);
+endmodule
diff --git a/tests/memories/.gitignore b/tests/memories/.gitignore
new file mode 100644
index 00000000..90a0983a
--- /dev/null
+++ b/tests/memories/.gitignore
@@ -0,0 +1,3 @@
+*.log
+*.out
+*.dmp
diff --git a/tests/memories/amber23_sram_byte_en.v b/tests/memories/amber23_sram_byte_en.v
new file mode 100644
index 00000000..3554af88
--- /dev/null
+++ b/tests/memories/amber23_sram_byte_en.v
@@ -0,0 +1,84 @@
+//////////////////////////////////////////////////////////////////
+// //
+// Generic Library SRAM with per byte write enable //
+// //
+// This file is part of the Amber project //
+// http://www.opencores.org/project,amber //
+// //
+// Description //
+// Configurable depth and width. The DATA_WIDTH must be a //
+// multiple of 8. //
+// //
+// Author(s): //
+// - Conor Santifort, csantifort.amber@gmail.com //
+// //
+//////////////////////////////////////////////////////////////////
+// //
+// Copyright (C) 2010 Authors and OPENCORES.ORG //
+// //
+// This source file may be used and distributed without //
+// restriction provided that this copyright statement is not //
+// removed from the file and that any derivative work contains //
+// the original copyright notice and the associated disclaimer. //
+// //
+// This source file is free software; you can redistribute it //
+// and/or modify it under the terms of the GNU Lesser General //
+// Public License as published by the Free Software Foundation; //
+// either version 2.1 of the License, or (at your option) any //
+// later version. //
+// //
+// This source is distributed in the hope that it will be //
+// useful, but WITHOUT ANY WARRANTY; without even the implied //
+// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //
+// PURPOSE. See the GNU Lesser General Public License for more //
+// details. //
+// //
+// You should have received a copy of the GNU Lesser General //
+// Public License along with this source; if not, download it //
+// from http://www.opencores.org/lgpl.shtml //
+// //
+//////////////////////////////////////////////////////////////////
+
+// expect-wr-ports 1
+// expect-rd-ports 1
+
+module generic_sram_byte_en
+#(
+parameter DATA_WIDTH = 32,
+parameter ADDRESS_WIDTH = 4
+)
+
+(
+input i_clk,
+input [DATA_WIDTH-1:0] i_write_data,
+input i_write_enable,
+input [ADDRESS_WIDTH-1:0] i_address,
+input [DATA_WIDTH/8-1:0] i_byte_enable,
+output reg [DATA_WIDTH-1:0] o_read_data
+ );
+
+reg [DATA_WIDTH-1:0] mem [0:2**ADDRESS_WIDTH-1];
+integer i;
+
+always @(posedge i_clk)
+ begin
+ // read
+ o_read_data <= i_write_enable ? {DATA_WIDTH{1'd0}} : mem[i_address];
+
+ // write
+ if (i_write_enable)
+ for (i=0;i<DATA_WIDTH/8;i=i+1)
+ begin
+ mem[i_address][i*8+0] <= i_byte_enable[i] ? i_write_data[i*8+0] : mem[i_address][i*8+0] ;
+ mem[i_address][i*8+1] <= i_byte_enable[i] ? i_write_data[i*8+1] : mem[i_address][i*8+1] ;
+ mem[i_address][i*8+2] <= i_byte_enable[i] ? i_write_data[i*8+2] : mem[i_address][i*8+2] ;
+ mem[i_address][i*8+3] <= i_byte_enable[i] ? i_write_data[i*8+3] : mem[i_address][i*8+3] ;
+ mem[i_address][i*8+4] <= i_byte_enable[i] ? i_write_data[i*8+4] : mem[i_address][i*8+4] ;
+ mem[i_address][i*8+5] <= i_byte_enable[i] ? i_write_data[i*8+5] : mem[i_address][i*8+5] ;
+ mem[i_address][i*8+6] <= i_byte_enable[i] ? i_write_data[i*8+6] : mem[i_address][i*8+6] ;
+ mem[i_address][i*8+7] <= i_byte_enable[i] ? i_write_data[i*8+7] : mem[i_address][i*8+7] ;
+ end
+ end
+
+endmodule
+
diff --git a/tests/memories/implicit_en.v b/tests/memories/implicit_en.v
new file mode 100644
index 00000000..cfce378b
--- /dev/null
+++ b/tests/memories/implicit_en.v
@@ -0,0 +1,24 @@
+// expect-wr-ports 1
+// expect-rd-ports 1
+
+module test(clk, rd_addr, rd_data, wr_addr, wr_en, wr_data);
+
+input clk;
+
+input [3:0] rd_addr;
+output reg [31:0] rd_data;
+
+input [3:0] wr_addr, wr_en;
+input [31:0] wr_data;
+
+reg [31:0] mem [0:15];
+
+always @(posedge clk) begin
+ mem[wr_addr][ 7: 0] <= wr_en[0] ? wr_data[ 7: 0] : mem[wr_addr][ 7: 0];
+ mem[wr_addr][15: 8] <= wr_en[1] ? wr_data[15: 8] : mem[wr_addr][15: 8];
+ mem[wr_addr][23:16] <= wr_en[2] ? wr_data[23:16] : mem[wr_addr][23:16];
+ mem[wr_addr][31:24] <= wr_en[3] ? wr_data[31:24] : mem[wr_addr][31:24];
+ rd_data <= mem[rd_addr];
+end
+
+endmodule
diff --git a/tests/memories/no_implicit_en.v b/tests/memories/no_implicit_en.v
new file mode 100644
index 00000000..0e96e4ae
--- /dev/null
+++ b/tests/memories/no_implicit_en.v
@@ -0,0 +1,24 @@
+// expect-wr-ports 1
+// expect-rd-ports 2
+
+module test(clk, rd_addr, rd_data, cp_addr, wr_addr, wr_en, wr_data);
+
+input clk;
+
+input [3:0] rd_addr;
+output reg [31:0] rd_data;
+
+input [3:0] cp_addr, wr_addr, wr_en;
+input [31:0] wr_data;
+
+reg [31:0] mem [0:15];
+
+always @(posedge clk) begin
+ mem[wr_addr][ 7: 0] <= wr_en[0] ? wr_data[ 7: 0] : mem[cp_addr][ 7: 0];
+ mem[wr_addr][15: 8] <= wr_en[1] ? wr_data[15: 8] : mem[cp_addr][15: 8];
+ mem[wr_addr][23:16] <= wr_en[2] ? wr_data[23:16] : mem[cp_addr][23:16];
+ mem[wr_addr][31:24] <= wr_en[3] ? wr_data[31:24] : mem[cp_addr][31:24];
+ rd_data <= mem[rd_addr];
+end
+
+endmodule
diff --git a/tests/memories/run-test.sh b/tests/memories/run-test.sh
new file mode 100755
index 00000000..734a9668
--- /dev/null
+++ b/tests/memories/run-test.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+set -e
+
+OPTIND=1
+seed="" # default to no seed specified
+while getopts "S:" opt
+do
+ case "$opt" in
+ S) seed="-S $OPTARG" ;;
+ esac
+done
+shift "$((OPTIND-1))"
+
+bash ../tools/autotest.sh $seed -G *.v
+
+for f in `egrep -l 'expect-(wr|rd)-ports' *.v`; do
+ echo -n "Testing expectations for $f .."
+ ../../yosys -qp "proc; opt; memory -nomap;; dump -outfile ${f%.v}.dmp t:\$mem" $f
+ if grep -q expect-wr-ports $f; then
+ grep -q "parameter \\\\WR_PORTS $(gawk '/expect-wr-ports/ { print $3; }' $f)\$" ${f%.v}.dmp ||
+ { echo " ERROR: Unexpected number of write ports."; false; }
+ fi
+ if grep -q expect-rd-ports $f; then
+ grep -q "parameter \\\\RD_PORTS $(gawk '/expect-rd-ports/ { print $3; }' $f)\$" ${f%.v}.dmp ||
+ { echo " ERROR: Unexpected number of read ports."; false; }
+ fi
+ echo " ok."
+done
+
diff --git a/tests/memories/shared_ports.v b/tests/memories/shared_ports.v
new file mode 100644
index 00000000..94bad53e
--- /dev/null
+++ b/tests/memories/shared_ports.v
@@ -0,0 +1,25 @@
+// expect-wr-ports 1
+// expect-rd-ports 1
+
+module test(
+ input clk,
+ input wr_en1, wr_en2, wr_en3,
+ input [3:0] wr_addr1, wr_addr2, wr_addr3,
+ input [15:0] wr_data,
+ input [3:0] rd_addr,
+ output reg [31:0] rd_data
+);
+
+reg [31:0] mem [0:15];
+
+always @(posedge clk) begin
+ if (wr_en1)
+ mem[wr_addr1][15:0] <= wr_data;
+ else if (wr_en2)
+ mem[wr_addr2][23:8] <= wr_data;
+ else if (wr_en3)
+ mem[wr_addr3][31:16] <= wr_data;
+ rd_data <= mem[rd_addr];
+end
+
+endmodule
diff --git a/tests/memories/simple_sram_byte_en.v b/tests/memories/simple_sram_byte_en.v
new file mode 100644
index 00000000..dee1d228
--- /dev/null
+++ b/tests/memories/simple_sram_byte_en.v
@@ -0,0 +1,26 @@
+// expect-wr-ports 1
+// expect-rd-ports 1
+
+module generic_sram_byte_en #(
+ parameter DATA_WIDTH = 32,
+ parameter ADDRESS_WIDTH = 4
+) (
+ input i_clk,
+ input [DATA_WIDTH-1:0] i_write_data,
+ input i_write_enable,
+ input [ADDRESS_WIDTH-1:0] i_address,
+ input [DATA_WIDTH/8-1:0] i_byte_enable,
+ output reg [DATA_WIDTH-1:0] o_read_data
+);
+
+reg [DATA_WIDTH-1:0] mem [0:2**ADDRESS_WIDTH-1];
+integer i;
+
+always @(posedge i_clk) begin
+ for (i=0;i<DATA_WIDTH/8;i=i+1)
+ if (i_write_enable && i_byte_enable[i])
+ mem[i_address][i*8 +: 8] <= i_write_data[i*8 +: 8];
+ o_read_data <= mem[i_address];
+end
+
+endmodule
diff --git a/tests/realmath/.gitignore b/tests/realmath/.gitignore
new file mode 100644
index 00000000..9c595a6f
--- /dev/null
+++ b/tests/realmath/.gitignore
@@ -0,0 +1 @@
+temp
diff --git a/tests/realmath/generate.py b/tests/realmath/generate.py
new file mode 100644
index 00000000..2bedf38e
--- /dev/null
+++ b/tests/realmath/generate.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python3
+
+import argparse
+import sys
+import random
+from contextlib import contextmanager
+
+@contextmanager
+def redirect_stdout(new_target):
+ old_target, sys.stdout = sys.stdout, new_target
+ try:
+ yield new_target
+ finally:
+ sys.stdout = old_target
+
+def random_expression(depth = 3, maxparam = 0):
+ def recursion():
+ return random_expression(depth = depth-1, maxparam = maxparam)
+ if depth == 0:
+ if maxparam != 0 and random.randint(0, 1) != 0:
+ return 'p%02d' % random.randint(0, maxparam-1)
+ return random.choice([ '%e', '%f', '%g' ]) % random.uniform(-2, +2)
+ if random.randint(0, 4) == 0:
+ return recursion() + random.choice([ ' < ', ' <= ', ' == ', ' != ', ' >= ', ' > ' ]) + recursion() + ' ? ' + recursion() + ' : ' + recursion()
+ op_prefix = [ '+(', '-(' ]
+ op_infix = [ ' + ', ' - ', ' * ', ' / ' ]
+ op_func1 = [ '$ln', '$log10', '$exp', '$sqrt', '$floor', '$ceil', '$sin', '$cos', '$tan', '$asin', '$acos', '$atan', '$sinh', '$cosh', '$tanh', '$asinh', '$acosh', '$atanh' ]
+ op_func2 = [ '$pow', '$atan2', '$hypot' ]
+ op = random.choice(op_prefix + op_infix + op_func1 + op_func2)
+ if op in op_prefix:
+ return op + recursion() + ')'
+ if op in op_infix:
+ return '(' + recursion() + op + recursion() + ')'
+ if op in op_func1:
+ return op + '(' + recursion() + ')'
+ if op in op_func2:
+ return op + '(' + recursion() + ', ' + recursion() + ')'
+ raise
+
+parser = argparse.ArgumentParser(formatter_class = argparse.ArgumentDefaultsHelpFormatter)
+parser.add_argument('-S', '--seed', type = int, help = 'seed for PRNG')
+parser.add_argument('-c', '--count', type = int, default = 100, help = 'number of test cases to generate')
+args = parser.parse_args()
+
+if args.seed is not None:
+ print("PRNG seed: %d" % args.seed)
+ random.seed(args.seed)
+
+for idx in range(args.count):
+ with open('temp/uut_%05d.v' % idx, 'w') as f:
+ with redirect_stdout(f):
+ print('module uut_%05d(output [63:0] %s);\n' % (idx, ', '.join(['y%02d' % i for i in range(100)])))
+ for i in range(30):
+ if idx < 10:
+ print('localparam p%02d = %s;' % (i, random_expression()))
+ else:
+ print('localparam%s p%02d = %s;' % (random.choice(['', ' real', ' integer']), i, random_expression()))
+ for i in range(30, 60):
+ if idx < 10:
+ print('localparam p%02d = %s;' % (i, random_expression(maxparam = 30)))
+ else:
+ print('localparam%s p%02d = %s;' % (random.choice(['', ' real', ' integer']), i, random_expression(maxparam = 30)))
+ for i in range(100):
+ print('assign y%02d = 65536 * (%s);' % (i, random_expression(maxparam = 60)))
+ print('endmodule')
+ with open('temp/uut_%05d.ys' % idx, 'w') as f:
+ with redirect_stdout(f):
+ print('read_verilog uut_%05d.v' % idx)
+ print('rename uut_%05d uut_%05d_syn' % (idx, idx))
+ print('write_verilog uut_%05d_syn.v' % idx)
+ with open('temp/uut_%05d_tb.v' % idx, 'w') as f:
+ with redirect_stdout(f):
+ print('module uut_%05d_tb;\n' % idx)
+ print('wire [63:0] %s;' % (', '.join(['r%02d' % i for i in range(100)])))
+ print('wire [63:0] %s;' % (', '.join(['s%02d' % i for i in range(100)])))
+ print('uut_%05d ref(%s);' % (idx, ', '.join(['r%02d' % i for i in range(100)])))
+ print('uut_%05d_syn syn(%s);' % (idx, ', '.join(['s%02d' % i for i in range(100)])))
+ print('task compare_ref_syn;')
+ print(' input [7:0] i;')
+ print(' input [63:0] r, s;')
+ print(' reg [64*8-1:0] buffer;')
+ print(' integer j;')
+ print(' begin')
+ print(' if (-1 <= $signed(r-s) && $signed(r-s) <= +1) begin')
+ print(' // $display("%d: %b %b", i, r, s);')
+ print(' end else if (r === s) begin ')
+ print(' // $display("%d: %b %b", i, r, s);')
+ print(' end else begin ')
+ print(' for (j = 0; j < 64; j = j+1)')
+ print(' buffer[j*8 +: 8] = r[j] !== s[j] ? "^" : " ";')
+ print(' $display("\\n%3d: %b %b", i, r, s);')
+ print(' $display(" %s %s", buffer, buffer);')
+ print(' end')
+ print(' end')
+ print('endtask')
+ print('initial begin #1;')
+ for i in range(100):
+ print(' compare_ref_syn(%2d, r%02d, s%02d);' % (i, i, i))
+ print('end')
+ print('endmodule')
+
diff --git a/tests/realmath/run-test.sh b/tests/realmath/run-test.sh
new file mode 100755
index 00000000..e1a36c69
--- /dev/null
+++ b/tests/realmath/run-test.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+set -e
+
+OPTIND=1
+count=100
+seed="" # default to no seed specified
+while getopts "c:S:" opt
+do
+ case "$opt" in
+ c) count="$OPTARG" ;;
+ S) seed="-S $OPTARG" ;;
+ esac
+done
+shift "$((OPTIND-1))"
+
+rm -rf temp
+mkdir -p temp
+echo "generating tests.."
+python3 generate.py -c $count $seed
+
+cd temp
+echo "running tests.."
+for ((i = 0; i < $count; i++)); do
+ echo -n "[$i]"
+ idx=$( printf "%05d" $i )
+ ../../../yosys -qq uut_${idx}.ys
+ iverilog -o uut_${idx}_tb uut_${idx}_tb.v uut_${idx}.v uut_${idx}_syn.v
+ ./uut_${idx}_tb | tee uut_${idx}.err
+ if test -s uut_${idx}.err; then
+ echo "Note: Make sure that 'iverilog' is an up-to-date git checkout of Icarus Verilog."
+ exit 1
+ fi
+ rm -f uut_${idx}.err
+done
+echo
+
diff --git a/tests/sat/.gitignore b/tests/sat/.gitignore
new file mode 100644
index 00000000..397b4a76
--- /dev/null
+++ b/tests/sat/.gitignore
@@ -0,0 +1 @@
+*.log
diff --git a/tests/sat/asserts.v b/tests/sat/asserts.v
new file mode 100644
index 00000000..c6f8095e
--- /dev/null
+++ b/tests/sat/asserts.v
@@ -0,0 +1,14 @@
+// http://www.reddit.com/r/yosys/comments/1vljks/new_support_for_systemveriloglike_asserts/
+module test(input clk, input rst, output y);
+reg [2:0] state;
+always @(posedge clk) begin
+ if (rst || state == 3) begin
+ state <= 0;
+ end else begin
+ assert(state < 3);
+ state <= state + 1;
+ end
+end
+assign y = state[2];
+assert property (y !== 1'b1);
+endmodule
diff --git a/tests/sat/asserts.ys b/tests/sat/asserts.ys
new file mode 100644
index 00000000..d8f99492
--- /dev/null
+++ b/tests/sat/asserts.ys
@@ -0,0 +1,3 @@
+read_verilog -sv asserts.v
+hierarchy; proc; opt
+sat -verify -seq 1 -set-at 1 rst 1 -tempinduct -prove-asserts
diff --git a/tests/sat/asserts_seq.v b/tests/sat/asserts_seq.v
new file mode 100644
index 00000000..9715104f
--- /dev/null
+++ b/tests/sat/asserts_seq.v
@@ -0,0 +1,87 @@
+module test_001(clk, a, a_old, b);
+ // test case taken from:
+ // http://www.reddit.com/r/yosys/comments/1wvpj6/trouble_with_assertions_and_sat_solver/
+
+ input wire clk;
+ input wire a;
+
+ output reg a_old = 0;
+ output reg b = 1;
+ wire assertion = (a_old != b);
+
+ always @(posedge clk) begin
+ a_old <= a;
+ b <= !a;
+
+ assert(a_old != b);
+ end
+endmodule
+
+module test_002(clk, a, a_old, b);
+ // copy from test_001 with modifications
+
+ input wire clk;
+ input wire a;
+
+ output reg a_old = 0;
+ output reg b = 0; // <-- this will fail
+ wire assertion = (a_old != b);
+
+ always @(posedge clk) begin
+ a_old <= a;
+ b <= !a;
+ assert(a_old != b);
+ end
+endmodule
+
+module test_003(clk, a, a_old, b);
+ // copy from test_001 with modifications
+
+ input wire clk;
+ input wire a;
+
+ output reg a_old = 0;
+ output reg b; // <-- this will fail
+ wire assertion = (a_old != b);
+
+ always @(posedge clk) begin
+ a_old <= a;
+ b <= !a;
+ assert(a_old != b);
+ end
+endmodule
+
+module test_004(clk, a, a_old, b);
+ // copy from test_001 with modifications
+
+ input wire clk;
+ input wire a;
+
+ output reg a_old = 0;
+ output reg b = 1;
+ wire assertion = (a_old != b);
+
+ always @(posedge clk) begin
+ a_old <= a;
+ b <= !a;
+ assert(a_old == b); // <-- this will fail
+ end
+endmodule
+
+module test_005(clk, a, a_old, b);
+ // copy from test_001 with modifications
+
+ input wire clk;
+ input wire a;
+
+ output reg a_old = 1; // <-- inverted, no problem
+ output reg b = 0;
+ wire assertion = (a_old != b);
+
+ always @(posedge clk) begin
+ a_old <= a;
+ b <= !a;
+ assert(a_old != b);
+ end
+endmodule
+
diff --git a/tests/sat/asserts_seq.ys b/tests/sat/asserts_seq.ys
new file mode 100644
index 00000000..e9768664
--- /dev/null
+++ b/tests/sat/asserts_seq.ys
@@ -0,0 +1,15 @@
+read_verilog -sv asserts_seq.v
+hierarchy; proc; opt
+
+sat -verify -prove-asserts -tempinduct -seq 1 test_001
+sat -falsify -prove-asserts -tempinduct -seq 1 test_002
+sat -falsify -prove-asserts -tempinduct -seq 1 test_003
+sat -falsify -prove-asserts -tempinduct -seq 1 test_004
+sat -verify -prove-asserts -tempinduct -seq 1 test_005
+
+sat -verify -prove-asserts -seq 2 test_001
+sat -falsify -prove-asserts -seq 2 test_002
+sat -falsify -prove-asserts -seq 2 test_003
+sat -falsify -prove-asserts -seq 2 test_004
+sat -verify -prove-asserts -seq 2 test_005
+
diff --git a/tests/sat/counters.v b/tests/sat/counters.v
new file mode 100644
index 00000000..09e27304
--- /dev/null
+++ b/tests/sat/counters.v
@@ -0,0 +1,35 @@
+
+module counter1(clk, rst, ping);
+ input clk, rst;
+ output ping;
+ reg [31:0] count;
+
+ always @(posedge clk) begin
+ if (rst)
+ count <= 0;
+ else
+ count <= count + 1;
+ end
+
+ assign ping = &count;
+endmodule
+
+module counter2(clk, rst, ping);
+ input clk, rst;
+ output ping;
+ reg [31:0] count;
+
+ integer i;
+ reg carry;
+
+ always @(posedge clk) begin
+ carry = 1;
+ for (i = 0; i < 32; i = i+1) begin
+ count[i] <= !rst & (count[i] ^ carry);
+ carry = count[i] & carry;
+ end
+ end
+
+ assign ping = &count;
+endmodule
+
diff --git a/tests/sat/counters.ys b/tests/sat/counters.ys
new file mode 100644
index 00000000..330895f8
--- /dev/null
+++ b/tests/sat/counters.ys
@@ -0,0 +1,10 @@
+
+read_verilog counters.v
+proc; opt
+
+expose -shared counter1 counter2
+miter -equiv -make_assert -make_outputs counter1 counter2 miter
+
+cd miter; flatten; opt
+sat -verify -prove-asserts -tempinduct -set-at 1 in_rst 1 -seq 1 -show-inputs -show-outputs
+
diff --git a/tests/sat/expose_dff.v b/tests/sat/expose_dff.v
new file mode 100644
index 00000000..708e2da3
--- /dev/null
+++ b/tests/sat/expose_dff.v
@@ -0,0 +1,33 @@
+
+module test1(input clk, input [3:0] a, output reg [3:0] y);
+always @(posedge clk)
+ y <= a;
+endmodule
+
+module test2(input clk, input [3:0] a, output reg [3:0] y);
+wire clk_n = !clk;
+always @(negedge clk_n)
+ y[1:0] <= a[1:0];
+always @(negedge clk_n)
+ y[3:2] <= a[3:2];
+endmodule
+
+// -----------------------------------------------------------
+
+module test3(input clk, rst, input [3:0] a, output reg [3:0] y);
+always @(posedge clk, posedge rst)
+ if (rst)
+ y <= 12;
+ else
+ y <= |a;
+endmodule
+
+module test4(input clk, rst, input [3:0] a, output reg [3:0] y);
+wire rst_n = !rst;
+always @(posedge clk, negedge rst_n)
+ if (!rst_n)
+ y <= 12;
+ else
+ y <= a != 0;
+endmodule
+
diff --git a/tests/sat/expose_dff.ys b/tests/sat/expose_dff.ys
new file mode 100644
index 00000000..95556840
--- /dev/null
+++ b/tests/sat/expose_dff.ys
@@ -0,0 +1,15 @@
+
+read_verilog expose_dff.v
+hierarchy; proc;;
+
+expose -shared -evert-dff test1 test2
+miter -equiv test1 test2 miter12
+flatten miter12; opt miter12
+
+expose -shared -evert-dff test3 test4
+miter -equiv test3 test4 miter34
+flatten miter34; opt miter34
+
+sat -verify -prove trigger 0 miter12
+sat -verify -prove trigger 0 miter34
+
diff --git a/tests/sat/initval.v b/tests/sat/initval.v
new file mode 100644
index 00000000..5b661f8d
--- /dev/null
+++ b/tests/sat/initval.v
@@ -0,0 +1,15 @@
+module test(input clk, input [3:0] bar, output [3:0] foo);
+ reg [3:0] foo = 0;
+ reg [3:0] last_bar = 0;
+
+ always @*
+ foo[1:0] <= bar[1:0];
+
+ always @(posedge clk)
+ foo[3:2] <= bar[3:2];
+
+ always @(posedge clk)
+ last_bar <= bar;
+
+ assert property (foo == {last_bar[3:2], bar[1:0]});
+endmodule
diff --git a/tests/sat/initval.ys b/tests/sat/initval.ys
new file mode 100644
index 00000000..2079d2f3
--- /dev/null
+++ b/tests/sat/initval.ys
@@ -0,0 +1,4 @@
+read_verilog -sv initval.v
+proc;;
+
+sat -seq 10 -prove-asserts
diff --git a/tests/sat/run-test.sh b/tests/sat/run-test.sh
new file mode 100755
index 00000000..67e1beb2
--- /dev/null
+++ b/tests/sat/run-test.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -e
+for x in *.ys; do
+ echo "Running $x.."
+ ../../yosys -ql ${x%.ys}.log $x
+done
diff --git a/tests/sat/share.v b/tests/sat/share.v
new file mode 100644
index 00000000..e06fc8f1
--- /dev/null
+++ b/tests/sat/share.v
@@ -0,0 +1,32 @@
+module test_1(
+ input [7:0] a, b, c,
+ input s, x,
+ output [7:0] y1, y2
+);
+ wire [7:0] t1, t2;
+ assign t1 = s ? a*b : 0, t2 = !s ? b*c : 0;
+ assign y1 = x ? t2 : t1, y2 = x ? t1 : t2;
+endmodule
+
+
+module test_2(
+ input s,
+ input [7:0] a, b, c,
+ output reg [7:0] y
+);
+ always @* begin
+ y <= 'bx;
+ if (s) begin
+ if (a * b > 8)
+ y <= b / c;
+ else
+ y <= c / b;
+ end else begin
+ if (b * c > 8)
+ y <= a / b;
+ else
+ y <= b / a;
+ end
+ end
+endmodule
+
diff --git a/tests/sat/share.ys b/tests/sat/share.ys
new file mode 100644
index 00000000..f2f5d649
--- /dev/null
+++ b/tests/sat/share.ys
@@ -0,0 +1,17 @@
+read_verilog share.v
+proc;;
+
+copy test_1 gold_1
+copy test_2 gold_2
+share test_1 test_2;;
+
+select -assert-count 1 test_1/t:$mul
+select -assert-count 1 test_2/t:$mul
+select -assert-count 1 test_2/t:$div
+
+miter -equiv -flatten -make_outputs -make_outcmp gold_1 test_1 miter_1
+sat -verify -prove trigger 0 -show-inputs -show-outputs miter_1
+
+miter -equiv -flatten -make_outputs -make_outcmp gold_2 test_2 miter_2
+sat -verify -prove trigger 0 -show-inputs -show-outputs miter_2
+
diff --git a/tests/sat/splice.v b/tests/sat/splice.v
new file mode 100644
index 00000000..8d1dcd22
--- /dev/null
+++ b/tests/sat/splice.v
@@ -0,0 +1,14 @@
+module test(a, b, y);
+
+input [15:0] a, b;
+output [15:0] y;
+
+wire [7:0] ah = a[15:8], al = a[7:0];
+wire [7:0] bh = b[15:8], bl = b[7:0];
+
+wire [7:0] th = ah + bh, tl = al + bl;
+wire [15:0] t = {th, tl}, k = t ^ 16'hcd;
+
+assign y = { k[7:0], k[15:8] };
+
+endmodule
diff --git a/tests/sat/splice.ys b/tests/sat/splice.ys
new file mode 100644
index 00000000..365a4e2f
--- /dev/null
+++ b/tests/sat/splice.ys
@@ -0,0 +1,14 @@
+read_verilog splice.v
+hierarchy -check; opt
+copy test gold
+
+cd test
+splice
+# show
+
+cd ..
+rename test gate
+miter -equiv -make_assert -make_outputs gold gate miter
+
+flatten miter
+sat -verify -prove-asserts -show-inputs -show-outputs miter
diff --git a/tests/share/.gitignore b/tests/share/.gitignore
new file mode 100644
index 00000000..9c595a6f
--- /dev/null
+++ b/tests/share/.gitignore
@@ -0,0 +1 @@
+temp
diff --git a/tests/share/generate.py b/tests/share/generate.py
new file mode 100644
index 00000000..7e87bd64
--- /dev/null
+++ b/tests/share/generate.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python3
+
+import argparse
+import sys
+import random
+from contextlib import contextmanager
+
+@contextmanager
+def redirect_stdout(new_target):
+ old_target, sys.stdout = sys.stdout, new_target
+ try:
+ yield new_target
+ finally:
+ sys.stdout = old_target
+
+def random_plus_x():
+ return "%s x" % random.choice(['+', '+', '+', '-', '-', '|', '&', '^'])
+
+def maybe_plus_x(expr):
+ if random.randint(0, 4) == 0:
+ return "(%s %s)" % (expr, random_plus_x())
+ else:
+ return expr
+
+parser = argparse.ArgumentParser(formatter_class = argparse.ArgumentDefaultsHelpFormatter)
+parser.add_argument('-S', '--seed', type = int, help = 'seed for PRNG')
+parser.add_argument('-c', '--count', type = int, default = 100, help = 'number of test cases to generate')
+args = parser.parse_args()
+
+if args.seed is not None:
+ print("PRNG seed: %d" % args.seed)
+ random.seed(args.seed)
+
+for idx in range(args.count):
+ with open('temp/uut_%05d.v' % idx, 'w') as f:
+ with redirect_stdout(f):
+ if random.choice(['bin', 'uni']) == 'bin':
+ print('module uut_%05d(a, b, c, d, x, s, y);' % (idx))
+ op = random.choice([
+ random.choice(['+', '-', '*', '/', '%']),
+ random.choice(['<', '<=', '==', '!=', '===', '!==', '>=', '>' ]),
+ random.choice(['<<', '>>', '<<<', '>>>']),
+ random.choice(['|', '&', '^', '~^', '||', '&&']),
+ ])
+ print(' input%s [%d:0] a;' % (random.choice(['', ' signed']), random.randint(0, 8)))
+ print(' input%s [%d:0] b;' % (random.choice(['', ' signed']), random.randint(0, 8)))
+ print(' input%s [%d:0] c;' % (random.choice(['', ' signed']), random.randint(0, 8)))
+ print(' input%s [%d:0] d;' % (random.choice(['', ' signed']), random.randint(0, 8)))
+ print(' input%s [%d:0] x;' % (random.choice(['', ' signed']), random.randint(0, 8)))
+ print(' input s;')
+ print(' output [%d:0] y;' % random.randint(0, 8))
+ print(' assign y = (s ? %s(%s %s %s) : %s(%s %s %s))%s;' %
+ (random.choice(['', '$signed', '$unsigned']), maybe_plus_x('a'), op, maybe_plus_x('b'),
+ random.choice(['', '$signed', '$unsigned']), maybe_plus_x('c'), op, maybe_plus_x('d'),
+ random_plus_x() if random.randint(0, 4) == 0 else ''))
+ print('endmodule')
+ else:
+ print('module uut_%05d(a, b, x, s, y);' % (idx))
+ op = random.choice(['~', '-', '!'])
+ print(' input%s [%d:0] a;' % (random.choice(['', ' signed']), random.randint(0, 8)))
+ print(' input%s [%d:0] b;' % (random.choice(['', ' signed']), random.randint(0, 8)))
+ print(' input%s [%d:0] x;' % (random.choice(['', ' signed']), random.randint(0, 8)))
+ print(' input s;')
+ print(' output [%d:0] y;' % random.randint(0, 8))
+ print(' assign y = (s ? %s(%s%s) : %s(%s%s))%s;' %
+ (random.choice(['', '$signed', '$unsigned']), op, maybe_plus_x('a'),
+ random.choice(['', '$signed', '$unsigned']), op, maybe_plus_x('b'),
+ random_plus_x() if random.randint(0, 4) == 0 else ''))
+ print('endmodule')
+ with open('temp/uut_%05d.ys' % idx, 'w') as f:
+ with redirect_stdout(f):
+ print('read_verilog temp/uut_%05d.v' % idx)
+ print('proc;;')
+ print('copy uut_%05d gold' % idx)
+ print('rename uut_%05d gate' % idx)
+ print('tee -a temp/all_share_log.txt log')
+ print('tee -a temp/all_share_log.txt log #job# uut_%05d' % idx)
+ print('tee -a temp/all_share_log.txt wreduce')
+ print('tee -a temp/all_share_log.txt share -aggressive gate')
+ print('miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp gold gate miter')
+ print('sat -set-def-inputs -verify -prove trigger 0 -show-inputs -show-outputs miter')
+
diff --git a/tests/share/run-test.sh b/tests/share/run-test.sh
new file mode 100755
index 00000000..1bcd8e42
--- /dev/null
+++ b/tests/share/run-test.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+# run this test many times:
+# time bash -c 'for ((i=0; i<100; i++)); do echo "-- $i --"; bash run-test.sh || exit 1; done'
+
+set -e
+
+OPTIND=1
+count=100
+seed="" # default to no seed specified
+while getopts "c:S:" opt
+do
+ case "$opt" in
+ c) count="$OPTARG" ;;
+ S) seed="-S $OPTARG" ;;
+ esac
+done
+shift "$((OPTIND-1))"
+
+rm -rf temp
+mkdir -p temp
+echo "generating tests.."
+python3 generate.py -c $count $seed
+
+echo "running tests.."
+for i in $( ls temp/*.ys | sed 's,[^0-9],,g; s,^0*\(.\),\1,g;' ); do
+ echo -n "[$i]"
+ idx=$( printf "%05d" $i )
+ ../../yosys -ql temp/uut_${idx}.log temp/uut_${idx}.ys
+done
+echo
+
+failed_share=$( echo $( gawk '/^#job#/ { j=$2; db[j]=0; } /^Removing [246] cells/ { delete db[j]; } END { for (j in db) print(j); }' temp/all_share_log.txt ) )
+if [ -n "$failed_share" ]; then
+ echo "Resource sharing failed for the following test cases: $failed_share"
+ false
+fi
+
+exit 0
diff --git a/tests/simple/.gitignore b/tests/simple/.gitignore
new file mode 100644
index 00000000..073f4615
--- /dev/null
+++ b/tests/simple/.gitignore
@@ -0,0 +1,2 @@
+*.log
+*.out
diff --git a/tests/simple/aes_kexp128.v b/tests/simple/aes_kexp128.v
new file mode 100644
index 00000000..3ee03478
--- /dev/null
+++ b/tests/simple/aes_kexp128.v
@@ -0,0 +1,24 @@
+
+// test taken from aes_core from iwls2005
+
+module aes_key_expand_128(clk, kld, key, wo_0, wo_1, wo_2, wo_3);
+
+input clk, kld;
+input [15:0] key;
+output [3:0] wo_0, wo_1, wo_2, wo_3;
+reg [3:0] w[3:0];
+
+assign wo_0 = w[0];
+assign wo_1 = w[1];
+assign wo_2 = w[2];
+assign wo_3 = w[3];
+
+always @(posedge clk) begin
+ w[0] <= kld ? key[15:12] : w[0];
+ w[1] <= kld ? key[11: 8] : w[0]^w[1];
+ w[2] <= kld ? key[ 7: 4] : w[0]^w[1]^w[2];
+ w[3] <= kld ? key[ 3: 0] : w[0]^w[1]^w[2]^w[3];
+end
+
+endmodule
+
diff --git a/tests/simple/always01.v b/tests/simple/always01.v
new file mode 100644
index 00000000..21379cb0
--- /dev/null
+++ b/tests/simple/always01.v
@@ -0,0 +1,10 @@
+module uut_always01(clock, reset, count);
+
+input clock, reset;
+output [3:0] count;
+reg [3:0] count;
+
+always @(posedge clock)
+ count <= reset ? 0 : count + 1;
+
+endmodule
diff --git a/tests/simple/always02.v b/tests/simple/always02.v
new file mode 100644
index 00000000..8c7ef0fb
--- /dev/null
+++ b/tests/simple/always02.v
@@ -0,0 +1,13 @@
+module uut_always02(clock, reset, count);
+
+input clock, reset;
+output [3:0] count;
+reg [3:0] count;
+
+always @(posedge clock) begin
+ count <= count + 1;
+ if (reset)
+ count <= 0;
+end
+
+endmodule
diff --git a/tests/simple/always03.v b/tests/simple/always03.v
new file mode 100644
index 00000000..5542175e
--- /dev/null
+++ b/tests/simple/always03.v
@@ -0,0 +1,22 @@
+module uut_always03(clock, in1, in2, in3, in4, in5, in6, in7, out1, out2, out3);
+
+input clock, in1, in2, in3, in4, in5, in6, in7;
+output out1, out2, out3;
+reg out1, out2, out3;
+
+always @(posedge clock) begin
+ out1 = in1;
+ if (in2)
+ out1 = !out1;
+ out2 <= out1;
+ if (in3)
+ out2 <= out2;
+ if (in4)
+ if (in5)
+ out3 <= in6;
+ else
+ out3 <= in7;
+ out1 = out1 ^ out2;
+end
+
+endmodule
diff --git a/tests/simple/arraycells.v b/tests/simple/arraycells.v
new file mode 100644
index 00000000..704ca3fd
--- /dev/null
+++ b/tests/simple/arraycells.v
@@ -0,0 +1,15 @@
+
+module array_test001(a, b, c, y);
+ input a;
+ input [31:0] b, c;
+ input [31:0] y;
+
+ aoi12 p [31:0] (a, b, c, y);
+endmodule
+
+module aoi12(a, b, c, y);
+ input a, b, c;
+ output y;
+ assign y = ~((a & b) | c);
+endmodule
+
diff --git a/tests/simple/arrays01.v b/tests/simple/arrays01.v
new file mode 100644
index 00000000..bd0eda29
--- /dev/null
+++ b/tests/simple/arrays01.v
@@ -0,0 +1,16 @@
+module uut_arrays01(clock, we, addr, wr_data, rd_data);
+
+input clock, we;
+input [3:0] addr, wr_data;
+output [3:0] rd_data;
+reg [3:0] rd_data;
+
+reg [3:0] memory [15:0];
+
+always @(posedge clock) begin
+ if (we)
+ memory[addr] <= wr_data;
+ rd_data <= memory[addr];
+end
+
+endmodule
diff --git a/tests/simple/carryadd.v b/tests/simple/carryadd.v
new file mode 100644
index 00000000..4f777f79
--- /dev/null
+++ b/tests/simple/carryadd.v
@@ -0,0 +1,24 @@
+module carryadd(a, b, y);
+
+parameter WIDTH = 8;
+
+input [WIDTH-1:0] a, b;
+output [WIDTH-1:0] y;
+
+genvar i;
+generate
+ for (i = 0; i < WIDTH; i = i+1) begin:STAGE
+ wire IN1 = a[i], IN2 = b[i];
+ wire C, Y;
+ if (i == 0)
+ assign C = IN1 & IN2, Y = IN1 ^ IN2;
+ else
+ assign C = (IN1 & IN2) | ((IN1 | IN2) & STAGE[i-1].C),
+ Y = IN1 ^ IN2 ^ STAGE[i-1].C;
+ assign y[i] = Y;
+ end
+endgenerate
+
+// assert property (y == a + b);
+
+endmodule
diff --git a/tests/simple/constmuldivmod.v b/tests/simple/constmuldivmod.v
new file mode 100644
index 00000000..d1d8be86
--- /dev/null
+++ b/tests/simple/constmuldivmod.v
@@ -0,0 +1,27 @@
+module constmuldivmod(input [7:0] A, input [2:0] mode, output reg [7:0] Y);
+ always @* begin
+ case (mode)
+ 0: Y = A / 8'd0;
+ 1: Y = A % 8'd0;
+ 2: Y = A * 8'd0;
+
+ 3: Y = A / 8'd1;
+ 4: Y = A % 8'd1;
+ 5: Y = A * 8'd1;
+
+ 6: Y = A / 8'd2;
+ 7: Y = A % 8'd2;
+ 8: Y = A * 8'd2;
+
+ 9: Y = A / 8'd4;
+ 10: Y = A % 8'd4;
+ 11: Y = A * 8'd4;
+
+ 12: Y = A / 8'd8;
+ 13: Y = A % 8'd8;
+ 14: Y = A * 8'd8;
+
+ default: Y = 8'd16 * A;
+ endcase
+ end
+endmodule
diff --git a/tests/simple/constpower.v b/tests/simple/constpower.v
new file mode 100644
index 00000000..451866a6
--- /dev/null
+++ b/tests/simple/constpower.v
@@ -0,0 +1,15 @@
+module constpower(ys, yu);
+
+output [8*8*8-1:0] ys, yu;
+
+genvar i, j;
+
+generate
+ for (i = 0; i < 8; i = i+1)
+ for (j = 0; j < 8; j = j+1) begin:V
+ assign ys[i*8 + j*64 + 7 : i*8 + j*64] = $signed(i-4) ** $signed(j-4);
+ assign yu[i*8 + j*64 + 7 : i*8 + j*64] = $unsigned(i) ** $unsigned(j);
+ end
+endgenerate
+
+endmodule
diff --git a/tests/simple/dff_different_styles.v b/tests/simple/dff_different_styles.v
new file mode 100644
index 00000000..7765d6e2
--- /dev/null
+++ b/tests/simple/dff_different_styles.v
@@ -0,0 +1,105 @@
+
+module dff(clk, d, q);
+input clk, d;
+output reg q;
+always @(posedge clk)
+ q <= d;
+endmodule
+
+module dffa(clk, arst, d, q);
+input clk, arst, d;
+output reg q;
+always @(posedge clk or posedge arst) begin
+ if (arst)
+ q <= 1;
+ else
+ q <= d;
+end
+endmodule
+
+module dffa1(clk, arst, d, q);
+input clk, arst, d;
+output reg q;
+always @(posedge clk or negedge arst) begin
+ if (~arst)
+ q <= 0;
+ else
+ q <= d;
+end
+endmodule
+
+module dffa2(clk, arst, d, q);
+input clk, arst, d;
+output reg q;
+always @(posedge clk or negedge arst) begin
+ if (!arst)
+ q <= 0;
+ else
+ q <= d;
+end
+endmodule
+
+module dffa3(clk, arst, d, q);
+input clk, arst, d;
+output reg q;
+always @(posedge clk or negedge arst) begin
+ if (~(!arst))
+ q <= d;
+ else
+ q <= 1;
+end
+endmodule
+
+module dffa4(clk, arst1, arst2, arst3, d, q);
+input clk, arst1, arst2, arst3, d;
+output reg q;
+always @(posedge clk, posedge arst1, posedge arst2, negedge arst3) begin
+ if (arst1)
+ q <= 0;
+ else if (arst2)
+ q <= 0;
+ else if (!arst3)
+ q <= 0;
+ else
+ q <= d;
+end
+endmodule
+
+// SR-Flip-Flops are on the edge of well defined Verilog constructs in terms of
+// simulation-implementation mismatches. The following testcases try to cover the
+// part that is defined and avoid the undefined cases.
+
+module dffsr1(clk, arst, d, q);
+input clk, arst, d;
+output reg q;
+always @(posedge clk, posedge arst) begin
+ if (arst)
+ q <= d^d; // constant expression -- but the frontend optimizer does not know that..
+ else
+ q <= d;
+end
+endmodule
+
+module dffsr2(clk, preset, clear, d, q);
+input clk, preset, clear, d;
+output q;
+(* gentb_clock *)
+wire clk, preset, clear, d;
+dffsr2_sub uut (clk, preset && !clear, !preset && clear, d, q);
+endmodule
+
+(* gentb_skip *)
+module dffsr2_sub(clk, preset, clear, d, q);
+input clk, preset, clear, d;
+output reg q;
+always @(posedge clk, posedge preset, posedge clear) begin
+ if (preset)
+ q <= 1;
+ else if (clear)
+ q <= 0;
+ else
+ q <= d;
+end
+endmodule
+
+
diff --git a/tests/simple/fiedler-cooley.v b/tests/simple/fiedler-cooley.v
new file mode 100644
index 00000000..96861973
--- /dev/null
+++ b/tests/simple/fiedler-cooley.v
@@ -0,0 +1,33 @@
+// borrowed with some modifications from
+// http://www.ee.ed.ac.uk/~gerard/Teach/Verilog/manual/Example/lrgeEx2/cooley.html
+module up3down5(clock, data_in, up, down, carry_out, borrow_out, count_out, parity_out);
+
+input [8:0] data_in;
+input clock, up, down;
+
+output reg [8:0] count_out;
+output reg carry_out, borrow_out, parity_out;
+
+reg [9:0] cnt_up, cnt_dn;
+reg [8:0] count_nxt;
+
+always @(posedge clock)
+begin
+ cnt_dn = count_out - 3'b 101;
+ cnt_up = count_out + 2'b 11;
+
+ case ({up,down})
+ 2'b 00 : count_nxt = data_in;
+ 2'b 01 : count_nxt = cnt_dn;
+ 2'b 10 : count_nxt = cnt_up;
+ 2'b 11 : count_nxt = count_out;
+ default : count_nxt = 9'bX;
+ endcase
+
+ parity_out <= ^count_nxt;
+ carry_out <= up & cnt_up[9];
+ borrow_out <= down & cnt_dn[9];
+ count_out <= count_nxt;
+end
+
+endmodule
diff --git a/tests/simple/forgen01.v b/tests/simple/forgen01.v
new file mode 100644
index 00000000..8b7aa279
--- /dev/null
+++ b/tests/simple/forgen01.v
@@ -0,0 +1,23 @@
+
+// VERIFIC-SKIP
+
+module uut_forgen01(a, y);
+
+input [4:0] a;
+output y;
+
+integer i, j;
+reg [31:0] lut;
+
+initial begin
+ for (i = 0; i < 32; i = i+1) begin
+ lut[i] = i > 1;
+ for (j = 2; j*j <= i; j = j+1)
+ if (i % j == 0)
+ lut[i] = 0;
+ end
+end
+
+assign y = lut[a];
+
+endmodule
diff --git a/tests/simple/forgen02.v b/tests/simple/forgen02.v
new file mode 100644
index 00000000..14af070c
--- /dev/null
+++ b/tests/simple/forgen02.v
@@ -0,0 +1,30 @@
+module uut_forgen02(a, b, cin, y, cout);
+
+parameter WIDTH = 8;
+
+input [WIDTH-1:0] a, b;
+input cin;
+
+output [WIDTH-1:0] y;
+output cout;
+
+genvar i;
+wire [WIDTH-1:0] carry;
+
+generate
+ for (i = 0; i < WIDTH; i=i+1) begin:adder
+ wire [2:0] D;
+ assign D[1:0] = { a[i], b[i] };
+ if (i == 0) begin:chain
+ assign D[2] = cin;
+ end else begin:chain
+ assign D[2] = carry[i-1];
+ end
+ assign y[i] = ^D;
+ assign carry[i] = &D[1:0] | (^D[1:0] & D[2]);
+ end
+endgenerate
+
+assign cout = carry[WIDTH-1];
+
+endmodule
diff --git a/tests/simple/fsm.v b/tests/simple/fsm.v
new file mode 100644
index 00000000..2dba14bb
--- /dev/null
+++ b/tests/simple/fsm.v
@@ -0,0 +1,69 @@
+
+// `define ASYNC_RESET
+
+module fsm_test(clk, reset, button_a, button_b, red_a, green_a, red_b, green_b);
+
+input clk, reset, button_a, button_b;
+output reg red_a, green_a, red_b, green_b;
+
+(* gentb_constant = 0 *)
+wire reset;
+
+integer state;
+reg [3:0] cnt;
+
+`ifdef ASYNC_RESET
+always @(posedge clk, posedge reset)
+`else
+always @(posedge clk)
+`endif
+begin
+ cnt <= 0;
+ red_a <= 1;
+ red_b <= 1;
+ green_a <= 0;
+ green_b <= 0;
+
+ if (reset)
+ state <= 100;
+ else
+ case (state)
+ 100: begin
+ if (button_a && !button_b)
+ state <= 200;
+ if (!button_a && button_b)
+ state <= 300;
+ end
+ 200: begin
+ red_a <= 0;
+ green_a <= 1;
+ cnt <= cnt + 1;
+ if (cnt == 5)
+ state <= 210;
+ end
+ 210: begin
+ red_a <= 0;
+ green_a <= cnt[0];
+ cnt <= cnt + 1;
+ if (cnt == 10)
+ state <= 100;
+ end
+ 300: begin
+ red_b <= 0;
+ green_b <= 1;
+ cnt <= cnt + 1;
+ if (cnt == 5)
+ state <= 310;
+ end
+ 310: begin
+ red_b <= 0;
+ green_b <= cnt[0];
+ cnt <= cnt + 1;
+ if (cnt == 10)
+ state <= 100;
+ end
+ endcase
+end
+
+endmodule
+
diff --git a/tests/simple/generate.v b/tests/simple/generate.v
new file mode 100644
index 00000000..24eb4462
--- /dev/null
+++ b/tests/simple/generate.v
@@ -0,0 +1,94 @@
+
+module gen_test1(clk, a, b, y);
+
+input clk;
+input [7:0] a, b;
+output reg [7:0] y;
+
+genvar i, j;
+wire [15:0] tmp1;
+
+generate
+
+ for (i = 0; i < 8; i = i + 1) begin:gen1
+ wire and_wire, or_wire;
+ assign and_wire = a[i] & b[i];
+ assign or_wire = a[i] | b[i];
+ if (i % 2 == 0) begin:gen2true
+ assign tmp1[i] = and_wire;
+ assign tmp1[i+8] = or_wire;
+ end else begin:gen2false
+ assign tmp1[i] = or_wire;
+ assign tmp1[i+8] = and_wire;
+ end
+ end
+
+ for (i = 0; i < 8; i = i + 1) begin:gen3
+ wire [4:0] tmp2;
+ for (j = 0; j <= 4; j = j + 1) begin:gen4
+ wire tmpbuf;
+ assign tmpbuf = tmp1[i+2*j];
+ assign tmp2[j] = tmpbuf;
+ end
+ always @(posedge clk)
+ y[i] <= ^tmp2;
+ end
+
+endgenerate
+
+endmodule
+
+// ------------------------------------------
+
+module gen_test2(clk, a, b, y);
+
+input clk;
+input [7:0] a, b;
+output reg [8:0] y;
+
+integer i;
+reg [8:0] carry;
+
+always @(posedge clk) begin
+ carry[0] = 0;
+ for (i = 0; i < 8; i = i + 1) begin
+ casez ({a[i], b[i], carry[i]})
+ 3'b?11, 3'b1?1, 3'b11?:
+ carry[i+1] = 1;
+ default:
+ carry[i+1] = 0;
+ endcase
+ y[i] = a[i] ^ b[i] ^ carry[i];
+ end
+ y[8] = carry[8];
+end
+
+endmodule
+
+// ------------------------------------------
+
+module gen_test3(a, b, sel, y, z);
+
+input [3:0] a, b;
+input sel;
+output [3:0] y, z;
+
+genvar i;
+generate
+ for (i=0; i < 2; i=i+1)
+ assign y[i] = sel ? a[i] : b[i], z[i] = sel ? b[i] : a[i];
+ for (i=0; i < 2; i=i+1) begin
+ if (i == 0)
+ assign y[2] = sel ? a[2] : b[2];
+ else
+ assign z[2] = sel ? a[2] : b[2];
+ case (i)
+ default:
+ assign z[3] = sel ? a[3] : b[3];
+ 0:
+ assign y[3] = sel ? a[3] : b[3];
+ endcase
+ end
+endgenerate
+
+endmodule
diff --git a/tests/simple/graphtest.v b/tests/simple/graphtest.v
new file mode 100644
index 00000000..74788dbb
--- /dev/null
+++ b/tests/simple/graphtest.v
@@ -0,0 +1,34 @@
+module graphtest (A,B,X,Y,Z);
+
+input [3:0] A;
+input [3:0] B;
+output reg [3:0] X;
+output [9:0] Y;
+output [7:0] Z;
+
+wire [4:0] t;
+
+assign t[4] = 1'b0; // Constant connects to wire
+assign t[2:0] = A[2:0] & { 2'b10, B[3]}; // Concatenation of intermediate wire
+assign t[3] = A[2] ^ B[3]; // Bitwise-XOR
+
+// assign Y[2:0] = 3'b111;
+// assign Y[6:3] = A;
+// assign Y[9:7] = t[0:2];
+assign Y = {3'b111, A, t[2:0]}; // Direct assignment of concatenation
+
+assign Z[0] = 1'b0; // Constant connects to PO
+assign Z[1] = t[3]; // Intermediate sig connects to PO
+assign Z[3:2] = A[2:1]; // PI connects to PO
+assign Z[7:4] = {1'b0, B[2:0]}; // Concat of CV and PI connect to PO
+
+always @* begin
+ if (A == 4'b1111) begin // All-Const at port (eq)
+ X = B;
+ end
+ else begin
+ X = 4'b0000; // All-Const at port (mux)
+ end
+end
+
+endmodule
diff --git a/tests/simple/hierarchy.v b/tests/simple/hierarchy.v
new file mode 100644
index 00000000..123afaea
--- /dev/null
+++ b/tests/simple/hierarchy.v
@@ -0,0 +1,27 @@
+
+(* top *)
+module top(a, b, y1, y2, y3, y4);
+input [3:0] a;
+input signed [3:0] b;
+output [7:0] y1, y2, y3, y4;
+
+// this version triggers a bug in Icarus Verilog
+// submod #(-3'sd1, 3'b111 + 3'b001) foo (a, b, y1, y2, y3, y4);
+
+// this version is handled correctly by Icarus Verilog
+submod #(-3'sd1, -3'sd1) foo (a, b, y1, y2, y3, y4);
+
+endmodule
+
+(* gentb_skip *)
+module submod(a, b, y1, y2, y3, y4);
+parameter c = 0;
+parameter [7:0] d = 0;
+input [3:0] a, b;
+output [7:0] y1, y2, y3, y4;
+assign y1 = a;
+assign y2 = b;
+assign y3 = c;
+assign y4 = d;
+endmodule
+
diff --git a/tests/simple/i2c_master_tests.v b/tests/simple/i2c_master_tests.v
new file mode 100644
index 00000000..3aa59663
--- /dev/null
+++ b/tests/simple/i2c_master_tests.v
@@ -0,0 +1,62 @@
+// one of my early test cases was the OpenCores I2C master
+// This is a collection of stripped down code snippets from
+// this core that triggered bugs in early versions of yosys.
+
+// from i2c_master_bit_ctrl
+module i2c_test01(clk, rst, nReset, al);
+
+ input clk, rst, nReset;
+ output reg al;
+
+ reg cmd_stop;
+ always @(posedge clk or negedge nReset)
+ if (~nReset)
+ cmd_stop <= #1 1'b0;
+ else if (rst)
+ cmd_stop <= #1 1'b0;
+
+ always @(posedge clk or negedge nReset)
+ if (~nReset)
+ al <= #1 1'b0;
+ else if (rst)
+ al <= #1 1'b0;
+ else
+ al <= #1 ~cmd_stop;
+
+endmodule
+
+// from i2c_master_bit_ctrl
+module i2c_test02(clk, slave_wait, clk_cnt, cmd, cmd_stop, cnt);
+
+ input clk, slave_wait, clk_cnt;
+ input cmd;
+
+ output reg cmd_stop;
+
+ reg clk_en;
+ output reg [15:0] cnt;
+
+ always @(posedge clk)
+ if (~|cnt)
+ if (~slave_wait)
+ begin
+ cnt <= #1 clk_cnt;
+ clk_en <= #1 1'b1;
+ end
+ else
+ begin
+ cnt <= #1 cnt;
+ clk_en <= #1 1'b0;
+ end
+ else
+ begin
+ cnt <= #1 cnt - 16'h1;
+ clk_en <= #1 1'b0;
+ end
+
+ always @(posedge clk)
+ if (clk_en)
+ cmd_stop <= #1 cmd;
+
+endmodule
+
diff --git a/tests/simple/loops.v b/tests/simple/loops.v
new file mode 100644
index 00000000..d7743a42
--- /dev/null
+++ b/tests/simple/loops.v
@@ -0,0 +1,79 @@
+
+// a simple test case extracted from systemcaes (as included in iwls2005)
+// this design has latches (or logic loops) for the two temp variables.
+// this latches (or logic loops) must be removed in the final synthesis results
+
+module aes(
+ // inputs
+ input [3:0] addroundkey_data_i,
+ input [3:0] addroundkey_data_reg,
+ input [3:0] addroundkey_round,
+ input [3:0] key_i,
+ input [3:0] keysched_new_key_o,
+ input [3:0] round,
+ input addroundkey_start_i,
+ input keysched_ready_o,
+
+ // outputs
+ output reg [3:0] keysched_last_key_i,
+ output reg [3:0] keysched_round_i,
+ output reg [3:0] next_addroundkey_data_reg,
+ output reg [3:0] next_addroundkey_round,
+ output reg [3:0] round_data_var,
+ output reg keysched_start_i,
+ output reg next_addroundkey_ready_o
+);
+
+// temp variables
+reg [3:0] data_var;
+reg [3:0] round_key_var;
+
+always @*
+begin
+ keysched_start_i = 0;
+ keysched_round_i = addroundkey_round;
+ round_data_var = addroundkey_data_reg;
+ next_addroundkey_data_reg = addroundkey_data_reg;
+ next_addroundkey_ready_o = 0;
+ next_addroundkey_round = addroundkey_round;
+
+ if (addroundkey_round == 1 || addroundkey_round == 0)
+ keysched_last_key_i = key_i;
+ else
+ keysched_last_key_i = keysched_new_key_o;
+
+ if (round == 0 && addroundkey_start_i)
+ begin
+ data_var = addroundkey_data_i;
+ round_key_var = key_i;
+ round_data_var = round_key_var ^ data_var;
+ next_addroundkey_data_reg = round_data_var;
+ next_addroundkey_ready_o = 1;
+ end
+ else if (addroundkey_start_i && round != 0)
+ begin
+ keysched_last_key_i = key_i;
+ keysched_start_i = 1;
+ keysched_round_i = 1;
+ next_addroundkey_round = 1;
+ end
+ else if (addroundkey_round != round && keysched_ready_o)
+ begin
+ next_addroundkey_round = addroundkey_round + 1;
+ keysched_last_key_i = keysched_new_key_o;
+ keysched_start_i = 1;
+ keysched_round_i = addroundkey_round + 1;
+ end
+ else if (addroundkey_round == round && keysched_ready_o)
+ begin
+ data_var = addroundkey_data_i;
+ round_key_var = keysched_new_key_o;
+ round_data_var = round_key_var ^ data_var;
+ next_addroundkey_data_reg = round_data_var;
+ next_addroundkey_ready_o = 1;
+ next_addroundkey_round = 0;
+ end
+end
+
+endmodule
+
diff --git a/tests/simple/macros.v b/tests/simple/macros.v
new file mode 100644
index 00000000..7b4d616e
--- /dev/null
+++ b/tests/simple/macros.v
@@ -0,0 +1,244 @@
+
+module test_def(a, y);
+
+`define MSB_LSB_SEP :
+`define get_msb(off, len) ((off)+(len)-1)
+`define get_lsb(off, len) (off)
+`define sel_bits(offset, len) `get_msb(offset, len) `MSB_LSB_SEP `get_lsb(offset, len)
+
+input [31:0] a;
+output [7:0] y;
+
+assign y = a[`sel_bits(16, 8)];
+
+endmodule
+
+// ---------------------------------------------------
+
+module test_ifdef(a, y);
+
+input [2:0] a;
+output reg [31:0] y;
+
+always @* begin
+ y = 0;
+
+ `undef X
+ `ifdef X
+ y = y + 42;
+ `else
+ `undef A
+ `undef B
+ `ifdef A
+ y = (y << 1) | a[0];
+ `elsif B
+ y = (y << 1) | a[1];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ `undef A
+ `define B
+ `ifdef A
+ y = (y << 1) | a[0];
+ `elsif B
+ y = (y << 1) | a[1];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ `define A
+ `undef B
+ `ifdef A
+ y = (y << 1) | a[0];
+ `elsif B
+ y = (y << 1) | a[1];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ `define A
+ `define B
+ `ifdef A
+ y = (y << 1) | a[0];
+ `elsif B
+ y = (y << 1) | a[1];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ // ------------------------------------
+ `undef A
+ `undef B
+ `ifndef A
+ y = (y << 1) | a[0];
+ `elsif B
+ y = (y << 1) | a[1];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ `undef A
+ `define B
+ `ifndef A
+ y = (y << 1) | a[0];
+ `elsif B
+ y = (y << 1) | a[1];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ `define A
+ `undef B
+ `ifndef A
+ y = (y << 1) | a[0];
+ `elsif B
+ y = (y << 1) | a[1];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ `define A
+ `define B
+ `ifndef A
+ y = (y << 1) | a[0];
+ `elsif B
+ y = (y << 1) | a[1];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ // ------------------------------------
+ `undef A
+ `ifdef A
+ y = (y << 1) | a[0];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ `define A
+ `ifdef A
+ y = (y << 1) | a[0];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ // ------------------------------------
+ `undef A
+ `ifndef A
+ y = (y << 1) | a[0];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ `define A
+ `ifndef A
+ y = (y << 1) | a[0];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ `endif
+
+ `define X
+ `ifdef X
+ `undef A
+ `undef B
+ `ifdef A
+ y = (y << 1) | a[0];
+ `elsif B
+ y = (y << 1) | a[1];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ `undef A
+ `define B
+ `ifdef A
+ y = (y << 1) | a[0];
+ `elsif B
+ y = (y << 1) | a[1];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ `define A
+ `undef B
+ `ifdef A
+ y = (y << 1) | a[0];
+ `elsif B
+ y = (y << 1) | a[1];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ `define A
+ `define B
+ `ifdef A
+ y = (y << 1) | a[0];
+ `elsif B
+ y = (y << 1) | a[1];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ // ------------------------------------
+ `undef A
+ `undef B
+ `ifndef A
+ y = (y << 1) | a[0];
+ `elsif B
+ y = (y << 1) | a[1];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ `undef A
+ `define B
+ `ifndef A
+ y = (y << 1) | a[0];
+ `elsif B
+ y = (y << 1) | a[1];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ `define A
+ `undef B
+ `ifndef A
+ y = (y << 1) | a[0];
+ `elsif B
+ y = (y << 1) | a[1];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ `define A
+ `define B
+ `ifndef A
+ y = (y << 1) | a[0];
+ `elsif B
+ y = (y << 1) | a[1];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ // ------------------------------------
+ `undef A
+ `ifdef A
+ y = (y << 1) | a[0];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ `define A
+ `ifdef A
+ y = (y << 1) | a[0];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ // ------------------------------------
+ `undef A
+ `ifndef A
+ y = (y << 1) | a[0];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ `define A
+ `ifndef A
+ y = (y << 1) | a[0];
+ `else
+ y = (y << 1) | a[2];
+ `endif
+ `else
+ y = y + 42;
+ `endif
+end
+
+endmodule
+
+`define SIZE 4 // comment supported in this part
+module test_comment_in_macro ( din_a, dout_a );
+input [`SIZE-1:0] din_a;
+output [`SIZE-1:0] dout_a;
+assign dout_a = din_a | `SIZE'ha;
+endmodule
diff --git a/tests/simple/mem2reg.v b/tests/simple/mem2reg.v
new file mode 100644
index 00000000..9839fd4a
--- /dev/null
+++ b/tests/simple/mem2reg.v
@@ -0,0 +1,94 @@
+
+module mem2reg_test1(in_addr, in_data, out_addr, out_data);
+
+input [1:0] in_addr, out_addr;
+input [3:0] in_data;
+output reg [3:0] out_data;
+
+reg [3:0] array [2:0];
+
+always @* begin
+ array[0] = 0;
+ array[1] = 23;
+ array[2] = 42;
+ array[in_addr] = in_data;
+ out_data = array[out_addr];
+end
+
+endmodule
+
+// ------------------------------------------------------
+
+module mem2reg_test2(clk, reset, mode, addr, data);
+
+input clk, reset, mode;
+input [2:0] addr;
+output [3:0] data;
+
+(* mem2reg *)
+reg [3:0] mem [0:7];
+
+assign data = mem[addr];
+
+integer i;
+
+always @(posedge clk) begin
+ if (reset) begin
+ for (i=0; i<8; i=i+1)
+ mem[i] <= i;
+ end else
+ if (mode) begin
+ for (i=0; i<8; i=i+1)
+ mem[i] <= mem[i]+1;
+ end else begin
+ mem[addr] <= 0;
+ end
+end
+
+endmodule
+
+// ------------------------------------------------------
+
+// http://www.reddit.com/r/yosys/comments/28d9lx/problem_with_concatenation_of_two_dimensional/
+module mem2reg_test3( input clk, input [8:0] din_a, output reg [7:0] dout_a, output [7:0] dout_b);
+reg [7:0] dint_c [0:7];
+always @(posedge clk)
+ begin
+ {dout_a[0], dint_c[3]} <= din_a;
+ end
+assign dout_b = dint_c[3];
+endmodule
+
+// ------------------------------------------------------
+
+module mem2reg_test4(result1, result2, result3);
+ output signed [9:0] result1;
+ output signed [9:0] result2;
+ output signed [9:0] result3;
+
+ wire signed [9:0] intermediate [0:3];
+
+ function integer depth2Index;
+ input integer depth;
+ depth2Index = depth;
+ endfunction
+
+ assign intermediate[depth2Index(1)] = 1;
+ assign intermediate[depth2Index(2)] = 2;
+ assign intermediate[3] = 3;
+ assign result1 = intermediate[1];
+ assign result2 = intermediate[depth2Index(2)];
+ assign result3 = intermediate[depth2Index(3)];
+endmodule
+
+// ------------------------------------------------------
+
+module mem2reg_test5(input ctrl, output out);
+ wire [0:0] foo[0:0];
+ wire [0:0] bar[0:1];
+
+ assign foo[0] = ctrl;
+ assign bar[0] = 0, bar[1] = 1;
+ assign out = bar[foo[0]];
+endmodule
+
diff --git a/tests/simple/mem_arst.v b/tests/simple/mem_arst.v
new file mode 100644
index 00000000..9bd38fcb
--- /dev/null
+++ b/tests/simple/mem_arst.v
@@ -0,0 +1,43 @@
+
+module MyMem #(
+ parameter AddrWidth = 4,
+ parameter DataWidth = 4) (
+ (* gentb_constant = 1 *)
+ input Reset_n_i,
+ input Clk_i,
+ input [AddrWidth-1:0] Addr_i,
+ input [DataWidth-1:0] Data_i,
+ output [DataWidth-1:0] Data_o,
+ input WR_i);
+
+ reg [DataWidth-1:0] Data_o;
+
+ localparam Size = 2**AddrWidth;
+
+ (* mem2reg *)
+ reg [DataWidth-1:0] Mem[Size-1:0];
+
+ integer i;
+
+ always @(negedge Reset_n_i or posedge Clk_i)
+ begin
+ if (!Reset_n_i)
+ begin
+ Data_o <= 'bx;
+ for (i=0; i<Size; i=i+1)
+ begin
+ Mem[i] <= 0;
+ end
+ end
+ else
+ begin
+ Data_o <= Mem[Addr_i];
+ if (WR_i)
+ begin
+ Mem[Addr_i] <= Data_i;
+ end
+ end
+ end
+
+endmodule
+
diff --git a/tests/simple/memory.v b/tests/simple/memory.v
new file mode 100644
index 00000000..f38bdafd
--- /dev/null
+++ b/tests/simple/memory.v
@@ -0,0 +1,309 @@
+
+module memtest00(clk, setA, setB, y);
+
+input clk, setA, setB;
+output y;
+reg mem [1:0];
+
+always @(posedge clk) begin
+ if (setA) mem[0] <= 0; // this is line 9
+ if (setB) mem[0] <= 1; // this is line 10
+end
+
+assign y = mem[0];
+
+endmodule
+
+// ----------------------------------------------------------
+
+module memtest01(clk, wr_en, wr_addr, wr_value, rd_addr, rd_value);
+
+input clk, wr_en;
+input [3:0] wr_addr, rd_addr;
+input [7:0] wr_value;
+output reg [7:0] rd_value;
+
+reg [7:0] data [15:0];
+
+always @(posedge clk)
+ if (wr_en)
+ data[wr_addr] <= wr_value;
+
+always @(posedge clk)
+ rd_value <= data[rd_addr];
+
+endmodule
+
+// ----------------------------------------------------------
+
+module memtest02(clk, setA, setB, addr, bit, y1, y2, y3, y4);
+
+input clk, setA, setB;
+input [1:0] addr;
+input [2:0] bit;
+output reg y1, y2;
+output y3, y4;
+
+reg [7:0] mem1 [3:0];
+
+(* mem2reg *)
+reg [7:0] mem2 [3:0];
+
+always @(posedge clk) begin
+ if (setA) begin
+ mem1[0] <= 10;
+ mem1[1] <= 20;
+ mem1[2] <= 30;
+ mem2[0] <= 17;
+ mem2[1] <= 27;
+ mem2[2] <= 37;
+ end
+ if (setB) begin
+ mem1[0] <= 1;
+ mem1[1] <= 2;
+ mem1[2] <= 3;
+ mem2[0] <= 71;
+ mem2[1] <= 72;
+ mem2[2] <= 73;
+ end
+ y1 <= mem1[addr][bit];
+ y2 <= mem2[addr][bit];
+end
+
+assign y3 = mem1[addr][bit];
+assign y4 = mem2[addr][bit];
+
+endmodule
+
+// ----------------------------------------------------------
+
+module memtest03(clk, wr_addr, wr_data, wr_enable, rd_addr, rd_data);
+
+input clk, wr_enable;
+input [3:0] wr_addr, wr_data, rd_addr;
+output reg [3:0] rd_data;
+
+reg [3:0] memory [0:15];
+
+always @(posedge clk) begin
+ if (wr_enable)
+ memory[wr_addr] <= wr_data;
+ rd_data <= memory[rd_addr];
+end
+
+endmodule
+
+// ----------------------------------------------------------
+
+module memtest04(clk, wr_addr, wr_data, wr_enable, rd_addr, rd_data);
+
+input clk, wr_enable;
+input [3:0] wr_addr, wr_data, rd_addr;
+output [3:0] rd_data;
+
+reg rd_addr_buf;
+reg [3:0] memory [0:15];
+
+always @(posedge clk) begin
+ if (wr_enable)
+ memory[wr_addr] <= wr_data;
+ rd_addr_buf <= rd_addr;
+end
+
+assign rd_data = memory[rd_addr_buf];
+
+endmodule
+
+// ----------------------------------------------------------
+
+module memtest05(clk, addr, wdata, rdata, wen);
+
+input clk;
+input [1:0] addr;
+input [7:0] wdata;
+output reg [7:0] rdata;
+input [3:0] wen;
+
+reg [7:0] mem [0:3];
+
+integer i;
+always @(posedge clk) begin
+ for (i = 0; i < 4; i = i+1)
+ if (wen[i]) mem[addr][i*2 +: 2] <= wdata[i*2 +: 2];
+ rdata <= mem[addr];
+end
+
+endmodule
+
+// ----------------------------------------------------------
+
+module memtest06_sync(input clk, input rst, input [2:0] idx, input [7:0] din, output [7:0] dout);
+ (* gentb_constant=0 *) wire rst;
+ reg [7:0] test [0:7];
+ integer i;
+ always @(posedge clk) begin
+ if (rst) begin
+ for (i=0; i<8; i=i+1)
+ test[i] <= 0;
+ end else begin
+ test[0][2] <= din[1];
+ test[0][5] <= test[0][2];
+ test[idx][3] <= din[idx];
+ test[idx][6] <= test[idx][2];
+ test[idx][idx] <= !test[idx][idx];
+ end
+ end
+ assign dout = test[idx];
+endmodule
+
+module memtest06_async(input clk, input rst, input [2:0] idx, input [7:0] din, output [7:0] dout);
+ (* gentb_constant=0 *) wire rst;
+ reg [7:0] test [0:7];
+ integer i;
+ always @(posedge clk or posedge rst) begin
+ if (rst) begin
+ for (i=0; i<8; i=i+1)
+ test[i] <= 0;
+ end else begin
+ test[0][2] <= din[1];
+ test[0][5] <= test[0][2];
+ test[idx][3] <= din[idx];
+ test[idx][6] <= test[idx][2];
+ test[idx][idx] <= !test[idx][idx];
+ end
+ end
+ assign dout = test[idx];
+endmodule
+
+// ----------------------------------------------------------
+
+module memtest07(clk, addr, woffset, wdata, rdata);
+
+input clk;
+input [1:0] addr;
+input [3:0] wdata;
+input [1:0] woffset;
+output reg [7:0] rdata;
+
+reg [7:0] mem [0:3];
+
+integer i;
+always @(posedge clk) begin
+ mem[addr][woffset +: 4] <= wdata;
+ rdata <= mem[addr];
+end
+
+endmodule
+
+// ----------------------------------------------------------
+
+module memtest08(input clk, input [3:0] a, b, c, output reg [3:0] y);
+ reg [3:0] mem [0:15] [0:15];
+ always @(posedge clk) begin
+ y <= mem[a][b];
+ mem[a][b] <= c;
+ end
+endmodule
+
+// ----------------------------------------------------------
+
+module memtest09 (
+ input clk,
+ input [3:0] a_addr, a_din, b_addr, b_din,
+ input a_wen, b_wen,
+ output reg [3:0] a_dout, b_dout
+);
+ reg [3:0] memory [10:35];
+
+ always @(posedge clk) begin
+ if (a_wen)
+ memory[10 + a_addr] <= a_din;
+ a_dout <= memory[10 + a_addr];
+ end
+
+ always @(posedge clk) begin
+ if (b_wen && (10 + a_addr != 20 + b_addr || !a_wen))
+ memory[20 + b_addr] <= b_din;
+ b_dout <= memory[20 + b_addr];
+ end
+endmodule
+
+// ----------------------------------------------------------
+
+module memtest10(input clk, input [5:0] din, output [5:0] dout);
+ reg [5:0] queue [0:3];
+ integer i;
+
+ always @(posedge clk) begin
+ queue[0] <= din;
+ for (i = 1; i < 4; i=i+1) begin
+ queue[i] <= queue[i-1];
+ end
+ end
+
+ assign dout = queue[3];
+endmodule
+
+// ----------------------------------------------------------
+
+module memtest11(clk, wen, waddr, raddr, wdata, rdata);
+ input clk, wen;
+ input [1:0] waddr, raddr;
+ input [7:0] wdata;
+ output [7:0] rdata;
+
+ reg [7:0] mem [3:0];
+
+ assign rdata = mem[raddr];
+
+ always @(posedge clk) begin
+ if (wen)
+ mem[waddr] <= wdata;
+ else
+ mem[waddr] <= mem[waddr];
+ end
+endmodule
+
+// ----------------------------------------------------------
+
+module memtest12 (
+ input clk,
+ input [1:0] adr,
+ input [1:0] din,
+ output reg [1:0] q
+);
+ reg [1:0] ram [3:0];
+ always@(posedge clk)
+ {ram[adr], q} <= {din, ram[adr]};
+endmodule
+
+// ----------------------------------------------------------
+
+module memtest13 (
+ input clk, rst,
+ input [1:0] a1, a2, a3, a4, a5, a6,
+ input [3:0] off1, off2,
+ input [31:5] din1,
+ input [3:0] din2, din3,
+ output reg [3:0] dout1, dout2,
+ output reg [31:5] dout3
+);
+ reg [31:5] mem [0:3];
+
+ always @(posedge clk) begin
+ if (rst) begin
+ mem[0] <= 0;
+ mem[1] <= 0;
+ mem[2] <= 0;
+ mem[3] <= 0;
+ end else begin
+ mem[a1] <= din1;
+ mem[a2][14:11] <= din2;
+ mem[a3][5 + off1 +: 4] <= din3;
+ dout1 <= mem[a4][12:9];
+ dout2 <= mem[a5][5 + off2 +: 4];
+ dout3 <= mem[a6];
+ end
+ end
+endmodule
+
diff --git a/tests/simple/multiplier.v b/tests/simple/multiplier.v
new file mode 100644
index 00000000..3c0aa1fc
--- /dev/null
+++ b/tests/simple/multiplier.v
@@ -0,0 +1,132 @@
+
+// Via http://www.edaplayground.com/s/6/591
+// stackoverflow 20556634
+// http://stackoverflow.com/questions/20556634/how-can-i-iteratively-create-buses-of-parameterized-size-to-connect-modules-also
+
+// Code your design here
+`define macro_args
+`define indexed_part_select
+
+module Multiplier_flat #(parameter M = 4, parameter N = 4)(
+input [M-1:0] A, //Input A, size M
+input [N-1:0] B, //Input B, size N
+output [M+N-1:0] P ); //Output P (product), size M+N
+
+/* Calculate LSB using Wolfram Alpha
+ N==3 : http://www.wolframalpha.com/input/?i=0%2C+4%2C+9%2C+15%2C+...
+ N==4 : http://www.wolframalpha.com/input/?i=0%2C+5%2C+11%2C+18%2C+26%2C+35%2C+...
+ N==5 : http://www.wolframalpha.com/input/?i=0%2C+6%2C+13%2C+21%2C+30%2C+...
+ */
+`ifdef macro_args
+// initial $display("Use Macro Args");
+`define calc_pp_lsb(n) (((n)-1)*((n)+2*M)/2)
+//`define calc_pp_msb(n) (`calc_pp_lsb(n+1)-1)
+`define calc_pp_msb(n) ((n**2+(2*M+1)*n-2)/2)
+//`define calc_range(n) `calc_pp_msb(n):`calc_pp_lsb(n)
+`define calc_pp_range(n) `calc_pp_lsb(n) +: (M+n)
+
+wire [`calc_pp_msb(N):0] PP;
+assign PP[`calc_pp_range(1)] = { 1'b0 , { A & {M{B[0]}} } };
+assign P = PP[`calc_pp_range(N)];
+`elsif indexed_part_select
+// initial $display("Use Indexed Part Select");
+localparam MSB = (N**2+(2*M+1)*N-2)/2;
+wire [MSB:0] PP;
+assign PP[M:0] = { 1'b0 , { A & {M{B[0]}} } };
+assign P = PP[MSB -: M+N];
+`else
+// initial $display("Use Worst Case");
+localparam MSB = (N**2+(2*M+1)*N-2)/2;
+wire [MSB:0] PP;
+assign PP[M:0] = { 1'b0 , { A & {M{B[0]}} } };
+assign P = PP[MSB : MSB+1-M-N];
+`endif
+
+genvar i;
+generate
+for (i=1; i < N; i=i+1)
+begin: addPartialProduct
+ wire [M+i-1:0] gA,gB,gS;
+ wire Cout;
+ assign gA = { A & {M{B[i]}} , {i{1'b0}} };
+ `ifdef macro_args
+ assign gB = PP[`calc_pp_range(i)];
+ assign PP[`calc_pp_range(i+1)] = {Cout,gS};
+ `elsif indexed_part_select
+ assign gB = PP[(i-1)*(i+2*M)/2 +: M+i];
+ assign PP[i*((i+1)+2*M)/2 +: M+i+1] = {Cout,gS};
+ `else
+ localparam LSB = (i-1)*(i+2*M)/2;
+ localparam MSB = (i**2+(2*M+1)*i-2)/2;
+ localparam MSB2 = ((i+1)**2+(2*M+1)*(i+1)-2)/2;
+ assign gB = PP[MSB : LSB];
+ assign PP[ MSB2 : MSB+1] = {Cout,gS};
+ `endif
+ RippleCarryAdder#(M+i) adder( .A(gA), .B(gB), .S(gS), .Cin (1'b0), .Cout(Cout) );
+end
+endgenerate
+
+`ifdef macro_args
+// Cleanup global space
+`undef calc_pp_range
+`undef calc_pp_msb
+`undef calc_pp_lsb
+`endif
+endmodule
+
+module Multiplier_2D #(parameter M = 4, parameter N = 4)(
+input [M-1:0] A, //Input A, size M
+input [N-1:0] B, //Input B, size N
+output [M+N-1:0] P ); //Output P (product), size M+N
+
+wire [M+N-1:0] PP [N-1:0];
+
+// Note: bits PP[0][M+N-1:M+1] are never used. Unused bits are optimized out during synthesis
+//assign PP[0][M:0] = { {1'b0} , { A & {M{B[0]}} } };
+//assign PP[0][M+N-1:M+1] = {(N-1){1'b0}}; // uncomment to make probing readable
+assign PP[0] = { {N{1'b0}} , { A & {M{B[0]}} } };
+assign P = PP[N-1];
+
+genvar i;
+generate
+for (i=1; i < N; i=i+1)
+begin: addPartialProduct
+ wire [M+i-1:0] gA,gB,gS; wire Cout;
+ assign gA = { A & {M{B[i]}} , {i{1'b0}} };
+ assign gB = PP[i-1][M+i-1:0];
+ //assign PP[i][M+i:0] = {Cout,gS};
+ //if (i+1<N) assign PP[i][M+N-1:M+i+1] = {(N-i){1'b0}}; // uncomment to make probing readable
+ assign PP[i] = { {(N-i){1'b0}}, Cout, gS};
+ RippleCarryAdder#(M+i) adder(
+ .A(gA), .B(gB), .S(gS), .Cin(1'b0), .Cout(Cout) );
+end
+endgenerate
+
+//always@* foreach(S[i]) $display("S[%0d]:%b",i,S[i]);
+
+endmodule
+
+module RippleCarryAdder#(parameter N = 4)(A,B,Cin,S,Cout);
+
+input [N-1:0] A;
+input [N-1:0] B;
+input Cin;
+output [N-1:0] S;
+output Cout;
+wire [N:0] CC;
+
+assign CC[0] = Cin;
+assign Cout = CC[N];
+genvar i;
+generate
+for (i=0; i < N; i=i+1)
+begin: addbit
+ FullAdder unit(A[i],B[i],CC[i],S[i],CC[i+1]);
+end
+endgenerate
+
+endmodule
+
+module FullAdder(input A,B,Cin, output wire S,Cout);
+assign {Cout,S} = A+B+Cin;
+endmodule
diff --git a/tests/simple/muxtree.v b/tests/simple/muxtree.v
new file mode 100644
index 00000000..1fb1cea5
--- /dev/null
+++ b/tests/simple/muxtree.v
@@ -0,0 +1,83 @@
+
+// test case generated from IWLS 2005 usb_phy core
+// (triggered a bug in opt_muxtree pass)
+
+module usb_tx_phy(clk, rst, DataOut_i, TxValid_i, hold_reg);
+
+input clk;
+input rst;
+input DataOut_i;
+input TxValid_i;
+output reg hold_reg;
+
+reg state, next_state;
+reg ld_sop_d;
+reg ld_data_d;
+
+always @(posedge clk)
+ if(ld_sop_d)
+ hold_reg <= 0;
+ else
+ hold_reg <= DataOut_i;
+
+always @(posedge clk)
+ if(!rst) state <= 0;
+ else state <= next_state;
+
+always @(state or TxValid_i)
+ begin
+ next_state = state;
+
+ ld_sop_d = 1'b0;
+ ld_data_d = 1'b0;
+
+ case(state) // synopsys full_case parallel_case
+ 0:
+ if(TxValid_i)
+ begin
+ ld_sop_d = 1'b1;
+ next_state = 1;
+ end
+ 1:
+ if(TxValid_i)
+ begin
+ ld_data_d = 1'b1;
+ next_state = 0;
+ end
+ endcase
+ end
+
+endmodule
+
+
+// test case inspired by softusb_navre code:
+// default not as last case
+
+module default_cases(a, y);
+
+input [2:0] a;
+output reg [3:0] y;
+
+always @* begin
+ case (a)
+ 3'b000, 3'b111: y <= 0;
+ default: y <= 4;
+ 3'b001: y <= 1;
+ 3'b010: y <= 2;
+ 3'b100: y <= 3;
+ endcase
+end
+
+endmodule
+
+
+// test case for muxtree with select on leaves
+
+module select_leaves(input R, C, D, output reg Q);
+ always @(posedge C)
+ if (!R)
+ Q <= R;
+ else
+ Q <= Q ? Q : D ? D : Q;
+endmodule
+
diff --git a/tests/simple/omsp_dbg_uart.v b/tests/simple/omsp_dbg_uart.v
new file mode 100644
index 00000000..569a28ad
--- /dev/null
+++ b/tests/simple/omsp_dbg_uart.v
@@ -0,0 +1,34 @@
+
+module omsp_dbg_uart (dbg_clk, dbg_rst, mem_burst, cmd_valid);
+
+input dbg_clk;
+input dbg_rst;
+input mem_burst;
+output cmd_valid;
+
+reg [2:0] uart_state;
+reg [2:0] uart_state_nxt;
+
+wire xfer_done;
+
+parameter RX_SYNC = 3'h0;
+parameter RX_CMD = 3'h1;
+parameter RX_DATA = 3'h2;
+
+always @(uart_state or mem_burst)
+ case (uart_state)
+ RX_SYNC : uart_state_nxt = RX_CMD;
+ RX_CMD : uart_state_nxt = mem_burst ? RX_DATA : RX_SYNC;
+ RX_DATA : uart_state_nxt = RX_SYNC;
+ default : uart_state_nxt = RX_CMD;
+ endcase
+
+always @(posedge dbg_clk or posedge dbg_rst)
+ if (dbg_rst) uart_state <= RX_SYNC;
+ else if (xfer_done | mem_burst) uart_state <= uart_state_nxt;
+
+assign cmd_valid = (uart_state==RX_CMD) & xfer_done;
+assign xfer_done = uart_state!=RX_SYNC;
+
+endmodule
+
diff --git a/tests/simple/operators.v b/tests/simple/operators.v
new file mode 100644
index 00000000..2f0fdb82
--- /dev/null
+++ b/tests/simple/operators.v
@@ -0,0 +1,121 @@
+module optest(clk, mode, u1, s1, u2, s2, y);
+
+input clk;
+input [6:0] mode;
+
+input [3:0] u1, u2;
+input signed [3:0] s1, s2;
+
+output reg [7:0] y;
+
+always @(posedge clk) begin
+ y <= 8'h42;
+ case (mode)
+ 0: y <= u1 << u2;
+ 1: y <= u1 << s2;
+ 2: y <= s1 << u2;
+ 3: y <= s1 << s2;
+
+ 4: y <= u1 >> u2;
+ 5: y <= u1 >> s2;
+ 6: y <= s1 >> u2;
+ 7: y <= s1 >> s2;
+
+ 8: y <= u1 <<< u2;
+ 9: y <= u1 <<< s2;
+ 10: y <= s1 <<< u2;
+ 11: y <= s1 <<< s2;
+
+ 12: y <= u1 >>> u2;
+ 13: y <= u1 >>> s2;
+ 14: y <= s1 >>> u2;
+ 15: y <= s1 >>> s2;
+
+ 16: y <= u1 < u2;
+ 17: y <= u1 < s2;
+ 18: y <= s1 < u2;
+ 19: y <= s1 < s2;
+
+ 20: y <= u1 <= u2;
+ 21: y <= u1 <= s2;
+ 22: y <= s1 <= u2;
+ 23: y <= s1 <= s2;
+
+ 24: y <= u1 == u2;
+ 25: y <= u1 == s2;
+ 26: y <= s1 == u2;
+ 27: y <= s1 == s2;
+
+ 28: y <= u1 != u2;
+ 29: y <= u1 != s2;
+ 30: y <= s1 != u2;
+ 31: y <= s1 != s2;
+
+ 32: y <= u1 >= u2;
+ 33: y <= u1 >= s2;
+ 34: y <= s1 >= u2;
+ 35: y <= s1 >= s2;
+
+ 36: y <= u1 > u2;
+ 37: y <= u1 > s2;
+ 38: y <= s1 > u2;
+ 39: y <= s1 > s2;
+
+ 40: y <= u1 + u2;
+ 41: y <= u1 + s2;
+ 42: y <= s1 + u2;
+ 43: y <= s1 + s2;
+
+ 44: y <= u1 - u2;
+ 45: y <= u1 - s2;
+ 46: y <= s1 - u2;
+ 47: y <= s1 - s2;
+
+ 48: y <= u1 * u2;
+ 49: y <= u1 * s2;
+ 50: y <= s1 * u2;
+ 51: y <= s1 * s2;
+
+ 52: y <= u1 / u2;
+ 53: y <= u1 / s2;
+ 54: y <= s1 / u2;
+ 55: y <= s1 / s2;
+
+ 56: y <= u1 % u2;
+ 57: y <= u1 % s2;
+ 58: y <= s1 % u2;
+ 59: y <= s1 % s2;
+
+ 60: y <= 4'd2 ** u1;
+ 61: y <= 4'd2 ** s1;
+ 62: y <= 4'sd2 ** u1;
+ 63: y <= 4'sd2 ** s1;
+
+ 64: y <= +u1;
+ 65: y <= -u1;
+ 66: y <= ~u1;
+ 67: y <= !u1;
+
+ 68: y <= +s1;
+ 69: y <= -s1;
+ 70: y <= ~s1;
+ 71: y <= !s1;
+
+ 72: y <= { &u1, ~&u1, |u1, ~|u1, ^u1, ~^u1, ^~u1 };
+ 73: y <= { &s1, ~&s1, |s1, ~|s1, ^s1, ~^s1, ^~s1 };
+ 74: y <= { &u1[1:0], ~&u1[1:0], |u1[1:0], ~|u1[1:0], ^u1[1:0], ~^u1[1:0], ^~u1[1:0] };
+ 75: y <= { &s1[1:0], ~&s1[1:0], |s1[1:0], ~|s1[1:0], ^s1[1:0], ~^s1[1:0], ^~s1[1:0] };
+
+ 76: y <= { u1[1:0] && u2[1:0], u1[1:0] && u2[1:0], !u1[1:0] };
+ 77: y <= {4{u1[1:0]}};
+ 78: y <= {u1, u2} ^ {s1, s2};
+ 79: y <= {u1, u2} & {s1, s2};
+
+ 80: y <= u1[0] ? u1 : u2;
+ 81: y <= u1[0] ? u1 : s2;
+ 82: y <= u1[0] ? s1 : u2;
+ 83: y <= u1[0] ? s1 : s2;
+ endcase
+end
+
+endmodule
diff --git a/tests/simple/paramods.v b/tests/simple/paramods.v
new file mode 100644
index 00000000..23cb276f
--- /dev/null
+++ b/tests/simple/paramods.v
@@ -0,0 +1,53 @@
+
+module pm_test1(a, b, x, y);
+
+input [7:0] a, b;
+output [7:0] x, y;
+
+inc #(.step(3)) inc_a (.in(a), .out(x));
+inc #(.width(4), .step(7)) inc_b (b, y);
+
+endmodule
+
+// -----------------------------------
+
+module pm_test2(a, b, x, y);
+
+input [7:0] a, b;
+output [7:0] x, y;
+
+inc #(5) inc_a (.in(a), .out(x));
+inc #(4, 7) inc_b (b, y);
+
+endmodule
+
+// -----------------------------------
+
+module pm_test3(a, b, x, y);
+
+input [7:0] a, b;
+output [7:0] x, y;
+
+inc inc_a (.in(a), .out(x));
+inc inc_b (b, y);
+
+defparam inc_a.step = 3;
+defparam inc_b.step = 7;
+defparam inc_b.width = 4;
+
+endmodule
+
+// -----------------------------------
+
+module inc(in, out);
+
+parameter width = 8;
+parameter step = 1;
+
+input [width-1:0] in;
+output [width-1:0] out;
+
+assign out = in + step;
+
+endmodule
+
diff --git a/tests/simple/partsel.v b/tests/simple/partsel.v
new file mode 100644
index 00000000..7461358a
--- /dev/null
+++ b/tests/simple/partsel.v
@@ -0,0 +1,62 @@
+module partsel_test001(input [2:0] idx, input [31:0] data, output [3:0] slice_up, slice_down);
+wire [5:0] offset = idx << 2;
+assign slice_up = data[offset +: 4];
+assign slice_down = data[offset + 3 -: 4];
+endmodule
+
+module partsel_test002 (
+ input clk, rst,
+ input [7:0] a,
+ input [0:7] b,
+ input [1:0] s,
+ output [7:0] x1, x2, x3,
+ output [0:7] x4, x5, x6,
+ output [7:0] y1, y2, y3,
+ output [0:7] y4, y5, y6,
+ output [7:0] z1, z2, z3,
+ output [0:7] z4, z5, z6,
+ output [7:0] w1, w2, w3,
+ output [0:7] w4, w5, w6,
+ output [7:0] p1, p2, p3, p4, p5, p6,
+ output [0:7] q1, q2, q3, q4, q5, q6,
+ output reg [7:0] r1,
+ output reg [0:7] r2
+);
+
+assign x1 = a, x2 = a + b, x3 = b;
+assign x4 = a, x5 = a + b, x6 = b;
+assign y1 = a[4 +: 3], y2 = a[4 +: 3] + b[4 +: 3], y3 = b[4 +: 3];
+assign y4 = a[4 +: 3], y5 = a[4 +: 3] + b[4 +: 3], y6 = b[4 +: 3];
+assign z1 = a[4 -: 3], z2 = a[4 -: 3] + b[4 -: 3], z3 = b[4 -: 3];
+assign z4 = a[4 -: 3], z5 = a[4 -: 3] + b[4 -: 3], z6 = b[4 -: 3];
+assign w1 = a[6:3], w2 = a[6:3] + b[3:6], w3 = b[3:6];
+assign w4 = a[6:3], w5 = a[6:3] + b[3:6], w6 = b[3:6];
+assign p1 = a[s], p2 = b[s], p3 = a[s+2 +: 2], p4 = b[s+2 +: 2], p5 = a[s+2 -: 2], p6 = b[s+2 -: 2];
+assign q1 = a[s], q2 = b[s], q3 = a[s+2 +: 2], q4 = b[s+2 +: 2], q5 = a[s+2 -: 2], q6 = b[s+2 -: 2];
+
+always @(posedge clk) begin
+ if (rst) begin
+ { r1, r2 } = 16'h1337 ^ {a, b};
+ end else begin
+ case (s)
+ 0: begin
+ r1[3:0] <= r2[0:3] ^ x1;
+ r2[4:7] <= r1[7:4] ^ x4;
+ end
+ 1: begin
+ r1[2 +: 3] <= r2[5 -: 3] + x1;
+ r2[3 +: 3] <= r1[6 -: 3] + x4;
+ end
+ 2: begin
+ r1[6 -: 3] <= r2[3 +: 3] - x1;
+ r2[7 -: 3] <= r1[4 +: 3] - x4;
+ end
+ 3: begin
+ r1 <= r2;
+ r2 <= r1;
+ end
+ endcase
+ end
+end
+
+endmodule
diff --git a/tests/simple/process.v b/tests/simple/process.v
new file mode 100644
index 00000000..8cb4c870
--- /dev/null
+++ b/tests/simple/process.v
@@ -0,0 +1,84 @@
+
+module blocking_cond (in, out);
+
+input in;
+output reg out;
+reg tmp;
+
+always @* begin
+ tmp = 1;
+ out = 1'b0;
+ case (1'b1)
+ tmp: out = in;
+ endcase
+ tmp = 0;
+end
+
+endmodule
+
+// -------------------------------------------------------------
+
+module uut(clk, arst, a, b, c, d, e, f, out1);
+
+input clk, arst, a, b, c, d, e, f;
+output reg [3:0] out1;
+
+always @(posedge clk, posedge arst) begin
+ if (arst)
+ out1 = 0;
+ else begin
+ if (a) begin
+ case ({b, c})
+ 2'b00:
+ out1 = out1 + 9;
+ 2'b01, 2'b10:
+ out1 = out1 + 13;
+ endcase
+ if (d) begin
+ out1 = out1 + 2;
+ out1 = out1 + 1;
+ end
+ case ({e, f})
+ 2'b11:
+ out1 = out1 + 8;
+ 2'b00:
+ ;
+ default:
+ out1 = out1 + 10;
+ endcase
+ out1 = out1 ^ 7;
+ end
+ out1 = out1 + 14;
+ end
+end
+
+endmodule
+
+// -------------------------------------------------------------
+
+// extracted from ../asicworld/code_hdl_models_uart.v
+// (triggered a bug in the proc_mux pass)
+module uart (reset, txclk, ld_tx_data, tx_empty, tx_cnt);
+
+input reset;
+input txclk;
+input ld_tx_data;
+
+output reg tx_empty;
+output reg [3:0] tx_cnt;
+
+always @ (posedge txclk)
+if (reset) begin
+ tx_empty <= 1;
+ tx_cnt <= 0;
+end else begin
+ if (ld_tx_data) begin
+ tx_empty <= 0;
+ end
+ if (!tx_empty) begin
+ tx_cnt <= tx_cnt + 1;
+ end
+end
+
+endmodule
+
diff --git a/tests/simple/realexpr.v b/tests/simple/realexpr.v
new file mode 100644
index 00000000..5b756e6b
--- /dev/null
+++ b/tests/simple/realexpr.v
@@ -0,0 +1,24 @@
+
+module demo_001(y1, y2, y3, y4);
+ output [7:0] y1, y2, y3, y4;
+
+ localparam [7:0] p1 = 123.45;
+ localparam real p2 = 123.45;
+ localparam real p3 = 123;
+ localparam p4 = 123.45;
+
+ assign y1 = p1 + 0.2;
+ assign y2 = p2 + 0.2;
+ assign y3 = p3 + 0.2;
+ assign y4 = p4 + 0.2;
+endmodule
+
+module demo_002(y0, y1, y2, y3);
+ output [63:0] y0, y1, y2, y3;
+
+ assign y0 = 1'bx >= (-1 * -1.17);
+ assign y1 = 1 ? 1 ? -1 : 'd0 : 0.0;
+ assign y2 = 1 ? -1 : 1 ? 'd0 : 0.0;
+ assign y3 = 1 ? -1 : 'd0;
+endmodule
+
diff --git a/tests/simple/repwhile.v b/tests/simple/repwhile.v
new file mode 100644
index 00000000..5d0c75fa
--- /dev/null
+++ b/tests/simple/repwhile.v
@@ -0,0 +1,36 @@
+module repwhile_test001(input [5:0] a, output [7:0] y, output [31:0] x);
+
+ function [7:0] mylog2;
+ input [31:0] value;
+ begin
+ mylog2 = 0;
+ while (value > 0) begin
+ value = value >> 1;
+ mylog2 = mylog2 + 1;
+ end
+ end
+ endfunction
+
+ function [31:0] myexp2;
+ input [7:0] value;
+ begin
+ myexp2 = 1;
+ repeat (value)
+ myexp2 = myexp2 << 1;
+ end
+ endfunction
+
+ reg [7:0] y_table [63:0];
+ reg [31:0] x_table [63:0];
+
+ integer i;
+ initial begin
+ for (i = 0; i < 64; i = i+1) begin
+ y_table[i] <= mylog2(i);
+ x_table[i] <= myexp2(i);
+ end
+ end
+
+ assign y = y_table[a];
+ assign x = x_table[a];
+endmodule
diff --git a/tests/simple/rotate.v b/tests/simple/rotate.v
new file mode 100644
index 00000000..a2fe0005
--- /dev/null
+++ b/tests/simple/rotate.v
@@ -0,0 +1,48 @@
+
+// test case taken from amber23 Verilog code
+module a23_barrel_shift_fpga_rotate(i_in, direction, shift_amount, rot_prod);
+
+input [31:0] i_in;
+input direction;
+input [4:0] shift_amount;
+output [31:0] rot_prod;
+
+// Generic rotate. Theoretical cost: 32x5 4-to-1 LUTs.
+// Practically a bit higher due to high fanout of "direction".
+generate
+genvar i, j;
+ for (i = 0; i < 5; i = i + 1)
+ begin : netgen
+ wire [31:0] in;
+ reg [31:0] out;
+ for (j = 0; j < 32; j = j + 1)
+ begin : net
+ always @*
+ out[j] = in[j] & (~shift_amount[i] ^ direction) |
+ in[wrap(j, i)] & (shift_amount[i] ^ direction);
+ end
+ end
+
+ // Order is reverted with respect to volatile shift_amount[0]
+ assign netgen[4].in = i_in;
+ for (i = 1; i < 5; i = i + 1)
+ begin : router
+ assign netgen[i-1].in = netgen[i].out;
+ end
+endgenerate
+
+// Aliasing
+assign rot_prod = netgen[0].out;
+
+function [4:0] wrap;
+input integer pos;
+input integer level;
+integer out;
+begin
+ out = pos - (1 << level);
+ wrap = out[4:0];
+end
+endfunction
+
+endmodule
+
diff --git a/tests/simple/run-test.sh b/tests/simple/run-test.sh
new file mode 100755
index 00000000..aaa1cf94
--- /dev/null
+++ b/tests/simple/run-test.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+OPTIND=1
+seed="" # default to no seed specified
+while getopts "S:" opt
+do
+ case "$opt" in
+ S) arg="${OPTARG#"${OPTARG%%[![:space:]]*}"}" # remove leading space
+ seed="SEED=$arg" ;;
+ esac
+done
+shift "$((OPTIND-1))"
+
+# check for Icarus Verilog
+if ! which iverilog > /dev/null ; then
+ echo "$0: Error: Icarus Verilog 'iverilog' not found."
+ exit 1
+fi
+
+exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.v
diff --git a/tests/simple/scopes.v b/tests/simple/scopes.v
new file mode 100644
index 00000000..eecc1a0b
--- /dev/null
+++ b/tests/simple/scopes.v
@@ -0,0 +1,63 @@
+module scopes_test_01(input [3:0] k, output reg [15:0] x, y);
+ function [15:0] func_01;
+ input [15:0] x, y;
+ begin
+ func_01 = x + y;
+ begin:blk
+ reg [15:0] x;
+ x = y;
+ func_01 = func_01 ^ x;
+ end
+ func_01 = func_01 ^ x;
+ end
+ endfunction
+
+ function [15:0] func_02;
+ input [15:0] x, y;
+ begin
+ func_02 = x - y;
+ begin:blk
+ reg [15:0] func_02;
+ func_02 = 0;
+ end
+ end
+ endfunction
+
+ task task_01;
+ input [3:0] a;
+ reg [15:0] y;
+ begin
+ y = a * 23;
+ x = x + y;
+ end
+ endtask
+
+ task task_02;
+ input [3:0] a;
+ begin:foo
+ reg [15:0] x, z;
+ x = y;
+ begin:bar
+ reg [15:0] x;
+ x = 77 + a;
+ z = -x;
+ end
+ y = x ^ z;
+ end
+ endtask
+
+ always @* begin
+ x = func_01(11, 22);
+ y = func_02(33, 44);
+ task_01(k);
+ task_02(k);
+ begin:foo
+ reg [15:0] y;
+ y = x;
+ y = y + k;
+ x = y;
+ end
+ x = func_01(y, x);
+ y = func_02(y, x);
+ end
+endmodule
diff --git a/tests/simple/signedexpr.v b/tests/simple/signedexpr.v
new file mode 100644
index 00000000..8bba4a4b
--- /dev/null
+++ b/tests/simple/signedexpr.v
@@ -0,0 +1,18 @@
+module signed_test01(a, b, xu, xs, yu, ys, zu, zs);
+
+input signed [1:0] a;
+input signed [2:0] b;
+output [3:0] xu, xs;
+output [3:0] yu, ys;
+output zu, zs;
+
+assign xu = (a + b) + 3'd0;
+assign xs = (a + b) + 3'sd0;
+
+assign yu = {a + b} + 3'd0;
+assign ys = {a + b} + 3'sd0;
+
+assign zu = a + b != 3'd0;
+assign zs = a + b != 3'sd0;
+
+endmodule
diff --git a/tests/simple/sincos.v b/tests/simple/sincos.v
new file mode 100644
index 00000000..b3124337
--- /dev/null
+++ b/tests/simple/sincos.v
@@ -0,0 +1,124 @@
+// File: design.v
+// Generated by MyHDL 0.8
+// Date: Tue Dec 3 04:33:14 2013
+
+
+module d (
+ cos_z0,
+ sin_z0,
+ done,
+ z0,
+ start,
+ clock,
+ reset
+);
+// Sine and cosine computer.
+//
+// This module computes the sine and cosine of an input angle. The
+// floating point numbers are represented as integers by scaling them
+// up with a factor corresponding to the number of bits after the point.
+//
+// Ports:
+// -----
+// cos_z0: cosine of the input angle
+// sin_z0: sine of the input angle
+// done: output flag indicated completion of the computation
+// z0: input angle
+// start: input that starts the computation on a posedge
+// clock: clock input
+// reset: reset input
+
+output signed [19:0] cos_z0;
+reg signed [19:0] cos_z0;
+output signed [19:0] sin_z0;
+reg signed [19:0] sin_z0;
+output done;
+reg done;
+input signed [19:0] z0;
+input start;
+input clock;
+input reset;
+
+(* gentb_constant = 1'b0 *)
+wire reset;
+
+always @(posedge clock, posedge reset) begin: DESIGN_PROCESSOR
+ reg [5-1:0] i;
+ reg [1-1:0] state;
+ reg signed [20-1:0] dz;
+ reg signed [20-1:0] dx;
+ reg signed [20-1:0] dy;
+ reg signed [20-1:0] y;
+ reg signed [20-1:0] x;
+ reg signed [20-1:0] z;
+ if (reset) begin
+ state = 1'b0;
+ cos_z0 <= 1;
+ sin_z0 <= 0;
+ done <= 1'b0;
+ x = 0;
+ y = 0;
+ z = 0;
+ i = 0;
+ end
+ else begin
+ case (state)
+ 1'b0: begin
+ if (start) begin
+ x = 159188;
+ y = 0;
+ z = z0;
+ i = 0;
+ done <= 1'b0;
+ state = 1'b1;
+ end
+ end
+ 1'b1: begin
+ dx = $signed(y >>> $signed({1'b0, i}));
+ dy = $signed(x >>> $signed({1'b0, i}));
+ case (i)
+ 0: dz = 205887;
+ 1: dz = 121542;
+ 2: dz = 64220;
+ 3: dz = 32599;
+ 4: dz = 16363;
+ 5: dz = 8189;
+ 6: dz = 4096;
+ 7: dz = 2048;
+ 8: dz = 1024;
+ 9: dz = 512;
+ 10: dz = 256;
+ 11: dz = 128;
+ 12: dz = 64;
+ 13: dz = 32;
+ 14: dz = 16;
+ 15: dz = 8;
+ 16: dz = 4;
+ 17: dz = 2;
+ default: dz = 1;
+ endcase
+ if ((z >= 0)) begin
+ x = x - dx;
+ y = y + dy;
+ z = z - dz;
+ end
+ else begin
+ x = x + dx;
+ y = y - dy;
+ z = z + dz;
+ end
+ if ((i == (19 - 1))) begin
+ cos_z0 <= x;
+ sin_z0 <= y;
+ state = 1'b0;
+ done <= 1'b1;
+ end
+ else begin
+ i = i + 1;
+ end
+ end
+ endcase
+ end
+end
+
+endmodule
diff --git a/tests/simple/subbytes.v b/tests/simple/subbytes.v
new file mode 100644
index 00000000..04269a99
--- /dev/null
+++ b/tests/simple/subbytes.v
@@ -0,0 +1,82 @@
+
+// test taken from systemcaes from iwls2005
+
+module subbytes_00(clk, reset, start_i, decrypt_i, data_i, ready_o, data_o, sbox_data_o, sbox_data_i, sbox_decrypt_o);
+
+input clk;
+input reset;
+input start_i;
+input decrypt_i;
+input [31:0] data_i;
+output ready_o;
+output [31:0] data_o;
+output [7:0] sbox_data_o;
+input [7:0] sbox_data_i;
+output sbox_decrypt_o;
+
+reg ready_o;
+reg [31:0] data_o;
+reg [7:0] sbox_data_o;
+reg sbox_decrypt_o;
+
+reg [1:0] state;
+reg [1:0] next_state;
+reg [31:0] data_reg;
+reg [31:0] next_data_reg;
+reg next_ready_o;
+
+always @(posedge clk or negedge reset)
+begin
+ if (!reset) begin
+ data_reg = 0;
+ state = 0;
+ ready_o = 0;
+ end else begin
+ data_reg = next_data_reg;
+ state = next_state;
+ ready_o = next_ready_o;
+ end
+end
+
+reg [31:0] data_i_var, data_reg_128;
+reg [7:0] data_array [3:0];
+reg [7:0] data_reg_var [3:0];
+
+always @(decrypt_i or start_i or state or data_i or sbox_data_i or data_reg)
+begin
+ data_i_var = data_i;
+
+ data_array[0] = data_i_var[ 31: 24];
+ data_array[1] = data_i_var[ 23: 16];
+ data_array[2] = data_i_var[ 15: 8];
+ data_array[3] = data_i_var[ 7: 0];
+
+ data_reg_var[0] = data_reg[ 31: 24];
+ data_reg_var[1] = data_reg[ 23: 16];
+ data_reg_var[2] = data_reg[ 15: 8];
+ data_reg_var[3] = data_reg[ 7: 0];
+
+ sbox_decrypt_o = decrypt_i;
+ sbox_data_o = data_array[state];
+ next_state = state;
+ next_data_reg = data_reg;
+
+ next_ready_o = 0;
+ data_o = data_reg;
+
+ if (state) begin
+ if (start_i) begin
+ next_state = 1;
+ end
+ end else begin
+ data_reg_var[state] = sbox_data_i;
+ data_reg_128[ 31: 24] = data_reg_var[0];
+ data_reg_128[ 23: 16] = data_reg_var[1];
+ data_reg_128[ 15: 8] = data_reg_var[2];
+ data_reg_128[ 7: 0] = data_reg_var[3];
+ next_data_reg = data_reg_128;
+ next_state = state + 1;
+ end
+end
+
+endmodule
diff --git a/tests/simple/task_func.v b/tests/simple/task_func.v
new file mode 100644
index 00000000..fa50c1d5
--- /dev/null
+++ b/tests/simple/task_func.v
@@ -0,0 +1,122 @@
+
+module task_func_test01(clk, a, b, c, x, y, z, w);
+
+input clk;
+input [7:0] a, b, c;
+output reg [7:0] x, y, z, w;
+
+function [7:0] sum_shift;
+input [3:0] s1, s2, s3;
+sum_shift = s1 + (s2 << 2) + (s3 << 4);
+endfunction
+
+task reset_w;
+w = 0;
+endtask
+
+task add_to;
+output [7:0] out;
+input [7:0] in;
+out = out + in;
+endtask
+
+always @(posedge clk) begin
+ x = sum_shift(a, b, c);
+ y = sum_shift(a[7:4], b[5:2], c[3:0]);
+ z = sum_shift(a[0], b[5:4], c >> 5) ^ sum_shift(1, 2, 3);
+
+ reset_w;
+ add_to(w, x);
+ add_to(w, y);
+ add_to(w, z);
+end
+
+endmodule
+
+// -------------------------------------------------------------------
+
+module task_func_test02(clk, a, b, c, x, y, z, w);
+
+input clk;
+input [7:0] a, b, c;
+output reg [7:0] x, y, z, w;
+
+function [7:0] sum_shift(input [3:0] s1, s2, s3);
+sum_shift = s1 + (s2 << 2) + (s3 << 4);
+endfunction
+
+task reset_w;
+w = 0;
+endtask
+
+task add_to(output [7:0] out, input [7:0] in);
+out = out + in;
+endtask
+
+always @(posedge clk) begin
+ x = sum_shift(a, b, c);
+ y = sum_shift(a[7:4], b[5:2], c[3:0]);
+ z = sum_shift(a[0], b[5:4], c >> 5) ^ sum_shift(1, 2, 3);
+
+ reset_w;
+ add_to(w, x);
+ add_to(w, y);
+ add_to(w, z);
+end
+
+endmodule
+
+// -------------------------------------------------------------------
+
+module task_func_test03(input [7:0] din_a, input [7:0] din_b, output [7:0] dout_a);
+ assign dout_a = test(din_a,din_b);
+ function [7:0] test;
+ input [7:0] a;
+ input [7:0] b;
+ begin : TEST
+ integer i;
+ for (i = 0; i <= 7; i = i + 1)
+ test[i] = a[i] & b[i];
+ end
+ endfunction
+endmodule
+
+// -------------------------------------------------------------------
+
+module task_func_test04(input [7:0] in, output [7:0] out1, out2, out3, out4);
+ parameter p = 23;
+ parameter px = 42;
+ function [7:0] test1;
+ input [7:0] i;
+ parameter p = 42;
+ begin
+ test1 = i + p;
+ end
+ endfunction
+ function [7:0] test2;
+ input [7:0] i;
+ parameter p2 = p+42;
+ begin
+ test2 = i + p2;
+ end
+ endfunction
+ function [7:0] test3;
+ input [7:0] i;
+ begin
+ test3 = i + p;
+ end
+ endfunction
+ function [7:0] test4;
+ input [7:0] i;
+ parameter px = p + 13;
+ parameter p3 = px - 37;
+ parameter p4 = p3 ^ px;
+ begin
+ test4 = i + p4;
+ end
+ endfunction
+ assign out1 = test1(in);
+ assign out2 = test2(in);
+ assign out3 = test3(in);
+ assign out4 = test4(in);
+endmodule
diff --git a/tests/simple/undef_eqx_nex.v b/tests/simple/undef_eqx_nex.v
new file mode 100644
index 00000000..b0178677
--- /dev/null
+++ b/tests/simple/undef_eqx_nex.v
@@ -0,0 +1,11 @@
+module undef_eqx_nex(y);
+output [7:0] y;
+assign y[0] = 0/0;
+assign y[1] = 0/1;
+assign y[2] = 0/0 == 32'bx;
+assign y[3] = 0/0 != 32'bx;
+assign y[4] = 0/0 === 32'bx;
+assign y[5] = 0/0 !== 32'bx;
+assign y[6] = 0/1 === 32'bx;
+assign y[7] = 0/1 !== 32'bx;
+endmodule
diff --git a/tests/simple/usb_phy_tests.v b/tests/simple/usb_phy_tests.v
new file mode 100644
index 00000000..bc45e71a
--- /dev/null
+++ b/tests/simple/usb_phy_tests.v
@@ -0,0 +1,36 @@
+
+// from usb_rx_phy
+module usb_phy_test01(clk, rst, rx_en, fs_ce);
+
+input clk, rst;
+input rx_en;
+output reg fs_ce;
+reg [1:0] dpll_next_state;
+reg [1:0] dpll_state;
+
+always @(posedge clk)
+ dpll_state <= rst ? 0 : dpll_next_state;
+
+always @*
+ begin
+ fs_ce = 1'b0;
+ case(dpll_state)
+ 2'h0:
+ if(rx_en) dpll_next_state = 2'h0;
+ else dpll_next_state = 2'h1;
+ 2'h1:begin
+ fs_ce = 1'b1;
+ if(rx_en) dpll_next_state = 2'h3;
+ else dpll_next_state = 2'h2;
+ end
+ 2'h2:
+ if(rx_en) dpll_next_state = 2'h0;
+ else dpll_next_state = 2'h3;
+ 2'h3:
+ if(rx_en) dpll_next_state = 2'h0;
+ else dpll_next_state = 2'h0;
+ endcase
+ end
+
+endmodule
+
diff --git a/tests/simple/values.v b/tests/simple/values.v
new file mode 100644
index 00000000..afcd251f
--- /dev/null
+++ b/tests/simple/values.v
@@ -0,0 +1,44 @@
+
+module test_signed(a, b, c, d, y);
+
+input [3:0] a, b, c;
+input signed [3:0] d;
+output reg [7:0] y;
+
+always @* begin
+ if (a && b)
+ y = c;
+ else
+ y = d;
+end
+
+endmodule
+
+module test_const(a, y);
+
+input [3:0] a;
+output reg [28:0] y;
+
+always @*
+ case (a)
+ 4'b0000: y = 0;
+ 4'b0001: y = 11;
+ 4'b0010: y = 222;
+ 4'b0011: y = 3456;
+ 4'b0100: y = 'b10010010;
+ 4'b0101: y = 'h123abc;
+ 4'b0110: y = 'o1234567;
+ 4'b0111: y = 'd3456789;
+ 4'b1000: y = 16'b10010010;
+ 4'b1001: y = 16'h123abc;
+ 4'b1010: y = 16'o1234567;
+ 4'b1011: y = 16'd3456789;
+ 4'b1100: y = { "foo", "bar" };
+ 4'b1101: y = "foobarfoobarfoobar";
+ 4'b1110: y = 16'h1;
+ 4'b1111: y = a;
+ default: y = 'bx;
+ endcase
+
+endmodule
+
diff --git a/tests/simple/vloghammer.v b/tests/simple/vloghammer.v
new file mode 100644
index 00000000..3bb3cf99
--- /dev/null
+++ b/tests/simple/vloghammer.v
@@ -0,0 +1,82 @@
+
+// test cases found using vloghammer
+// https://github.com/cliffordwolf/VlogHammer
+
+module test01(a, y);
+ input [7:0] a;
+ output [3:0] y;
+ assign y = ~a >> 4;
+endmodule
+
+module test02(a, y);
+ input signed [3:0] a;
+ output signed [4:0] y;
+ assign y = (~a) >> 1;
+endmodule
+
+module test03(a, b, y);
+ input [2:0] a;
+ input signed [1:0] b;
+ output y;
+ assign y = ~(a >>> 1) == b;
+endmodule
+
+module test04(a, y);
+ input a;
+ output [1:0] y;
+ assign y = ~(a - 1'b0);
+endmodule
+
+// .. this test triggers a bug in Xilinx ISIM.
+// module test05(a, y);
+// input a;
+// output y;
+// assign y = 12345 >> {a, 32'd0};
+// endmodule
+
+// .. this test triggers a bug in Icarus Verilog.
+// module test06(a, b, c, y);
+// input signed [3:0] a;
+// input signed [1:0] b;
+// input signed [1:0] c;
+// output [5:0] y;
+// assign y = (a >> b) >>> c;
+// endmodule
+
+module test07(a, b, y);
+ input signed [1:0] a;
+ input signed [2:0] b;
+ output y;
+ assign y = 2'b11 != a+b;
+endmodule
+
+module test08(a, b, y);
+ input [1:0] a;
+ input [1:0] b;
+ output y;
+ assign y = a == ($signed(b) >>> 1);
+endmodule
+
+module test09(a, b, c, y);
+ input a;
+ input signed [1:0] b;
+ input signed [2:0] c;
+ output [3:0] y;
+ assign y = a ? b : c;
+endmodule
+
+module test10(a, b, c, y);
+ input a;
+ input signed [1:0] b;
+ input signed [2:0] c;
+ output y;
+ assign y = ^(a ? b : c);
+endmodule
+
+// module test11(a, b, y);
+// input signed [3:0] a;
+// input signed [3:0] b;
+// output signed [5:0] y;
+// assign y = -(5'd27);
+// endmodule
+
diff --git a/tests/simple/wreduce.v b/tests/simple/wreduce.v
new file mode 100644
index 00000000..ba548438
--- /dev/null
+++ b/tests/simple/wreduce.v
@@ -0,0 +1,9 @@
+module wreduce_test0(input [7:0] a, b, output [15:0] x, y, z);
+ assign x = -$signed({1'b0, a});
+ assign y = $signed({1'b0, a}) + $signed({1'b0, b});
+ assign z = x ^ y;
+endmodule
+
+module wreduce_test1(input [31:0] a, b, output [7:0] x, y, z, w);
+ assign x = a - b, y = a * b, z = a >> b, w = a << b;
+endmodule
diff --git a/tests/smv/.gitignore b/tests/smv/.gitignore
new file mode 100644
index 00000000..9c595a6f
--- /dev/null
+++ b/tests/smv/.gitignore
@@ -0,0 +1 @@
+temp
diff --git a/tests/smv/run-single.sh b/tests/smv/run-single.sh
new file mode 100644
index 00000000..a261f4ea
--- /dev/null
+++ b/tests/smv/run-single.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+cat > $1.tpl <<EOT
+%module main
+ INVARSPEC ! bool(_trigger)
+EOT
+
+cat > $1.ys <<EOT
+echo on
+
+read_ilang $1.il
+hierarchy; proc; opt
+rename -top uut
+design -save gold
+
+synth
+design -stash gate
+
+design -copy-from gold -as gold uut
+design -copy-from gate -as gate uut
+miter -equiv -flatten gold gate main
+hierarchy -top main
+
+dump
+write_smv -tpl $1.tpl $1.smv
+EOT
+
+set -ex
+
+../../yosys -l $1.log -q $1.ys
+NuSMV -bmc $1.smv >> $1.log
+grep "^-- invariant .* is true" $1.log
+
diff --git a/tests/smv/run-test.sh b/tests/smv/run-test.sh
new file mode 100755
index 00000000..74a54486
--- /dev/null
+++ b/tests/smv/run-test.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+set -ex
+
+rm -rf temp
+mkdir -p temp
+
+../../yosys -p 'test_cell -muxdiv -w temp/test all'
+rm -f temp/test_{alu,fa,lcu,lut,macc,shiftx}_*
+
+cat > temp/makefile << "EOT"
+all: $(addsuffix .ok,$(basename $(wildcard temp/test_*.il)))
+%.ok: %.il
+ bash run-single.sh $(basename $<)
+ touch $@
+EOT
+
+${MAKE:-make} -f temp/makefile
+
diff --git a/tests/techmap/.gitignore b/tests/techmap/.gitignore
new file mode 100644
index 00000000..397b4a76
--- /dev/null
+++ b/tests/techmap/.gitignore
@@ -0,0 +1 @@
+*.log
diff --git a/tests/techmap/mem_simple_4x1_cells.v b/tests/techmap/mem_simple_4x1_cells.v
new file mode 100644
index 00000000..7ecdd2de
--- /dev/null
+++ b/tests/techmap/mem_simple_4x1_cells.v
@@ -0,0 +1,13 @@
+module MEM4X1 (CLK, RD_ADDR, RD_DATA, WR_ADDR, WR_DATA, WR_EN);
+ input CLK, WR_DATA, WR_EN;
+ input [3:0] RD_ADDR, WR_ADDR;
+ output reg RD_DATA;
+
+ reg [15:0] memory;
+
+ always @(posedge CLK) begin
+ if (WR_EN)
+ memory[WR_ADDR] <= WR_DATA;
+ RD_DATA <= memory[RD_ADDR];
+ end
+endmodule
diff --git a/tests/techmap/mem_simple_4x1_map.v b/tests/techmap/mem_simple_4x1_map.v
new file mode 100644
index 00000000..762e2938
--- /dev/null
+++ b/tests/techmap/mem_simple_4x1_map.v
@@ -0,0 +1,152 @@
+
+module \$mem (RD_CLK, RD_EN, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA);
+ parameter MEMID = "";
+ parameter SIZE = 256;
+ parameter OFFSET = 0;
+ parameter ABITS = 8;
+ parameter WIDTH = 8;
+ parameter signed INIT = 1'bx;
+
+ parameter RD_PORTS = 1;
+ parameter RD_CLK_ENABLE = 1'b1;
+ parameter RD_CLK_POLARITY = 1'b1;
+ parameter RD_TRANSPARENT = 1'b1;
+
+ parameter WR_PORTS = 1;
+ parameter WR_CLK_ENABLE = 1'b1;
+ parameter WR_CLK_POLARITY = 1'b1;
+
+ input [RD_PORTS-1:0] RD_CLK;
+ input [RD_PORTS-1:0] RD_EN;
+ input [RD_PORTS*ABITS-1:0] RD_ADDR;
+ output reg [RD_PORTS*WIDTH-1:0] RD_DATA;
+
+ input [WR_PORTS-1:0] WR_CLK;
+ input [WR_PORTS*WIDTH-1:0] WR_EN;
+ input [WR_PORTS*ABITS-1:0] WR_ADDR;
+ input [WR_PORTS*WIDTH-1:0] WR_DATA;
+
+ wire [1023:0] _TECHMAP_DO_ = "proc; clean";
+
+ parameter _TECHMAP_CONNMAP_RD_CLK_ = 0;
+ parameter _TECHMAP_CONNMAP_WR_CLK_ = 0;
+
+ parameter _TECHMAP_CONSTVAL_RD_EN_ = 0;
+
+ parameter _TECHMAP_BITS_CONNMAP_ = 0;
+ parameter _TECHMAP_CONNMAP_WR_EN_ = 0;
+
+ reg _TECHMAP_FAIL_;
+ integer k;
+ initial begin
+ _TECHMAP_FAIL_ <= 0;
+
+ // no initialized memories
+ if (INIT !== 1'bx)
+ _TECHMAP_FAIL_ <= 1;
+
+ // only map cells with only one read and one write port
+ if (RD_PORTS > 1 || WR_PORTS > 1)
+ _TECHMAP_FAIL_ <= 1;
+
+ // read enable must be constant high
+ if (_TECHMAP_CONSTVAL_RD_EN_[0] !== 1'b1)
+ _TECHMAP_FAIL_ <= 1;
+
+ // we expect positive read clock and non-transparent reads
+ if (RD_TRANSPARENT || !RD_CLK_ENABLE || !RD_CLK_POLARITY)
+ _TECHMAP_FAIL_ <= 1;
+
+ // we expect positive write clock
+ if (!WR_CLK_ENABLE || !WR_CLK_POLARITY)
+ _TECHMAP_FAIL_ <= 1;
+
+ // only one global write enable bit is supported
+ for (k = 1; k < WR_PORTS*WIDTH; k = k+1)
+ if (_TECHMAP_CONNMAP_WR_EN_[0 +: _TECHMAP_BITS_CONNMAP_] !=
+ _TECHMAP_CONNMAP_WR_EN_[k*_TECHMAP_BITS_CONNMAP_ +: _TECHMAP_BITS_CONNMAP_])
+ _TECHMAP_FAIL_ <= 1;
+
+ // read and write must be in same clock domain
+ if (_TECHMAP_CONNMAP_RD_CLK_ != _TECHMAP_CONNMAP_WR_CLK_)
+ _TECHMAP_FAIL_ <= 1;
+
+ // we don't do small memories or memories with offsets
+ if (OFFSET != 0 || ABITS < 4 || SIZE < 16)
+ _TECHMAP_FAIL_ <= 1;
+ end
+
+ genvar i;
+ generate
+ for (i = 0; i < WIDTH; i=i+1) begin:slice
+ \$__mem_4x1_generator #(
+ .ABITS(ABITS),
+ .SIZE(SIZE)
+ ) bit_slice (
+ .CLK(RD_CLK),
+ .RD_ADDR(RD_ADDR),
+ .RD_DATA(RD_DATA[i]),
+ .WR_ADDR(WR_ADDR),
+ .WR_DATA(WR_DATA[i]),
+ .WR_EN(WR_EN[0])
+ );
+ end
+ endgenerate
+endmodule
+
+module \$__mem_4x1_generator (CLK, RD_ADDR, RD_DATA, WR_ADDR, WR_DATA, WR_EN);
+ parameter ABITS = 4;
+ parameter SIZE = 16;
+
+ input CLK, WR_DATA, WR_EN;
+ input [ABITS-1:0] RD_ADDR, WR_ADDR;
+ output RD_DATA;
+
+ wire [1023:0] _TECHMAP_DO_ = "proc; clean";
+
+ generate
+ if (ABITS > 4) begin
+ wire high_rd_data, low_rd_data;
+ if (SIZE > 2**(ABITS-1)) begin
+ \$__mem_4x1_generator #(
+ .ABITS(ABITS-1),
+ .SIZE(SIZE - 2**(ABITS-1))
+ ) part_high (
+ .CLK(CLK),
+ .RD_ADDR(RD_ADDR[ABITS-2:0]),
+ .RD_DATA(high_rd_data),
+ .WR_ADDR(WR_ADDR[ABITS-2:0]),
+ .WR_DATA(WR_DATA),
+ .WR_EN(WR_EN && WR_ADDR[ABITS-1])
+ );
+ end else begin
+ assign high_rd_data = 1'bx;
+ end
+ \$__mem_4x1_generator #(
+ .ABITS(ABITS-1),
+ .SIZE(SIZE > 2**(ABITS-1) ? 2**(ABITS-1) : SIZE)
+ ) part_low (
+ .CLK(CLK),
+ .RD_ADDR(RD_ADDR[ABITS-2:0]),
+ .RD_DATA(low_rd_data),
+ .WR_ADDR(WR_ADDR[ABITS-2:0]),
+ .WR_DATA(WR_DATA),
+ .WR_EN(WR_EN && !WR_ADDR[ABITS-1])
+ );
+ reg delayed_abit;
+ always @(posedge CLK)
+ delayed_abit <= RD_ADDR[ABITS-1];
+ assign RD_DATA = delayed_abit ? high_rd_data : low_rd_data;
+ end else begin
+ MEM4X1 _TECHMAP_REPLACE_ (
+ .CLK(CLK),
+ .RD_ADDR(RD_ADDR),
+ .RD_DATA(RD_DATA),
+ .WR_ADDR(WR_ADDR),
+ .WR_DATA(WR_DATA),
+ .WR_EN(WR_EN)
+ );
+ end
+ endgenerate
+endmodule
+
diff --git a/tests/techmap/mem_simple_4x1_runtest.sh b/tests/techmap/mem_simple_4x1_runtest.sh
new file mode 100644
index 00000000..e2c6303d
--- /dev/null
+++ b/tests/techmap/mem_simple_4x1_runtest.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -ev
+
+../../yosys -b 'verilog -noattr' -o mem_simple_4x1_synth.v -p 'proc; opt; memory -nomap; techmap -map mem_simple_4x1_map.v;; techmap; opt; abc;; stat' mem_simple_4x1_uut.v
+
+iverilog -o mem_simple_4x1_gold_tb mem_simple_4x1_tb.v mem_simple_4x1_uut.v
+iverilog -o mem_simple_4x1_gate_tb mem_simple_4x1_tb.v mem_simple_4x1_synth.v mem_simple_4x1_cells.v
+
+./mem_simple_4x1_gold_tb > mem_simple_4x1_gold_tb.out
+./mem_simple_4x1_gate_tb > mem_simple_4x1_gate_tb.out
+
+diff -u mem_simple_4x1_gold_tb.out mem_simple_4x1_gate_tb.out
+rm -f mem_simple_4x1_synth.v mem_simple_4x1_tb.vcd
+rm -f mem_simple_4x1_{gold,gate}_tb{,.out}
+: OK
+
diff --git a/tests/techmap/mem_simple_4x1_tb.v b/tests/techmap/mem_simple_4x1_tb.v
new file mode 100644
index 00000000..53262696
--- /dev/null
+++ b/tests/techmap/mem_simple_4x1_tb.v
@@ -0,0 +1,29 @@
+module tb;
+
+reg clk, rst;
+wire [7:0] out;
+wire [4:0] counter;
+
+uut uut (clk, rst, out, counter);
+
+initial begin
+ #5 clk <= 0;
+ repeat (100) #5 clk <= ~clk;
+ #5 $finish;
+end
+
+initial begin
+ rst <= 1;
+ repeat (2) @(posedge clk);
+ rst <= 0;
+end
+
+always @(posedge clk)
+ $display("%d %d %d", rst, out, counter);
+
+initial begin
+ $dumpfile("mem_simple_4x1_tb.vcd");
+ $dumpvars(0, uut);
+end
+
+endmodule
diff --git a/tests/techmap/mem_simple_4x1_uut.v b/tests/techmap/mem_simple_4x1_uut.v
new file mode 100644
index 00000000..8d461459
--- /dev/null
+++ b/tests/techmap/mem_simple_4x1_uut.v
@@ -0,0 +1,15 @@
+module uut (clk, rst, out, counter);
+
+input clk, rst;
+output reg [7:0] out;
+output reg [4:0] counter;
+
+reg [7:0] memory [0:19];
+
+always @(posedge clk) begin
+ counter <= rst || counter == 19 ? 0 : counter+1;
+ memory[counter] <= counter;
+ out <= memory[counter];
+end
+
+endmodule
diff --git a/tests/techmap/run-test.sh b/tests/techmap/run-test.sh
new file mode 100755
index 00000000..e2fc11e5
--- /dev/null
+++ b/tests/techmap/run-test.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+set -e
+for x in *_runtest.sh; do
+ echo "Running $x.."
+ if ! bash $x &> ${x%.sh}.log; then
+ tail ${x%.sh}.log
+ echo ERROR
+ exit 1
+ fi
+done
diff --git a/tests/tools/.gitignore b/tests/tools/.gitignore
new file mode 100644
index 00000000..8176dfbe
--- /dev/null
+++ b/tests/tools/.gitignore
@@ -0,0 +1 @@
+cmp_tbdata
diff --git a/tests/tools/autotest.mk b/tests/tools/autotest.mk
new file mode 100644
index 00000000..c6867892
--- /dev/null
+++ b/tests/tools/autotest.mk
@@ -0,0 +1,13 @@
+
+EXTRA_FLAGS=
+SEED=
+
+ifneq ($(strip $(SEED)),)
+SEEDOPT=-S$(SEED)
+endif
+
+$(MAKECMDGOALS):
+ @$(basename $(MAKEFILE_LIST)).sh -G -j $(SEEDOPT) $(EXTRA_FLAGS) $@
+
+.PHONY: $(MAKECMDGOALS)
+
diff --git a/tests/tools/autotest.sh b/tests/tools/autotest.sh
new file mode 100755
index 00000000..d0b0a89d
--- /dev/null
+++ b/tests/tools/autotest.sh
@@ -0,0 +1,178 @@
+#!/bin/bash
+
+libs=""
+genvcd=false
+use_xsim=false
+use_modelsim=false
+verbose=false
+keeprunning=false
+makejmode=false
+frontend="verilog"
+backend_opts="-noattr -noexpr"
+autotb_opts=""
+include_opts=""
+xinclude_opts=""
+minclude_opts=""
+scriptfiles=""
+scriptopt=""
+toolsdir="$(cd $(dirname $0); pwd)"
+warn_iverilog_git=false
+
+if [ ! -f $toolsdir/cmp_tbdata -o $toolsdir/cmp_tbdata.c -nt $toolsdir/cmp_tbdata ]; then
+ ( set -ex; ${CC:-gcc} -Wall -o $toolsdir/cmp_tbdata $toolsdir/cmp_tbdata.c; ) || exit 1
+fi
+
+while getopts xmGl:wkjvref:s:p:n:S:I: opt; do
+ case "$opt" in
+ x)
+ use_xsim=true ;;
+ m)
+ use_modelsim=true ;;
+ G)
+ warn_iverilog_git=true ;;
+ l)
+ libs="$libs $(cd $(dirname $OPTARG); pwd)/$(basename $OPTARG)";;
+ w)
+ genvcd=true ;;
+ k)
+ keeprunning=true ;;
+ j)
+ makejmode=true ;;
+ v)
+ verbose=true ;;
+ r)
+ backend_opts="$backend_opts -norename" ;;
+ e)
+ backend_opts="$( echo " $backend_opts " | sed 's, -noexpr ,,; s,^ ,,; s, $,,;'; )" ;;
+ f)
+ frontend="$OPTARG" ;;
+ s)
+ [[ "$OPTARG" == /* ]] || OPTARG="$PWD/$OPTARG"
+ scriptfiles="$scriptfiles $OPTARG" ;;
+ p)
+ scriptopt="$OPTARG" ;;
+ n)
+ autotb_opts="$autotb_opts -n $OPTARG" ;;
+ S)
+ autotb_opts="$autotb_opts -seed $OPTARG" ;;
+ I)
+ include_opts="$include_opts -I $OPTARG"
+ xinclude_opts="$xinclude_opts -i $OPTARG"
+ minclude_opts="$minclude_opts +incdir+$OPTARG" ;;
+ *)
+ echo "Usage: $0 [-x|-m] [-G] [-w] [-k] [-j] [-v] [-r] [-e] [-l libs] [-f frontend] [-s script] [-p cmdstring] [-n iters] [-S seed] [-I incdir] verilog-files\n" >&2
+ exit 1
+ esac
+done
+
+compile_and_run() {
+ exe="$1"; output="$2"; shift 2
+ if $use_modelsim; then
+ altver=$( ls -v /opt/altera/ | grep '^[0-9]' | tail -n1; )
+ /opt/altera/$altver/modelsim_ase/bin/vlib work
+ /opt/altera/$altver/modelsim_ase/bin/vlog $minclude_opts +define+outfile=\"$output\" "$@"
+ /opt/altera/$altver/modelsim_ase/bin/vsim -c -do 'run -all; exit;' testbench
+ elif $use_xsim; then
+ xilver=$( ls -v /opt/Xilinx/Vivado/ | grep '^[0-9]' | tail -n1; )
+ /opt/Xilinx/Vivado/$xilver/bin/xvlog $xinclude_opts -d outfile=\"$output\" "$@"
+ /opt/Xilinx/Vivado/$xilver/bin/xelab -R work.testbench
+ else
+ iverilog $include_opts -Doutfile=\"$output\" -s testbench -o "$exe" "$@"
+ vvp -n "$exe"
+ fi
+}
+
+shift $((OPTIND - 1))
+
+for fn
+do
+ bn=${fn%.v}
+ if [ "$bn" == "$fn" ]; then
+ echo "Invalid argument: $fn" >&2
+ exit 1
+ fi
+ [[ "$bn" == *_tb ]] && continue
+
+ if $makejmode; then
+ status_prefix="Test: $bn "
+ else
+ status_prefix=""
+ echo -n "Test: $bn "
+ fi
+
+ rm -f ${bn}.{err,log,sikp}
+ mkdir -p ${bn}.out
+ rm -rf ${bn}.out/*
+
+ body() {
+ cd ${bn}.out
+ fn=$(basename $fn)
+ bn=$(basename $bn)
+
+ egrep -v '^\s*`timescale' ../$fn > ${bn}_ref.v
+
+ if [ ! -f ../${bn}_tb.v ]; then
+ "$toolsdir"/../../yosys -f "$frontend $include_opts" -b "test_autotb $autotb_opts" -o ${bn}_tb.v ${bn}_ref.v
+ else
+ cp ../${bn}_tb.v ${bn}_tb.v
+ fi
+ if $genvcd; then sed -i 's,// \$dump,$dump,g' ${bn}_tb.v; fi
+ compile_and_run ${bn}_tb_ref ${bn}_out_ref ${bn}_tb.v ${bn}_ref.v $libs
+ if $genvcd; then mv testbench.vcd ${bn}_ref.vcd; fi
+
+ test_count=0
+ test_passes() {
+ "$toolsdir"/../../yosys -b "verilog $backend_opts" -o ${bn}_syn${test_count}.v "$@"
+ compile_and_run ${bn}_tb_syn${test_count} ${bn}_out_syn${test_count} \
+ ${bn}_tb.v ${bn}_syn${test_count}.v $libs \
+ "$toolsdir"/../../techlibs/common/simlib.v \
+ "$toolsdir"/../../techlibs/common/simcells.v
+ if $genvcd; then mv testbench.vcd ${bn}_syn${test_count}.vcd; fi
+ $toolsdir/cmp_tbdata ${bn}_out_ref ${bn}_out_syn${test_count}
+ test_count=$(( test_count + 1 ))
+ }
+
+ if [ "$frontend" = "verific" -o "$frontend" = "verific_gates" ] && grep -q VERIFIC-SKIP ${bn}_ref.v; then
+ touch ../${bn}.skip
+ return
+ fi
+
+ if [ -n "$scriptfiles" ]; then
+ test_passes -f "$frontend $include_opts" ${bn}_ref.v $scriptfiles
+ elif [ -n "$scriptopt" ]; then
+ test_passes -f "$frontend $include_opts" -p "$scriptopt" ${bn}_ref.v
+ elif [ "$frontend" = "verific" ]; then
+ test_passes -p "verific -vlog2k ${bn}_ref.v; verific -import -all; opt; memory;;"
+ elif [ "$frontend" = "verific_gates" ]; then
+ test_passes -p "verific -vlog2k ${bn}_ref.v; verific -import -gates -all; opt; memory;;"
+ else
+ test_passes -f "$frontend $include_opts" -p "hierarchy; proc; opt; memory; opt; fsm; opt -full -fine" ${bn}_ref.v
+ test_passes -f "$frontend $include_opts" -p "hierarchy; synth -run coarse; techmap; opt; abc -dff" ${bn}_ref.v
+ fi
+ touch ../${bn}.log
+ }
+
+ if $verbose; then
+ echo ".."
+ echo "Output written to console." > ${bn}.err
+ ( set -ex; body; )
+ else
+ ( set -ex; body; ) > ${bn}.err 2>&1
+ fi
+
+ if [ -f ${bn}.log ]; then
+ mv ${bn}.err ${bn}.log
+ echo "${status_prefix}-> ok"
+ elif [ -f ${bn}.skip ]; then
+ mv ${bn}.err ${bn}.skip
+ echo "${status_prefix}-> skip"
+ else
+ echo "${status_prefix}-> ERROR!"
+ if $warn_iverilog_git; then
+ echo "Note: Make sure that 'iverilog' is an up-to-date git checkout of Icarus Verilog."
+ fi
+ $keeprunning || exit 1
+ fi
+done
+
+exit 0
diff --git a/tests/tools/cmp_tbdata.c b/tests/tools/cmp_tbdata.c
new file mode 100644
index 00000000..c0b12cd9
--- /dev/null
+++ b/tests/tools/cmp_tbdata.c
@@ -0,0 +1,69 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+int line = 0;
+char buffer1[8192];
+char buffer2[8192];
+
+void check(bool ok)
+{
+ if (ok)
+ return;
+ fprintf(stderr, "Error in testbench output compare (line=%d):\n-%s\n+%s\n", line, buffer1, buffer2);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ FILE *f1, *f2;
+ bool eof1, eof2;
+ int i;
+
+ check(argc == 3);
+
+ f1 = fopen(argv[1], "r");
+ f2 = fopen(argv[2], "r");
+
+ check(f1 && f2);
+
+ while (!feof(f1) && !feof(f2))
+ {
+ line++;
+ buffer1[0] = 0;
+ buffer2[0] = 0;
+
+ eof1 = fgets(buffer1, 1024, f1) == NULL;
+ eof2 = fgets(buffer2, 1024, f2) == NULL;
+
+ if (*buffer1 && buffer1[strlen(buffer1)-1] == '\n')
+ buffer1[strlen(buffer1)-1] = 0;
+
+ if (*buffer2 && buffer2[strlen(buffer2)-1] == '\n')
+ buffer2[strlen(buffer2)-1] = 0;
+
+ check(eof1 == eof2);
+
+ for (i = 0; buffer1[i] || buffer2[i]; i++)
+ {
+ check(buffer1[i] != 0 && buffer2[i] != 0);
+
+ // first argument is the reference. An 'z' or 'x'
+ // here means we don't care about the result.
+ if (buffer1[i] == 'z' || buffer1[i] == 'x')
+ continue;
+ if (buffer1[i] == 'Z' || buffer1[i] == 'X')
+ continue;
+
+ check(buffer1[i] == buffer2[i]);
+ }
+ }
+
+ check(feof(f1) && feof(f2));
+
+ fclose(f1);
+ fclose(f2);
+ return 0;
+}
+
diff --git a/tests/tools/profiler.pl b/tests/tools/profiler.pl
new file mode 100755
index 00000000..456f634b
--- /dev/null
+++ b/tests/tools/profiler.pl
@@ -0,0 +1,55 @@
+#!/usr/bin/perl
+# parse 'yosys -t' logfile and find slow passes
+
+my $max_depth = 0;
+my %last_line_by_depth;
+my %last_time_by_depth;
+
+my @lines_text;
+my @lines_depth;
+my @lines_time;
+
+while (<>)
+{
+ chomp;
+ next unless /^\[([0-9.]+)\] (([0-9]+\.)+)/;
+ my ($this_time, $this_id, $this_header) = ($1, $2, $4);
+
+ push @lines_text, $_;
+ push @lines_depth, 0;
+ push @lines_time, 0;
+
+ my $depth = $this_id;
+ $depth =~ s/[^.]//g;
+ $depth = length $depth;
+ $max_depth = $depth if $depth > $max_depth;
+
+ for (my $i = $depth; $i <= $max_depth; $i++) {
+ next unless exists $last_time_by_depth{$i};
+ $lines_time[$last_line_by_depth{$i}] = $this_time-$last_time_by_depth{$i};
+ delete $last_time_by_depth{$i};
+ delete $last_header_by_depth{$i};
+ }
+
+ $last_time_by_depth{$depth} = $this_time;
+ $last_line_by_depth{$depth} = $#lines_text;
+ $lines_depth[$#lines_text] = $depth;
+}
+
+for (my $depth = 1; $depth <= $max_depth; $depth++) {
+ printf "\nSlow passes on recursion depth %d:\n", $depth;
+ my @lines;
+ for (my $i = 0; $i <= $#lines_text; $i++) {
+ next if $lines_depth[$i] != $depth or $lines_time[$i] < 1.0;
+ push @lines, sprintf("%3d %08.2f %s\n", $lines_depth[$i], $lines_time[$i], $lines_text[$i]);
+ }
+ for my $line (sort {$b cmp $a} @lines) {
+ print $line;
+ }
+}
+
+printf "\nFull journal of headers:\n";
+for (my $i = 0; $i <= $#lines_text; $i++) {
+ printf "%3d %08.2f %s\n", $lines_depth[$i], $lines_time[$i], $lines_text[$i];
+}
+
diff --git a/tests/tools/txt2tikztiming.py b/tests/tools/txt2tikztiming.py
new file mode 100755
index 00000000..9c6cd3a1
--- /dev/null
+++ b/tests/tools/txt2tikztiming.py
@@ -0,0 +1,106 @@
+#!/usr/bin/env python3
+
+import argparse
+import fileinput
+import sys
+
+parser = argparse.ArgumentParser(description='Convert vcd2txt output to tikz-timing line.')
+parser.add_argument('filename', metavar='FILE', help='input txt file')
+parser.add_argument('signame', metavar='SIG', help='Signal name')
+parser.add_argument('-s', metavar='scale', default=1.0, type=float, help='Scale all time spans with this factor')
+parser.add_argument('-l', action='store_true', help='Logic signal (high/low)')
+parser.add_argument('-b', action='store_true', help='Display binary value')
+parser.add_argument('-x', action='store_true', help='Display hex value')
+parser.add_argument('-d', action='store_true', help='Display decimal value')
+args = parser.parse_args()
+
+start_time = None
+stop_time = None
+time_val = { }
+
+def value_to_logic(value):
+ found_x = False
+ for char in value:
+ if char == '1':
+ return "H"
+ if char == 'x':
+ found_x = True
+ return "U" if found_x else "L"
+
+def value_to_binary(value):
+ return "D{%s}" % value
+
+def value_to_hex(value):
+ hex_string = ""
+ found_def = False
+ while len(value) % 4 != 0:
+ value = "0" + value
+ while len(value) != 0:
+ bin_digits = value[0:4]
+ hex_digit = 0
+ value = value[4:]
+ for b in bin_digits:
+ if b == '0':
+ hex_digit = hex_digit * 2
+ elif b == '1':
+ hex_digit = hex_digit * 2 + 1
+ else:
+ hex_digit += 100
+ if hex_digit > 15:
+ hex_string += "x"
+ else:
+ found_def = True
+ hex_string += "0123456789abcdef"[hex_digit]
+ if not found_def:
+ return "U";
+ return "D{%s}" % hex_string
+
+def value_to_decimal(value):
+ val = 0
+ found_def = False
+ found_undef = False
+ for digit in value:
+ if digit == 'x':
+ found_undef = True
+ else:
+ val = val*2 + int(digit)
+ found_def = True
+ if found_def:
+ if found_undef:
+ return "D{X}"
+ else:
+ return "D{%d}" % val
+ return "U"
+
+for line in fileinput.input(args.filename):
+ (node, time, name, value) = line.strip().split('\t')
+ time = int(time)
+ if start_time is None or start_time > time:
+ start_time = time
+ if stop_time is None or stop_time < time:
+ stop_time = time
+ if name == args.signame:
+ if args.l:
+ time_val[+time] = value_to_logic(value)
+ elif args.b:
+ time_val[+time] = value_to_binary(value)
+ elif args.x:
+ time_val[+time] = value_to_hex(value)
+ elif args.d:
+ time_val[+time] = value_to_decimal(value)
+ else:
+ time_val[+time] = value
+
+if start_time not in time_val:
+ time_val[start_time] = "S"
+
+last_time = None
+last_value = None
+for t in sorted(time_val.keys()):
+ if last_time is not None:
+ print("%f%s" % ((t - last_time)*args.s, last_value), end='')
+ (last_time, last_value) = (t, time_val[t])
+if last_time < stop_time:
+ print("%f%s" % ((stop_time - last_time)*args.s, last_value), end='')
+print('')
+
diff --git a/tests/tools/vcd2txt.pl b/tests/tools/vcd2txt.pl
new file mode 100755
index 00000000..92d3d165
--- /dev/null
+++ b/tests/tools/vcd2txt.pl
@@ -0,0 +1,61 @@
+#!/usr/bin/perl -w
+#
+# Note: You might need to install the Verilog::VCD package using CPAN..
+
+use strict;
+use Data::Dumper;
+use Verilog::VCD qw(parse_vcd list_sigs);
+
+$| = 1;
+
+my $from_time = -1;
+my $to_time = -1;
+
+while (1)
+{
+ if ($ARGV[0] eq '-f') {
+ $from_time = +$ARGV[1];
+ shift @ARGV;
+ shift @ARGV;
+ next;
+ }
+ if ($ARGV[0] eq '-t') {
+ $to_time = +$ARGV[1];
+ shift @ARGV;
+ shift @ARGV;
+ next;
+ }
+ last;
+}
+
+if ($#ARGV < 0) {
+ print STDERR "\n";
+ print STDERR "VCD2TXT - Convert VCD to tab-separated text file\n";
+ print STDERR "\n";
+ print STDERR "Usage: $0 [-f from_time] [-t to_time] input.vcd [<signal regex> ...]\n";
+ print STDERR "\n";
+ exit 1;
+}
+
+my $vcd = parse_vcd($ARGV[0]);
+
+for my $node (keys $vcd) {
+ for my $net (@{$vcd->{$node}->{'nets'}}) {
+ my $dump_this = $#ARGV == 0;
+ for (my $i = 1; $i <= $#ARGV; $i++) {
+ my $regex = $ARGV[$i];
+ $dump_this = 1 if ($net->{"hier"} . "." . $net->{"name"}) =~ /$regex/;
+ }
+ next unless $dump_this;
+ my $cached_value = "";
+ for my $tv (@{$vcd->{$node}->{'tv'}}) {
+ $cached_value = $tv->[1], next if $from_time >= 0 and +$tv->[0] < $from_time;
+ next if $to_time >= 0 and +$tv->[0] > $to_time;
+ printf "%s\t%s\t%s\t%s\n", $node, $from_time, $net->{"hier"} . "." . $net->{"name"}, $cached_value
+ if $cached_value ne "" and $from_time >= 0 and +$tv->[0] > $from_time;
+ printf "%s\t%s\t%s\t%s\n", $node, $tv->[0], $net->{"hier"} . "." . $net->{"name"}, $tv->[1];
+ $cached_value = "";
+ }
+ }
+}
+
diff --git a/tests/tools/vcdcd.pl b/tests/tools/vcdcd.pl
new file mode 100755
index 00000000..58a92b44
--- /dev/null
+++ b/tests/tools/vcdcd.pl
@@ -0,0 +1,287 @@
+#!/usr/bin/perl -w
+#
+# Note: You might need to install the Verilog::VCD package using CPAN..
+
+use strict;
+use Data::Dumper;
+use Verilog::VCD qw(parse_vcd list_sigs);
+
+$| = 1;
+
+my $opt_width = 0;
+my $opt_delay = 0;
+
+while (1)
+{
+ if ($ARGV[0] eq '-w') {
+ $opt_width = +$ARGV[1];
+ shift @ARGV;
+ shift @ARGV;
+ next;
+ }
+ if ($ARGV[0] eq '-d') {
+ $opt_delay = +$ARGV[1];
+ shift @ARGV;
+ shift @ARGV;
+ next;
+ }
+ last;
+}
+
+if ($#ARGV != 1) {
+ print STDERR "\n";
+ print STDERR "VCDCD - Value Change Dump Change Dumper\n";
+ print STDERR "\n";
+ print STDERR "Usage: $0 [-w N] [-d N] gold.vcd gate.vcd\n";
+ print STDERR "\n";
+ print STDERR " -w N\n";
+ print STDERR " reserve N characters for bitmap in text output (default: auto)\n";
+ print STDERR "\n";
+ print STDERR " -d N\n";
+ print STDERR " allow for N timesteps delay between gate and gold (default: 0)\n";
+ print STDERR "\n";
+ print STDERR "Compare a known-good (gold) vcd file with a second (gate) vcd file.\n";
+ print STDERR "This is not very efficient -- so use with care on large vcd files.\n";
+ print STDERR "\n";
+ exit 1;
+}
+
+my $fn_gold = $ARGV[0];
+my $fn_gate = $ARGV[1];
+
+print "Finding common signals..\n";
+my @gold_signals = list_sigs($fn_gold);
+my @gate_signals = list_sigs($fn_gate);
+
+my %gold_signals_hash;
+my %gate_signals_hash;
+
+for (@gold_signals) {
+ my $fullname = $_;
+ s/(\[([0-9]+|[0-9]+:[0-9]+)\])$//;
+ $gold_signals_hash{$_}->{$fullname} = 1 unless /(^|\.)_[0-9]+_/;
+}
+
+for (@gate_signals) {
+ my $fullname = $_;
+ s/(\[([0-9]+|[0-9]+:[0-9]+)\])$//;
+ $gate_signals_hash{$_}->{$fullname} = 1 unless /(^|\.)_[0-9]+_/;
+}
+
+my @signals;
+for my $net (sort keys %gold_signals_hash) {
+ next unless exists $gate_signals_hash{$net};
+ # next unless $net eq "tst_bench_top.i2c_top.byte_controller.bit_controller.cnt";
+ my %orig_net_names;
+ print "common signal: $net";
+ for my $fullname (keys $gold_signals_hash{$net}) {
+ $orig_net_names{$fullname} = 1;
+ }
+ for my $fullname (keys $gate_signals_hash{$net}) {
+ $orig_net_names{$fullname} = 1;
+ }
+ for my $net (sort keys %orig_net_names) {
+ push @signals, $net;
+ print " $1" if /(\[([0-9]+|[0-9]+:[0-9]+)\])$/;
+ }
+ print "\n";
+}
+
+print "Loading gold vcd data..\n";
+my $vcd_gold = parse_vcd($fn_gold, {siglist => \@signals});
+
+print "Loading gate vcd data..\n";
+my $vcd_gate = parse_vcd($fn_gate, {siglist => \@signals});
+
+# print Dumper($vcd_gold);
+# print Dumper($vcd_gate);
+
+my %times;
+my $signal_maxlen = 8;
+my $data_gold = { };
+my $data_gate = { };
+
+sub checklen($$)
+{
+ my ($net, $val) = @_;
+ my $thislen = length $val;
+ $thislen += $1 if $net =~ /\[([0-9]+)\]$/;
+ $thislen += $1 if $net =~ /\[([0-9]+):[0-9]+\]$/;
+ $signal_maxlen = $thislen if $signal_maxlen < $thislen;
+}
+
+print "Processing gold vcd data..\n";
+for my $key (keys %$vcd_gold) {
+ for my $net (@{$vcd_gold->{$key}->{'nets'}}) {
+ my $netname = $net->{'hier'} . "." . $net->{'name'};
+ for my $tv (@{$vcd_gold->{$key}->{'tv'}}) {
+ my $time = int($tv->[0]);
+ my $value = $tv->[1];
+ checklen($netname, $value);
+ $data_gold->{$time}->{$netname} = $value;
+ $times{$time} = 1;
+ }
+ }
+}
+
+print "Processing gate vcd data..\n";
+for my $key (keys %$vcd_gate) {
+ for my $net (@{$vcd_gate->{$key}->{'nets'}}) {
+ my $netname = $net->{'hier'} . "." . $net->{'name'};
+ for my $tv (@{$vcd_gate->{$key}->{'tv'}}) {
+ my $time = int($tv->[0]);
+ my $value = $tv->[1];
+ checklen($netname, $value);
+ $data_gate->{$time}->{$netname} = $value;
+ $times{$time} = 1;
+ }
+ }
+}
+
+$signal_maxlen = $opt_width if $opt_width > 0;
+
+my $diffcount = 0;
+my %state_gold;
+my %state_gate;
+my %signal_sync;
+my %touched_nets;
+
+sub set_state_bit($$$$)
+{
+ my ($state, $net, $bit, $value) = @_;
+ my @data;
+ @data = split //, $state->{$net} if exists $state->{$net};
+ unshift @data, "-" while $#data < $bit;
+ $data[$#data - $bit] = $value;
+ $state->{$net} = join "", @data;
+ $signal_sync{$net} = 1 unless exists $signal_sync{$net};
+ $touched_nets{$net} = 1;
+}
+
+sub set_state($$$)
+{
+ my ($state, $net, $value) = @_;
+
+ if ($net =~ /(.*)\[([0-9]+)\]$/) {
+ set_state_bit($state, $1, $2, $value);
+ return;
+ }
+
+ if ($net =~ /(.*)\[([0-9]+):([0-9]+)\]$/) {
+ my ($n, $u, $d) = ($1, $2, $3);
+ my @bits = split //, $value;
+ my $extbit = $bits[0] eq "1" ? "0" : $bits[0];
+ unshift @bits, $extbit while $#bits < $u - $d;
+ set_state_bit($state, $n, $u--, shift @bits) while $u >= $d;
+ return;
+ }
+
+ $state->{$net} = $value;
+ $signal_sync{$net} = 1 unless exists $signal_sync{$net};
+ $touched_nets{$net} = 1;
+}
+
+sub cmp_signal($$)
+{
+ my ($a, $b) = @_;
+ return 1 if $a eq $b;
+
+ my @a = split //, $a;
+ my @b = split //, $b;
+
+ my $trail_a = $#a < 0 ? '-' : $a[0] eq '1' ? '0' : $a[0];
+ my $trail_b = $#b < 0 ? '-' : $b[0] eq '1' ? '0' : $b[0];
+
+ unshift @a, $trail_a while $#a < $#b;
+ unshift @b, $trail_b while $#b < $#a;
+
+ for (my $i = 0; $i <= $#a; $i++) {
+ next if $a[$i] eq "-" || $b[$i] eq "-";
+ return 0 if $a[$i] ne "x" && $a[$i] ne $b[$i];
+ }
+
+ return 1;
+}
+
+# Message objects: .text, .time, .signal, .sync
+my @messages;
+
+print "Comparing vcd data..\n";
+for my $time (sort { $a <=> $b } keys %times)
+{
+ %touched_nets = ();
+ for my $net (keys %{$data_gold->{$time}}) {
+ set_state(\%state_gold, $net, $data_gold->{$time}->{$net});
+ }
+ for my $net (keys %{$data_gate->{$time}}) {
+ set_state(\%state_gate, $net, $data_gate->{$time}->{$net});
+ }
+ for my $net (sort keys %touched_nets) {
+ my ($stgo, $stga) = ('-', '-');
+ $stgo = $state_gold{$net} if exists $state_gold{$net};
+ $stga = $state_gate{$net} if exists $state_gate{$net};
+ if (cmp_signal($stgo, $stga)) {
+ next if $signal_sync{$net};
+ my $message = { };
+ $message->{text} = sprintf "%-10s %-20d %-*s %-*s %s\n", "<sync>", $time, $signal_maxlen, $stgo, $signal_maxlen, $stga, $net;
+ $message->{time} = $time;
+ $message->{signal} = $net;
+ $message->{sync} = 1;
+ push @messages, $message;
+ $signal_sync{$net} = 1;
+ } else {
+ my $message = { };
+ $message->{text} = sprintf "%-10d %-20d %-*s %-*s %s\n", $diffcount, $time, $signal_maxlen, $stgo, $signal_maxlen, $stga, $net;
+ $message->{time} = $time;
+ $message->{signal} = $net;
+ $message->{sync} = 0;
+ push @messages, $message;
+ $signal_sync{$net} = 0;
+ $diffcount++;
+ }
+ }
+}
+
+print "Found $diffcount differences.\n";
+
+if ($opt_delay > 0) {
+ my %per_net_history;
+ my $removed_diff_count = 0;
+ for (my $i = 0; $i <= $#messages; $i++) {
+ my $message = $messages[$i];
+ $message->{deleted} = 0;
+ $per_net_history{$message->{signal}} = [ ] unless exists $per_net_history{$message->{signal}};
+ if ($message->{sync}) {
+ my $deleted_all = 1;
+ for my $j (@{$per_net_history{$message->{signal}}}) {
+ my $m = $messages[$j];
+ if ($m->{time} + $opt_delay >= $message->{time}) {
+ $m->{deleted} = 1;
+ $removed_diff_count++;
+ } else {
+ $deleted_all = 0;
+ }
+ }
+ $message->{deleted} = 1 if $deleted_all;
+ $per_net_history{$message->{signal}} = [ ];
+ } else {
+ push @{$per_net_history{$message->{signal}}}, $i;
+ }
+ }
+ my @new_messages;
+ for my $message (@messages) {
+ push @new_messages, $message unless $message->{deleted};
+ }
+ @messages = @new_messages;
+ printf "Removed %d differences using delay filtering.\n", $removed_diff_count;
+ $diffcount = $diffcount - $removed_diff_count;
+}
+
+if ($#messages >= 0) {
+ printf "\n%-10s %-20s %-*s %-*s %s\n", "count", "time", $signal_maxlen, "gold", $signal_maxlen, "gate", "net" if $diffcount++ == 0;
+ for (my $i = 0; $i <= $#messages; $i++) {
+ printf "%s", $messages[$i]->{text};
+ }
+}
+
+exit ($diffcount > 0 ? 1 : 0);
diff --git a/tests/various/.gitignore b/tests/various/.gitignore
new file mode 100644
index 00000000..397b4a76
--- /dev/null
+++ b/tests/various/.gitignore
@@ -0,0 +1 @@
+*.log
diff --git a/tests/various/constmsk_test.v b/tests/various/constmsk_test.v
new file mode 100644
index 00000000..0d0e58fe
--- /dev/null
+++ b/tests/various/constmsk_test.v
@@ -0,0 +1,4 @@
+module test(input [3:0] A, output [3:0] Y1, Y2);
+ assign Y1 = |{A[3], 1'b0, A[1]};
+ assign Y2 = |{A[2], 1'b1, A[0]};
+endmodule
diff --git a/tests/various/constmsk_test.ys b/tests/various/constmsk_test.ys
new file mode 100644
index 00000000..ce36efc3
--- /dev/null
+++ b/tests/various/constmsk_test.ys
@@ -0,0 +1,15 @@
+read_verilog constmsk_test.v
+
+copy test gold
+rename test gate
+
+cd gate
+techmap -map constmsk_testmap.v;;
+cd ..
+
+select -assert-count 2 gold/r:A_WIDTH=3
+select -assert-count 1 gate/r:A_WIDTH=2
+select -assert-count 1 gate/c:*
+
+miter -equiv -flatten gold gate miter
+sat -verify -prove trigger 0 miter
diff --git a/tests/various/constmsk_testmap.v b/tests/various/constmsk_testmap.v
new file mode 100644
index 00000000..fab1b1bb
--- /dev/null
+++ b/tests/various/constmsk_testmap.v
@@ -0,0 +1,49 @@
+(* techmap_celltype = "$reduce_or" *)
+module my_opt_reduce_or(...);
+ parameter A_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ input [A_WIDTH-1:0] A;
+ output reg [Y_WIDTH-1:0] Y;
+
+ parameter _TECHMAP_CONSTMSK_A_ = 0;
+ parameter _TECHMAP_CONSTVAL_A_ = 0;
+
+ wire _TECHMAP_FAIL_ = count_nonconst_bits() == A_WIDTH;
+ wire [1024:0] _TECHMAP_DO_ = "proc;;";
+
+ function integer count_nonconst_bits;
+ integer i;
+ begin
+ count_nonconst_bits = 0;
+ for (i = 0; i < A_WIDTH; i=i+1)
+ if (!_TECHMAP_CONSTMSK_A_[i])
+ count_nonconst_bits = count_nonconst_bits+1;
+ end
+ endfunction
+
+ function has_const_one;
+ integer i;
+ begin
+ has_const_one = 0;
+ for (i = 0; i < A_WIDTH; i=i+1)
+ if (_TECHMAP_CONSTMSK_A_[i] && _TECHMAP_CONSTVAL_A_[i] === 1'b1)
+ has_const_one = 1;
+ end
+ endfunction
+
+ integer i;
+ reg [count_nonconst_bits()-1:0] tmp;
+
+ always @* begin
+ if (has_const_one()) begin
+ Y = 1;
+ end else begin
+ for (i = 0; i < A_WIDTH; i=i+1)
+ if (!_TECHMAP_CONSTMSK_A_[i])
+ tmp = {A[i], tmp[count_nonconst_bits()-1:1]};
+ Y = |tmp;
+ end
+ end
+endmodule
diff --git a/tests/various/muxcover.ys b/tests/various/muxcover.ys
new file mode 100644
index 00000000..7ac460f1
--- /dev/null
+++ b/tests/various/muxcover.ys
@@ -0,0 +1,51 @@
+
+read_verilog -formal <<EOT
+ module gate (input [2:0] A, B, C, D, X, output reg [2:0] Y);
+ always @*
+ (* parallel_case *)
+ casez (X)
+ 3'b??1: Y = A;
+ 3'b?1?: Y = B;
+ 3'b1??: Y = C;
+ 3'b000: Y = D;
+ endcase
+ endmodule
+EOT
+
+
+## Examle usage for "pmuxtree" and "muxcover"
+
+proc
+pmuxtree
+techmap
+muxcover -mux4
+
+splitnets -ports
+clean
+# show
+
+
+## Equivalence checking
+
+read_verilog -formal <<EOT
+ module gold (input [2:0] A, B, C, D, X, output reg [2:0] Y);
+ always @*
+ casez (X)
+ 3'b001: Y = A;
+ 3'b010: Y = B;
+ 3'b100: Y = C;
+ 3'b000: Y = D;
+ default: Y = 'bx;
+ endcase
+ endmodule
+EOT
+
+proc
+splitnets -ports
+techmap -map +/simcells.v t:$_MUX4_
+
+equiv_make gold gate equiv
+hierarchy -top equiv
+equiv_simple -undef
+equiv_status -assert
+
diff --git a/tests/various/run-test.sh b/tests/various/run-test.sh
new file mode 100755
index 00000000..67e1beb2
--- /dev/null
+++ b/tests/various/run-test.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -e
+for x in *.ys; do
+ echo "Running $x.."
+ ../../yosys -ql ${x%.ys}.log $x
+done
diff --git a/tests/various/submod_extract.ys b/tests/various/submod_extract.ys
new file mode 100644
index 00000000..8d11c21d
--- /dev/null
+++ b/tests/various/submod_extract.ys
@@ -0,0 +1,21 @@
+read_verilog << EOT
+ module test(input [7:0] a, b, c, d, output [7:0] x, y, z);
+ assign x = a + b, y = b + c, z = c + d;
+ endmodule
+EOT
+
+copy test gold
+rename test gate
+
+submod -name mycell gate/x %ci*
+design -copy-to mymap mycell
+extract -map %mymap gate
+
+select -assert-count 3 gold/t:*
+select -assert-count 3 gold/t:$add
+
+select -assert-count 3 gate/t:*
+select -assert-count 3 gate/t:mycell
+
+miter -equiv -flatten gold gate miter
+sat -verify -prove trigger 0 miter
diff --git a/tests/vloghtb/.gitignore b/tests/vloghtb/.gitignore
new file mode 100644
index 00000000..63db2fba
--- /dev/null
+++ b/tests/vloghtb/.gitignore
@@ -0,0 +1,9 @@
+Makefile
+refdat
+rtl
+scripts
+spec
+check_yosys
+vloghammer_tb.tar.bz2
+temp
+log_test_*
diff --git a/tests/vloghtb/common.sh b/tests/vloghtb/common.sh
new file mode 100644
index 00000000..a8335c2b
--- /dev/null
+++ b/tests/vloghtb/common.sh
@@ -0,0 +1,106 @@
+log_pass()
+{
+ printf "%-15s %s %s %s\n" "$1" "$2" "`printf "%20s" "$2" | tr -d a-zA-Z0-9_ | tr ' ' .`" "pass."
+}
+
+log_fail()
+{
+ printf "%-15s %s %s %s\n" "$1" "$2" "`printf "%20s" "$2" | tr -d a-zA-Z0-9_ | tr ' ' .`" "FAIL."
+}
+
+test_autotest()
+{
+ # Usage:
+ # test_autotest <test_name> <synth_script> <mod_name> <vlog_file>
+
+ test_name="$1"
+ synth_cmd="$2"
+ mod_name="$3"
+ vlog_file="$4"
+
+ mkdir -p log_test_$test_name
+ rm -rf log_test_$test_name/$mod_name.*
+
+ ../../yosys -q -l log_test_$test_name/$mod_name.out -o log_test_$test_name/$mod_name.v -p "$synth_cmd" "$vlog_file"
+ cat spec/${mod_name}_spec.v scripts/check.v >> log_test_$test_name/$mod_name.v
+ iverilog -o log_test_$test_name/$mod_name.bin -D"REFDAT_FN=\"refdat/${mod_name}_refdat.txt\"" log_test_$test_name/$mod_name.v
+
+ if log_test_$test_name/$mod_name.bin 2>&1 | tee -a log_test_$test_name/$mod_name.out | grep -q '++OK++'; then
+ mv log_test_$test_name/$mod_name.out log_test_$test_name/$mod_name.txt
+ log_pass test_$test_name $mod_name
+ else
+ mv log_test_$test_name/$mod_name.out log_test_$test_name/$mod_name.err
+ log_fail test_$test_name $mod_name
+ exit 1
+ fi
+}
+
+test_equiv()
+{
+ # Usage:
+ # test_equiv <test_name> <synth_script> <sat_options> <mod_name> <vlog_file>
+
+ mkdir -p log_test_$1
+ rm -f log_test_$1/$4.txt
+ rm -f log_test_$1/$4.err
+
+ if ! ../../yosys -q -l log_test_$1/$4.out - 2> /dev/null <<- EOT
+ read_verilog $5
+ proc;;
+
+ copy $4 gold
+ rename $4 work
+
+ cd work
+ $2
+ cd ..
+
+ miter -equiv -ignore_gold_x -make_outputs -make_outcmp gold work miter
+ flatten miter
+ sat $3 -verify -prove trigger 0 -show-inputs -show-outputs miter
+ EOT
+ then
+ log_fail test_$1 $4
+ mv log_test_$1/$4.out log_test_$1/$4.err
+ exit 1
+ fi
+
+ log_pass test_$1 $4
+ mv log_test_$1/$4.out log_test_$1/$4.txt
+}
+
+test_febe()
+{
+ # Usage:
+ # test_febe <test_name> <synth_script> <extension> <backend> <frontend> <sat_options> <mod_name> <vlog_file>
+ # $1 $2 $3 $4 $5 $6 $7 $8
+
+ mkdir -p log_test_$1
+ rm -f log_test_$1/$7.txt
+ rm -f log_test_$1/$7.err
+
+ if ! ../../yosys -q -l log_test_$1/$7.out - 2> /dev/null <<- EOT
+ echo on
+ read_verilog $8
+ $2
+ design -save gold
+ dump
+ $4 log_test_$1/$7$3
+ design -reset
+ $5 log_test_$1/$7$3
+
+ design -copy-from gold -as gold $7
+ rename $7 gate
+
+ miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp gold gate miter
+ sat $6 -verify -prove trigger 0 -show-inputs -show-outputs miter
+ EOT
+ then
+ log_fail test_$1 $7
+ mv log_test_$1/$7.out log_test_$1/$7.err
+ exit 1
+ fi
+
+ log_pass test_$1 $7
+ mv log_test_$1/$7.out log_test_$1/$7.txt
+}
diff --git a/tests/vloghtb/run-test.sh b/tests/vloghtb/run-test.sh
new file mode 100755
index 00000000..ad99226e
--- /dev/null
+++ b/tests/vloghtb/run-test.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+set -ex
+
+rm -rf Makefile refdat rtl scripts spec
+wget -N http://www.clifford.at/yosys/nogit/vloghammer_tb.tar.bz2
+tar --strip=1 -xjf vloghammer_tb.tar.bz2
+
+make clean
+rm -rf log_test_*
+
+${MAKE:-make} EXIT_ON_ERROR=1 YOSYS_BIN=$PWD/../../yosys YOSYS_SCRIPT="proc;;" check_yosys
+${MAKE:-make} -f test_makefile MODE=share
+${MAKE:-make} -f test_makefile MODE=mapopt
+
diff --git a/tests/vloghtb/test_febe.sh b/tests/vloghtb/test_febe.sh
new file mode 100644
index 00000000..482d44d9
--- /dev/null
+++ b/tests/vloghtb/test_febe.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+set -e
+source common.sh
+
+f=$1
+n=$(basename ${f%.v})
+
+test_febe vlog1 "synth" ".v" "write_verilog" "read_verilog" "-ignore_div_by_zero" $n $f
+test_febe vlog2 "synth -run coarse" ".v" "write_verilog" "read_verilog -icells" "-ignore_div_by_zero" $n $f
+test_febe blif "synth; splitnets -ports" ".blif" "write_blif" "read_blif" "-ignore_div_by_zero" $n $f
+
+exit 0
diff --git a/tests/vloghtb/test_makefile b/tests/vloghtb/test_makefile
new file mode 100644
index 00000000..174dbbc2
--- /dev/null
+++ b/tests/vloghtb/test_makefile
@@ -0,0 +1,9 @@
+
+MODE := share
+TESTS := $(shell ls rtl/ | sed 's,\.v$$,,' )
+
+run: $(addprefix log_test_$(MODE)/,$(addsuffix .txt,$(TESTS)))
+
+log_test_$(MODE)/%.txt: rtl/%.v
+ @bash test_$(MODE).sh $<
+
diff --git a/tests/vloghtb/test_mapopt.sh b/tests/vloghtb/test_mapopt.sh
new file mode 100644
index 00000000..61528c2b
--- /dev/null
+++ b/tests/vloghtb/test_mapopt.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -e
+source common.sh
+
+f=$1
+n=$(basename ${f%.v})
+
+mkdir -p log_test_mapopt
+rm -f log_test_mapopt/$n.*
+
+test_equiv mapopt_1 "opt -fine; techmap; opt" "-set-def-inputs" $n $f
+test_autotest mapopt_2 "proc; opt; techmap; opt" $n $f
+
+tail -n20 log_test_mapopt_1/$n.txt log_test_mapopt_2/$n.txt > log_test_mapopt/$n.txt
+
+exit 0
diff --git a/tests/vloghtb/test_share.sh b/tests/vloghtb/test_share.sh
new file mode 100644
index 00000000..67cfe44e
--- /dev/null
+++ b/tests/vloghtb/test_share.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+set -e
+source common.sh
+
+f=$1
+n=$(basename ${f%.v})
+
+test_equiv share "wreduce; share -aggressive" "-ignore_div_by_zero" $n $f
+
+exit 0